大量语言笔记...
下面是几门语言的笔记
javascript
Javascript于1995年由网景公司的Brendan Eich发明。
最初发明的目的是作为一个简单的网站脚本语言,来作为
复杂网站应用java的补充。但由于它与网页结合度很高并且由浏览器内置支持,
所以javascript变得比java在前端更为流行了。
不过 JavaScript 可不仅仅只用于浏览器: Node.js,一个基于Google Chrome V8引擎的独立运行时环境,也越来越流行。
// 注释方式和C很像,这是单行注释
/* 这是多行
注释 */
// 语句可以以分号结束
doStuff();
// ... 但是分号也可以省略,每当遇到一个新行时,分号会自动插入(除了一些特殊情况)。
doStuff()
// 因为这些特殊情况会导致意外的结果,所以我们在这里保留分号。
///////////////////////////////////
// 1. 数字、字符串与操作符
// Javascript 只有一种数字类型(即 64位 IEEE 754 双精度浮点 double)。
// double 有 52 位表示尾数,足以精确存储大到 9✕10¹⁵ 的整数。
3; // = 3
1.5; // = 1.5
// 所有基本的算数运算都如你预期。
1 + 1; // = 2
0.1 + 0.2; // = 0.30000000000000004
8 - 1; // = 7
10 * 2; // = 20
35 / 5; // = 7
// 包括无法整除的除法。
5 / 2; // = 2.5
// 位运算也和其他语言一样;当你对浮点数进行位运算时,
// 浮点数会转换为*至多* 32 位的无符号整数。
1 << 2; // = 4
// 括号可以决定优先级。
(1 + 3) * 2; // = 8
// 有三种非数字的数字类型
Infinity; // 1/0 的结果
-Infinity; // -1/0 的结果
NaN; // 0/0 的结果
// 也有布尔值。
true;
false;
// 可以通过单引号或双引号来构造字符串。
'abc';
"Hello, world";
// 用!来取非
!true; // = false
!false; // = true
// 相等 ===
1 === 1; // = true
2 === 1; // = false
// 不等 !=
1 !== 1; // = false
2 !== 1; // = true
// 更多的比较操作符
1 < 10; // = true
1 > 10; // = false
2 <= 2; // = true
2 >= 2; // = true
// 字符串用+连接
"Hello " + "world!"; // = "Hello world!"
// 字符串也可以用 < 、> 来比较
"a" < "b"; // = true
// 使用“==”比较时会进行类型转换...
"5" == 5; // = true
null == undefined; // = true
// ...除非你是用 ===
"5" === 5; // = false
null === undefined; // = false
// ...但会导致奇怪的行为
13 + !0; // 14
"13" + !0; // '13true'
// 你可以用`charAt`来得到字符串中的字符
"This is a string".charAt(0); // = 'T'
// ...或使用 `substring` 来获取更大的部分。
"Hello world".substring(0, 5); // = "Hello"
// `length` 是一个属性,所以不要使用 ().
"Hello".length; // = 5
// 还有两个特殊的值:`null`和`undefined`
null; // 用来表示刻意设置的空值
undefined; // 用来表示还没有设置的值(尽管`undefined`自身实际是一个值)
// false, null, undefined, NaN, 0 和 "" 都是假的;其他的都视作逻辑真
// 注意 0 是逻辑假而 "0"是逻辑真,尽管 0 == "0"。
///////////////////////////////////
// 2. 变量、数组和对象
// 变量需要用`var`关键字声明。Javascript是动态类型语言,
// 所以你无需指定类型。 赋值需要用 `=`
var someVar = 5;
// 如果你在声明时没有加var关键字,你也不会得到错误...
someOtherVar = 10;
// ...但是此时这个变量就会在全局作用域被创建,而非你定义的当前作用域
// 没有被赋值的变量都会被设置为undefined
var someThirdVar; // = undefined
// 对变量进行数学运算有一些简写法:
someVar += 5; // 等价于 someVar = someVar + 5; someVar 现在是 10
someVar *= 10; // 现在 someVar 是 100
// 自增和自减也有简写
someVar++; // someVar 是 101
someVar--; // 回到 100
// 数组是任意类型组成的有序列表
var myArray = ["Hello", 45, true];
// 数组的元素可以用方括号下标来访问。
// 数组的索引从0开始。
myArray[1]; // = 45
// 数组是可变的,并拥有变量 length。
myArray.push("World");
myArray.length; // = 4
// 在指定下标添加/修改
myArray[3] = "Hello";
// javascript中的对象相当于其他语言中的“字典”或“映射”:是键-值对的无序集合。
var myObj = {key1: "Hello", key2: "World"};
// 键是字符串,但如果键本身是合法的js标识符,则引号并非是必须的。
// 值可以是任意类型。
var myObj = {myKey: "myValue", "my other key": 4};
// 对象属性的访问可以通过下标
myObj["my other key"]; // = 4
// ... 或者也可以用 . ,如果属性是合法的标识符
myObj.myKey; // = "myValue"
// 对象是可变的;值也可以被更改或增加新的键
myObj.myThirdKey = true;
// 如果你想要获取一个还没有被定义的值,那么会返回undefined
myObj.myFourthKey; // = undefined
///////////////////////////////////
// 3. 逻辑与控制结构
// 本节介绍的语法与Java的语法几乎完全相同
// `if`语句和其他语言中一样。
var count = 1;
if (count == 3){
// count 是 3 时执行
} else if (count == 4){
// count 是 4 时执行
} else {
// 其他情况下执行
}
// while循环
while (true) {
// 无限循环
}
// Do-while 和 While 循环很像 ,但前者会至少执行一次
var input;
do {
input = getInput();
} while (!isValid(input))
// `for`循环和C、Java中的一样:
// 初始化; 继续执行的条件; 迭代。
for (var i = 0; i < 5; i++){
// 遍历5次
}
// && 是逻辑与, || 是逻辑或
if (house.size == "big" && house.colour == "blue"){
house.contains = "bear";
}
if (colour == "red" || colour == "blue"){
// colour是red或者blue时执行
}
// && 和 || 是“短路”语句,它在设定初始化值时特别有用
var name = otherName || "default";
// `switch`语句使用`===`检查相等性。
// 在每一个case结束时使用 'break'
// 否则其后的case语句也将被执行。
grade = 'B';
switch (grade) {
case 'A':
console.log("Great job");
break;
case 'B':
console.log("OK job");
break;
case 'C':
console.log("You can do better");
break;
default:
console.log("Oy vey");
break;
}
///////////////////////////////////
// 4. 函数、作用域、闭包
// JavaScript 函数由`function`关键字定义
function myFunction(thing){
return thing.toUpperCase();
}
myFunction("foo"); // = "FOO"
// 注意被返回的值必须开始于`return`关键字的那一行,
// 否则由于自动的分号补齐,你将返回`undefined`。
// 在使用Allman风格的时候要注意.
function myFunction()
{
return // <- 分号自动插在这里
{
thisIsAn: 'object literal'
}
}
myFunction(); // = undefined
// javascript中函数是一等对象,所以函数也能够赋给一个变量,
// 并且被作为参数传递 —— 比如一个事件处理函数:
function myFunction(){
// 这段代码将在5秒钟后被调用
}
setTimeout(myFunction, 5000);
// 注意:setTimeout不是js语言的一部分,而是由浏览器和Node.js提供的。
// 函数对象甚至不需要声明名称 —— 你可以直接把一个函数定义写到另一个函数的参数中
setTimeout(function(){
// 这段代码将在5秒钟后被调用
}, 5000);
// JavaScript 有函数作用域;函数有其自己的作用域而其他的代码块则没有。
if (true){
var i = 5;
}
i; // = 5 - 并非我们在其他语言中所期望得到的undefined
// 这就导致了人们经常使用的“立即执行匿名函数”的模式,
// 这样可以避免一些临时变量扩散到全局作用域去。
(function(){
var temporary = 5;
// 我们可以访问修改全局对象("global object")来访问全局作用域,
// 在web浏览器中是`window`这个对象。
// 在其他环境如Node.js中这个对象的名字可能会不同。
window.permanent = 10;
})();
temporary; // 抛出引用异常ReferenceError
permanent; // = 10
// javascript最强大的功能之一就是闭包。
// 如果一个函数在另一个函数中定义,那么这个内部函数就拥有外部函数的所有变量的访问权,
// 即使在外部函数结束之后。
function sayHelloInFiveSeconds(name){
var prompt = "Hello, " + name + "!";
// 内部函数默认是放在局部作用域的,
// 就像是用`var`声明的。
function inner(){
alert(prompt);
}
setTimeout(inner, 5000);
// setTimeout是异步的,所以 sayHelloInFiveSeconds 函数会立即退出,
// 而 setTimeout 会在后面调用inner
// 然而,由于inner是由sayHelloInFiveSeconds“闭合包含”的,
// 所以inner在其最终被调用时仍然能够访问`prompt`变量。
}
sayHelloInFiveSeconds("Adam"); // 会在5秒后弹出 "Hello, Adam!"
///////////////////////////////////
// 5. 对象、构造函数与原型
// 对象可以包含方法。
var myObj = {
myFunc: function(){
return "Hello world!";
}
};
myObj.myFunc(); // = "Hello world!"
// 当对象中的函数被调用时,这个函数可以通过`this`关键字访问其依附的这个对象。
myObj = {
myString: "Hello world!",
myFunc: function(){
return this.myString;
}
};
myObj.myFunc(); // = "Hello world!"
// 但这个函数访问的其实是其运行时环境,而非定义时环境,即取决于函数是如何调用的。
// 所以如果函数被调用时不在这个对象的上下文中,就不会运行成功了。
var myFunc = myObj.myFunc;
myFunc(); // = undefined
// 相应的,一个函数也可以被指定为一个对象的方法,并且可以通过`this`访问
// 这个对象的成员,即使在函数被定义时并没有依附在对象上。
var myOtherFunc = function(){
return this.myString.toUpperCase();
}
myObj.myOtherFunc = myOtherFunc;
myObj.myOtherFunc(); // = "HELLO WORLD!"
// 当我们通过`call`或者`apply`调用函数的时候,也可以为其指定一个执行上下文。
var anotherFunc = function(s){
return this.myString + s;
}
anotherFunc.call(myObj, " And Hello Moon!"); // = "Hello World! And Hello Moon!"
// `apply`函数几乎完全一样,只是要求一个array来传递参数列表。
anotherFunc.apply(myObj, [" And Hello Sun!"]); // = "Hello World! And Hello Sun!"
// 当一个函数接受一系列参数,而你想传入一个array时特别有用。
Math.min(42, 6, 27); // = 6
Math.min([42, 6, 27]); // = NaN (uh-oh!)
Math.min.apply(Math, [42, 6, 27]); // = 6
// 但是`call`和`apply`只是临时的。如果我们希望函数附着在对象上,可以使用`bind`。
var boundFunc = anotherFunc.bind(myObj);
boundFunc(" And Hello Saturn!"); // = "Hello World! And Hello Saturn!"
// `bind` 也可以用来部分应用一个函数(柯里化)。
var product = function(a, b){ return a * b; }
var doubler = product.bind(this, 2);
doubler(8); // = 16
// 当你通过`new`关键字调用一个函数时,就会创建一个对象,
// 而且可以通过this关键字访问该函数。
// 设计为这样调用的函数就叫做构造函数。
var MyConstructor = function(){
this.myNumber = 5;
}
myNewObj = new MyConstructor(); // = {myNumber: 5}
myNewObj.myNumber; // = 5
// 每一个js对象都有一个‘原型’。当你要访问一个实际对象中没有定义的一个属性时,
// 解释器就回去找这个对象的原型。
// 一些JS实现会让你通过`__proto__`属性访问一个对象的原型。
// 这虽然对理解原型很有用,但是它并不是标准的一部分;
// 我们后面会介绍使用原型的标准方式。
var myObj = {
myString: "Hello world!"
};
var myPrototype = {
meaningOfLife: 42,
myFunc: function(){
return this.myString.toLowerCase()
}
};
myObj.__proto__ = myPrototype;
myObj.meaningOfLife; // = 42
// 函数也可以工作。
myObj.myFunc() // = "hello world!"
// 当然,如果你要访问的成员在原型当中也没有定义的话,解释器就会去找原型的原型,以此类推。
myPrototype.__proto__ = {
myBoolean: true
};
myObj.myBoolean; // = true
// 这其中并没有对象的拷贝;每个对象实际上是持有原型对象的引用。
// 这意味着当我们改变对象的原型时,会影响到其他以这个原型为原型的对象。
myPrototype.meaningOfLife = 43;
myObj.meaningOfLife; // = 43
// 我们知道 `__proto__` 并非标准规定,实际上也没有标准办法来修改一个已存在对象的原型。
// 然而,我们有两种方式为指定原型创建一个新的对象。
// 第一种方式是 Object.create,这个方法是在最近才被添加到Js中的,
// 因此并不是所有的JS实现都有这个方法
var myObj = Object.create(myPrototype);
myObj.meaningOfLife; // = 43
// 第二种方式可以在任意版本中使用,不过必须通过构造函数。
// 构造函数有一个属性prototype。但是它 *不是* 构造函数本身的原型;相反,
// 是通过构造函数和new关键字创建的新对象的原型。
MyConstructor.prototype = {
myNumber: 5,
getMyNumber: function(){
return this.myNumber;
}
};
var myNewObj2 = new MyConstructor();
myNewObj2.getMyNumber(); // = 5
myNewObj2.myNumber = 6
myNewObj2.getMyNumber(); // = 6
// 字符串和数字等内置类型也有通过构造函数来创建的包装类型
var myNumber = 12;
var myNumberObj = new Number(12);
myNumber == myNumberObj; // = true
// 但是它们并非严格等价
typeof myNumber; // = 'number'
typeof myNumberObj; // = 'object'
myNumber === myNumberObj; // = false
if (0){
// 这段代码不会执行,因为0代表假
}
// 不过,包装类型和内置类型共享一个原型,
// 所以你实际可以给内置类型也增加一些功能,例如对string:
String.prototype.firstCharacter = function(){
return this.charAt(0);
}
"abc".firstCharacter(); // = "a"
// 这个技巧经常用在“代码填充”中,来为老版本的javascript子集增加新版本js的特性,
// 这样就可以在老的浏览器中使用新功能了。
// 比如,我们知道Object.create并没有在所有的版本中都实现,
// 但是我们仍然可以通过“代码填充”来实现兼容:
if (Object.create === undefined){ // 如果存在则不覆盖
Object.create = function(proto){
// 用正确的原型来创建一个临时构造函数
var Constructor = function(){};
Constructor.prototype = proto;
// 之后用它来创建一个新的对象
return new Constructor();
}
}
更多阅读
Mozilla 开发者
网络 提供了优秀的介绍
Javascript如何在浏览器中使用的文档。而且它是wiki,所以你也可以自行编辑来分享你的知识。
MDN的 A re-introduction to
JavaScript
覆盖了这里提到的绝大多数话题的细节。该导引的大多数内容被限定在只是Javascript这个语言本身;
如果你想了解Javascript是如何在网页中被应用的,那么可以查看
Document Object
Model
Learn Javascript by Example and with Challenges 是本参考的另一个版本,并包含了挑战习题。
Javascript Garden 是一个深入
讲解所有Javascript反直觉部分的导引。
JavaScript: The Definitive Guide 是一个经典的指导参考书。
bash
Bash 是一个为 GNU 计划编写的 Unix shell,是 Linux 和 Mac OS X 下的默认 shell。
以下大多数例子可以作为脚本的一部分运行,也可直接在 shell 下交互执行。
#!/bin/bash
# 脚本的第一行叫 shebang,用来告知系统如何执行该脚本:
# 参见: http://en.wikipedia.org/wiki/Shebang_(Unix)
# 如你所见,注释以 # 开头,shebang 也是注释。
# 显示 “Hello world!”
echo Hello world!
# 每一句指令以换行或分号隔开:
echo 'This is the first line'; echo 'This is the second line'
# 声明一个变量:
Variable="Some string"
# 下面是错误的做法:
Variable = "Some string"
# Bash 会把 Variable 当做一个指令,由于找不到该指令,因此这里会报错。
# 也不可以这样:
Variable= 'Some string'
# Bash 会认为 'Some string' 是一条指令,由于找不到该指令,这里再次报错。
# (这个例子中 'Variable=' 这部分会被当作仅对 'Some string' 起作用的赋值。)
# 使用变量:
echo $Variable
echo "$Variable"
echo '$Variable'
# 当你赋值 (assign) 、导出 (export),或者以其他方式使用变量时,变量名前不加 $。
# 如果要使用变量的值, 则要加 $。
# 注意: ' (单引号) 不会展开变量(即会屏蔽掉变量)。
# 在变量内部进行字符串代换
echo ${Variable/Some/A}
# 会把 Variable 中首次出现的 "some" 替换成 “A”。
# 变量的截取
Length=7
echo ${Variable:0:Length}
# 这样会仅返回变量值的前7个字符
# 变量的默认值
echo ${Foo:-"DefaultValueIfFooIsMissingOrEmpty"}
# 对 null (Foo=) 和空串 (Foo="") 起作用; 零(Foo=0)时返回0
# 注意这仅返回默认值而不是改变变量的值
# 内置变量:
# 下面的内置变量很有用
echo "Last program return value: $?"
echo "Script's PID: $$"
echo "Number of arguments: $#"
echo "Scripts arguments: $@"
echo "Scripts arguments separated in different variables: $1 $2..."
# 读取输入:
echo "What's your name?"
read Name # 这里不需要声明新变量
echo Hello, $Name!
# 通常的 if 结构看起来像这样:
# 'man test' 可查看更多的信息
if [ $Name -ne $USER ]
then
echo "Your name isn't your username"
else
echo "Your name is your username"
fi
# 根据上一个指令执行结果决定是否执行下一个指令
echo "Always executed" || echo "Only executed if first command fails"
echo "Always executed" && echo "Only executed if first command does NOT fail"
# 在 if 语句中使用 && 和 || 需要多对方括号
if [ $Name == "Steve" ] && [ $Age -eq 15 ]
then
echo "This will run if $Name is Steve AND $Age is 15."
fi
if [ $Name == "Daniya" ] || [ $Name == "Zach" ]
then
echo "This will run if $Name is Daniya OR Zach."
fi
# 表达式的格式如下:
echo $(( 10 + 5 ))
# 与其他编程语言不同的是,bash 运行时依赖上下文。比如,使用 ls 时,列出当前目录。
ls
# 指令可以带有选项:
ls -l # 列出文件和目录的详细信息
# 前一个指令的输出可以当作后一个指令的输入。grep 用来匹配字符串。
# 用下面的指令列出当前目录下所有的 txt 文件:
ls -l | grep "\.txt"
# 重定向输入和输出(标准输入,标准输出,标准错误)。
# 以 ^EOF$ 作为结束标记从标准输入读取数据并覆盖 hello.py :
cat > hello.py << EOF
#!/usr/bin/env python
from __future__ import print_function
import sys
print("#stdout", file=sys.stdout)
print("#stderr", file=sys.stderr)
for line in sys.stdin:
print(line, file=sys.stdout)
EOF
# 重定向可以到输出,输入和错误输出。
python hello.py < "input.in"
python hello.py > "output.out"
python hello.py 2> "error.err"
python hello.py > "output-and-error.log" 2>&1
python hello.py > /dev/null 2>&1
# > 会覆盖已存在的文件, >> 会以累加的方式输出文件中。
python hello.py >> "output.out" 2>> "error.err"
# 覆盖 output.out , 追加 error.err 并统计行数
info bash 'Basic Shell Features' 'Redirections' > output.out 2>> error.err
wc -l output.out error.err
# 运行指令并打印文件描述符 (比如 /dev/fd/123)
# 具体可查看: man fd
echo <(echo "#helloworld")
# 以 "#helloworld" 覆盖 output.out:
cat > output.out <(echo "#helloworld")
echo "#helloworld" > output.out
echo "#helloworld" | cat > output.out
echo "#helloworld" | tee output.out >/dev/null
# 清理临时文件并显示详情(增加 '-i' 选项启用交互模式)
rm -v output.out error.err output-and-error.log
# 一个指令可用 $( ) 嵌套在另一个指令内部:
# 以下的指令会打印当前目录下的目录和文件总数
echo "There are $(ls | wc -l) items here."
# 反引号 `` 起相同作用,但不允许嵌套
# 优先使用 $( ).
echo "There are `ls | wc -l` items here."
# Bash 的 case 语句与 Java 和 C++ 中的 switch 语句类似:
case "$Variable" in
# 列出需要匹配的字符串
0) echo "There is a zero.";;
1) echo "There is a one.";;
*) echo "It is not null.";;
esac
# 循环遍历给定的参数序列:
# 变量$Variable 的值会被打印 3 次。
for Variable in {1..3}
do
echo "$Variable"
done
# 或传统的 “for循环” :
for ((a=1; a <= 3; a++))
do
echo $a
done
# 也可以用于文件
# 用 cat 输出 file1 和 file2 内容
for Variable in file1 file2
do
cat "$Variable"
done
# 或作用于其他命令的输出
# 对 ls 输出的文件执行 cat 指令。
for Output in $(ls)
do
cat "$Output"
done
# while 循环:
while [ true ]
do
echo "loop body here..."
break
done
# 你也可以使用函数
# 定义函数:
function foo ()
{
echo "Arguments work just like script arguments: $@"
echo "And: $1 $2..."
echo "This is a function"
return 0
}
# 更简单的方法
bar ()
{
echo "Another way to declare functions!"
return 0
}
# 调用函数
foo "My name is" $Name
# 有很多有用的指令需要学习:
# 打印 file.txt 的最后 10 行
tail -n 10 file.txt
# 打印 file.txt 的前 10 行
head -n 10 file.txt
# 将 file.txt 按行排序
sort file.txt
# 报告或忽略重复的行,用选项 -d 打印重复的行
uniq -d file.txt
# 打印每行中 ',' 之前内容
cut -d ',' -f 1 file.txt
# 将 file.txt 文件所有 'okay' 替换为 'great', (兼容正则表达式)
sed -i 's/okay/great/g' file.txt
# 将 file.txt 中匹配正则的行打印到标准输出
# 这里打印以 "foo" 开头, "bar" 结尾的行
grep "^foo.*bar$" file.txt
# 使用选项 "-c" 统计行数
grep -c "^foo.*bar$" file.txt
# 如果只是要按字面形式搜索字符串而不是按正则表达式,使用 fgrep (或 grep -F)
fgrep "^foo.*bar$" file.txt
# 以 bash 内建的 'help' 指令阅读 Bash 自带文档:
help
help help
help for
help return
help source
help .
# 用 man 指令阅读相关的 Bash 手册
apropos bash
man 1 bash
man bash
# 用 info 指令查阅命令的 info 文档 (info 中按 ? 显示帮助信息)
apropos info | grep '^info.*('
man info
info info
info 5 info
# 阅读 Bash 的 info 文档:
info bash
info bash 'Bash Features'
info bash 6
info --apropos bash
matlab
MATLAB 是 MATrix LABoratory (矩阵实验室)的缩写,它是一种功能强大的数值计算语言,在工程和数学领域中应用广泛。
下面是matlab 笔记
% 以百分号作为注释符
%{
多行注释
可以
这样
表示
%}
% 指令可以随意跨行,但需要在跨行处用 '...' 标明:
a = 1 + 2 + ...
+ 4
% 可以在MATLAB中直接向操作系统发出指令
!ping google.com
who % 显示内存中的所有变量
whos % 显示内存中的所有变量以及它们的类型
clear % 清除内存中的所有变量
clear('A') % 清除指定的变量
openvar('A') % 在变量编辑器中编辑指定变量
clc % 清除命令窗口中显示的所有指令
diary % 将命令窗口中的内容写入本地文件
ctrl-c % 终止当前计算
edit('myfunction.m') % 在编辑器中打开指定函数或脚本
type('myfunction.m') % 在命令窗口中打印指定函数或脚本的源码
profile on % 打开 profile 代码分析工具
profile of % 关闭 profile 代码分析工具
profile viewer % 查看 profile 代码分析工具的分析结果
help command % 在命令窗口中显示指定命令的帮助文档
doc command % 在帮助窗口中显示指定命令的帮助文档
lookfor command % 在所有 MATLAB 内置函数的头部注释块的第一行中搜索指定命令
lookfor command -all % 在所有 MATLAB 内置函数的整个头部注释块中搜索指定命令
% 输出格式
format short % 浮点数保留 4 位小数
format long % 浮点数保留 15 位小数
format bank % 金融格式,浮点数只保留 2 位小数
fprintf('text') % 在命令窗口中显示 "text"
disp('text') % 在命令窗口中显示 "text"
% 变量与表达式
myVariable = 4 % 命令窗口中将新创建的变量
myVariable = 4; % 加上分号可使命令窗口中不显示当前语句执行结果
4 + 6 % ans = 10
8 * myVariable % ans = 32
2 ^ 3 % ans = 8
a = 2; b = 3;
c = exp(a)*sin(pi/2) % c = 7.3891
% 调用函数有两种方式:
% 标准函数语法:
load('myFile.mat', 'y') % 参数放在括号内,以英文逗号分隔
% 指令语法:
load myFile.mat y % 不加括号,以空格分隔参数
% 注意在指令语法中参数不需要加引号:在这种语法下,所有输入参数都只能是文本文字,
% 不能是变量的具体值,同样也不能是输出变量
[V,D] = eig(A); % 这条函数调用无法转换成等价的指令语法
[~,D] = eig(A); % 如果结果中只需要 D 而不需要 V 则可以这样写
% 逻辑运算
1 > 5 % 假,ans = 0
10 >= 10 % 真,ans = 1
3 ~= 4 % 不等于 -> ans = 1
3 == 3 % 等于 -> ans = 1
3 > 1 && 4 > 1 % 与 -> ans = 1
3 > 1 || 4 > 1 % 或 -> ans = 1
~1 % 非 -> ans = 0
% 逻辑运算可直接应用于矩阵,运算结果也是矩阵
A > 5
% 对矩阵中每个元素做逻辑运算,若为真,则在运算结果的矩阵中对应位置的元素就是 1
A( A > 5 )
% 如此返回的向量,其元素就是 A 矩阵中所有逻辑运算为真的元素
% 字符串
a = 'MyString'
length(a) % ans = 8
a(2) % ans = y
[a,a] % ans = MyStringMyString
b = '字符串' % MATLAB目前已经可以支持包括中文在内的多种文字
length(b) % ans = 3
b(2) % ans = 符
[b,b] % ans = 字符串字符串
% 元组(cell 数组)
a = {'one', 'two', 'three'}
a(1) % ans = 'one' - 返回一个元组
char(a(1)) % ans = one - 返回一个字符串
% 结构体
A.b = {'one','two'};
A.c = [1 2];
A.d.e = false;
% 向量
x = [4 32 53 7 1]
x(2) % ans = 32,MATLAB中向量的下标索引从1开始,不是0
x(2:3) % ans = 32 53
x(2:end) % ans = 32 53 7 1
x = [4; 32; 53; 7; 1] % 列向量
x = [1:10] % x = 1 2 3 4 5 6 7 8 9 10
% 矩阵
A = [1 2 3; 4 5 6; 7 8 9]
% 以分号分隔不同的行,以空格或逗号分隔同一行中的不同元素
% A =
% 1 2 3
% 4 5 6
% 7 8 9
A(2,3) % ans = 6,A(row, column)
A(6) % ans = 8
% (隐式地将 A 的三列首尾相接组成一个列向量,然后取其下标为 6 的元素)
A(2,3) = 42 % 将第 2 行第 3 列的元素设为 42
% A =
% 1 2 3
% 4 5 42
% 7 8 9
A(2:3,2:3) % 取原矩阵中的一块作为新矩阵
%ans =
% 5 42
% 8 9
A(:,1) % 第 1 列的所有元素
%ans =
% 1
% 4
% 7
A(1,:) % 第 1 行的所有元素
%ans =
% 1 2 3
[A ; A] % 将两个矩阵上下相接构成新矩阵
%ans =
% 1 2 3
% 4 5 42
% 7 8 9
% 1 2 3
% 4 5 42
% 7 8 9
% 等价于
vertcat(A, A);
[A , A] % 将两个矩阵左右相接构成新矩阵
%ans =
% 1 2 3 1 2 3
% 4 5 42 4 5 42
% 7 8 9 7 8 9
% 等价于
horzcat(A, A);
A(:, [3 1 2]) % 重新排布原矩阵的各列
%ans =
% 3 1 2
% 42 4 5
% 9 7 8
size(A) % 返回矩阵的行数和列数,ans = 3 3
A(1, :) =[] % 删除矩阵的第 1 行
A(:, 1) =[] % 删除矩阵的第 1 列
transpose(A) % 矩阵转置,等价于 A'
ctranspose(A) % 矩阵的共轭转置(对矩阵中的每个元素取共轭复数)
% 元素运算 vs. 矩阵运算
% 单独运算符就是对矩阵整体进行矩阵运算
% 在运算符加上英文句点就是对矩阵中的元素进行元素计算
% 示例如下:
A * B % 矩阵乘法,要求 A 的列数等于 B 的行数
A .* B % 元素乘法,要求 A 和 B 形状一致(A 的行数等于 B 的行数, A 的列数等于 B 的列数)
% 元素乘法的结果是与 A 和 B 形状一致的矩阵,其每个元素等于 A 对应位置的元素乘 B 对应位置的元素
% 以下函数中,函数名以 m 结尾的执行矩阵运算,其余执行元素运算:
exp(A) % 对矩阵中每个元素做指数运算
expm(A) % 对矩阵整体做指数运算
sqrt(A) % 对矩阵中每个元素做开方运算
sqrtm(A) % 对矩阵整体做开放运算(即试图求出一个矩阵,该矩阵与自身的乘积等于 A 矩阵)
% 绘图
x = 0:.10:2*pi; % 生成一向量,其元素从 0 开始,以 0.1 的间隔一直递增到 2*pi(pi 就是圆周率)
y = sin(x);
plot(x,y)
xlabel('x axis')
ylabel('y axis')
title('Plot of y = sin(x)')
axis([0 2*pi -1 1]) % x 轴范围是从 0 到 2*pi,y 轴范围是从 -1 到 1
plot(x,y1,'-',x,y2,'--',x,y3,':') % 在同一张图中绘制多条曲线
legend('Line 1 label', 'Line 2 label') % 为图片加注图例
% 图例数量应当小于或等于实际绘制的曲线数目,从 plot 绘制的第一条曲线开始对应
% 在同一张图上绘制多条曲线的另一种方法:
% 使用 hold on,令系统保留前次绘图结果并在其上直接叠加新的曲线,
% 如果没有 hold on,则每个 plot 都会首先清除之前的绘图结果再进行绘制。
% 在 hold on 和 hold off 中可以放置任意多的 plot 指令,
% 它们和 hold on 前最后一个 plot 指令的结果都将显示在同一张图中。
plot(x, y1)
hold on
plot(x, y2)
plot(x, y3)
plot(x, y4)
hold off
loglog(x, y) % 对数—对数绘图
semilogx(x, y) % 半对数(x 轴对数)绘图
semilogy(x, y) % 半对数(y 轴对数)绘图
fplot (@(x) x^2, [2,5]) % 绘制函数 x^2 在 [2, 5] 区间的曲线
grid on % 在绘制的图中显示网格,使用 grid off 可取消网格显示
axis square % 将当前坐标系设定为正方形(保证在图形显示上各轴等长)
axis equal % 将当前坐标系设定为相等(保证在实际数值上各轴等长)
scatter(x, y); % 散点图
hist(x); % 直方图
z = sin(x);
plot3(x,y,z); % 绘制三维曲线
pcolor(A) % 伪彩色图(热图)
contour(A) % 等高线图
mesh(A) % 网格曲面图
h = figure % 创建新的图片对象并返回其句柄 h
figure(h) % 将句柄 h 对应的图片作为当前图片
close(h) % 关闭句柄 h 对应的图片
close all % 关闭 MATLAB 中所用打开的图片
close % 关闭当前图片
shg % 显示图形窗口
clf clear % 清除图形窗口中的图像,并重置图像属性
% 图像属性可以通过图像句柄进行设定
% 在创建图像时可以保存图像句柄以便于设置
% 也可以用 gcf 函数返回当前图像的句柄
h = plot(x, y); % 在创建图像时显式地保存图像句柄
set(h, 'Color', 'r')
% 颜色代码:'y' 黄色,'m' 洋红色,'c' 青色,'r' 红色,'g' 绿色,'b' 蓝色,'w' 白色,'k' 黑色
set(h, 'Color', [0.5, 0.5, 0.4])
% 也可以使用 RGB 值指定颜色
set(h, 'LineStyle', '--')
% 线型代码:'--' 实线,'---' 虚线,':' 点线,'-.' 点划线,'none' 不划线
get(h, 'LineStyle')
% 获取当前句柄的线型
% 用 gca 函数返回当前图像的坐标轴句柄
set(gca, 'XDir', 'reverse'); % 令 x 轴反向
% 用 subplot 指令创建平铺排列的多张子图
subplot(2,3,1); % 选择 2 x 3 排列的子图中的第 1 张图
plot(x1); title('First Plot') % 在选中的图中绘图
subplot(2,3,2); % 选择 2 x 3 排列的子图中的第 2 张图
plot(x2); title('Second Plot') % 在选中的图中绘图
% 要调用函数或脚本,必须保证它们在你的当前工作目录中
path % 显示当前工作目录
addpath /path/to/dir % 将指定路径加入到当前工作目录中
rmpath /path/to/dir % 将指定路径从当前工作目录中删除
cd /path/to/move/into % 以制定路径作为当前工作目录
% 变量可保存到 .mat 格式的本地文件
save('myFileName.mat') % 保存当前工作空间中的所有变量
load('myFileName.mat') % 将指定文件中的变量载入到当前工作空间
% .m 脚本文件
% 脚本文件是一个包含多条 MATLAB 指令的外部文件,以 .m 为后缀名
% 使用脚本文件可以避免在命令窗口中重复输入冗长的指令
% .m 函数文件
% 与脚本文件类似,同样以 .m 作为后缀名
% 但函数文件可以接受用户输入的参数并返回运算结果
% 并且函数拥有自己的工作空间(变量域),不必担心变量名称冲突
% 函数文件的名称应当与其所定义的函数的名称一致(比如下面例子中函数文件就应命名为 double_input.m)
% 使用 'help double_input.m' 可返回函数定义中第一行注释信息
function output = double_input(x)
% double_input(x) 返回 x 的 2 倍
output = 2*x;
end
double_input(6) % ans = 12
% 同样还可以定义子函数和内嵌函数
% 子函数与主函数放在同一个函数文件中,且只能被这个主函数调用
% 内嵌函数放在另一个函数体内,可以直接访问被嵌套函数的各个变量
% 使用匿名函数可以不必创建 .m 函数文件
% 匿名函数适用于快速定义某函数以便传递给另一指令或函数(如绘图、积分、求根、求极值等)
% 下面示例的匿名函数返回输入参数的平方根,可以使用句柄 sqr 进行调用:
sqr = @(x) x.^2;
sqr(10) % ans = 100
doc function_handle % find out more
% 接受用户输入
a = input('Enter the value: ')
% 从文件中读取数据
fopen(filename)
% 类似函数还有 xlsread(excel 文件)、importdata(CSV 文件)、imread(图像文件)
% 输出
disp(a) % 在命令窗口中打印变量 a 的值
disp('Hello World') % 在命令窗口中打印字符串
fprintf % 按照指定格式在命令窗口中打印内容
% 条件语句(if 和 elseif 语句中的括号并非必需,但推荐加括号避免混淆)
if (a > 15)
disp('Greater than 15')
elseif (a == 23)
disp('a is 23')
else
disp('neither condition met')
end
% 循环语句
% 注意:对向量或矩阵使用循环语句进行元素遍历的效率很低!!
% 注意:只要有可能,就尽量使用向量或矩阵的整体运算取代逐元素循环遍历!!
% MATLAB 在开发时对向量和矩阵运算做了专门优化,做向量和矩阵整体运算的效率高于循环语句
for k = 1:5
disp(k)
end
k = 0;
while (k < 5)
k = k + 1;
end
% 程序运行计时:'tic' 是计时开始,'toc' 是计时结束并打印结果
tic
A = rand(1000);
A*A*A*A*A*A*A;
toc
% 链接 MySQL 数据库
dbname = 'database_name';
username = 'root';
password = 'root';
driver = 'com.mysql.jdbc.Driver';
dburl = ['jdbc:mysql://localhost:8889/' dbname];
javaclasspath('mysql-connector-java-5.1.xx-bin.jar'); % 此处 xx 代表具体版本号
% 这里的 mysql-connector-java-5.1.xx-bin.jar 可从 http://dev.mysql.com/downloads/connector/j/ 下载
conn = database(dbname, username, password, driver, dburl);
sql = ['SELECT * from table_name where id = 22'] % SQL 语句
a = fetch(conn, sql) % a 即包含所需数据
% 常用数学函数
sin(x)
cos(x)
tan(x)
asin(x)
acos(x)
atan(x)
exp(x)
sqrt(x)
log(x)
log10(x)
abs(x)
min(x)
max(x)
ceil(x)
floor(x)
round(x)
rem(x)
rand % 均匀分布的伪随机浮点数
randi % 均匀分布的伪随机整数
randn % 正态分布的伪随机浮点数
% 常用常数
pi
NaN
inf
% 求解矩阵方程(如果方程无解,则返回最小二乘近似解)
% \ 操作符等价于 mldivide 函数,/ 操作符等价于 mrdivide 函数
x=A\b % 求解 Ax=b,比先求逆再左乘 inv(A)*b 更加高效、准确
x=b/A % 求解 xA=b
inv(A) % 逆矩阵
pinv(A) % 伪逆矩阵
% 常用矩阵函数
zeros(m, n) % m x n 阶矩阵,元素全为 0
ones(m, n) % m x n 阶矩阵,元素全为 1
diag(A) % 返回矩阵 A 的对角线元素
diag(x) % 构造一个对角阵,对角线元素就是向量 x 的各元素
eye(m, n) % m x n 阶单位矩阵
linspace(x1, x2, n) % 返回介于 x1 和 x2 之间的 n 个等距节点
inv(A) % 矩阵 A 的逆矩阵
det(A) % 矩阵 A 的行列式
eig(A) % 矩阵 A 的特征值和特征向量
trace(A) % 矩阵 A 的迹(即对角线元素之和),等价于 sum(diag(A))
isempty(A) % 测试 A 是否为空
all(A) % 测试 A 中所有元素是否都非 0 或都为真(逻辑值)
any(A) % 测试 A 中是否有元素非 0 或为真(逻辑值)
isequal(A, B) % 测试 A 和 B是否相等
numel(A) % 矩阵 A 的元素个数
triu(x) % 返回 x 的上三角这部分
tril(x) % 返回 x 的下三角这部分
cross(A, B) % 返回 A 和 B 的叉积(矢量积、外积)
dot(A, B) % 返回 A 和 B 的点积(数量积、内积),要求 A 和 B 必须等长
transpose(A) % A 的转置,等价于 A'
fliplr(A) % 将一个矩阵左右翻转
flipud(A) % 将一个矩阵上下翻转
% 矩阵分解
[L, U, P] = lu(A) % LU 分解:PA = LU,L 是下三角阵,U 是上三角阵,P 是置换阵
[P, D] = eig(A) % 特征值分解:AP = PD,D 是由特征值构成的对角阵,P 的各列就是对应的特征向量
[U, S, V] = svd(X) % 奇异值分解:XV = US,U 和 V 是酉矩阵,S 是由奇异值构成的半正定实数对角阵
% 常用向量函数
max % 最大值
min % 最小值
length % 元素个数
sort % 按升序排列
sum % 各元素之和
prod % 各元素之积
mode % 众数
median % 中位数
mean % 平均值
std % 标准差
perms(x) % x 元素的全排列
相关资料
- 官方网页:http://http://www.mathworks.com/products/matlab/
- 官方论坛:http://www.mathworks.com/matlabcentral/answers/
scala
/*
自行设置:
1) 下载 Scala - http://www.scala-lang.org/downloads
2) unzip/untar 到您喜欢的地方,并把 bin 子目录添加到 path 环境变量
3) 在终端输入 scala,启动 Scala 的 REPL,您会看到提示符:
scala>
这就是所谓的 REPL (读取-求值-输出循环,英语: Read-Eval-Print Loop),
您可以在其中输入合法的表达式,结果会被打印。
在教程中我们会进一步解释 Scala 文件是怎样的,但现在先了解一点基础。
*/
/////////////////////////////////////////////////
// 1. 基础
/////////////////////////////////////////////////
// 单行注释开始于两个斜杠
/*
多行注释,如您之前所见,看起来像这样
*/
// 打印并强制换行
println("Hello world!")
println(10)
// 没有强制换行的打印
print("Hello world")
// 通过 var 或者 val 来声明变量
// val 声明是不可变的,var 声明是可修改的。不可变性是好事。
val x = 10 // x 现在是 10
x = 20 // 错误: 对 val 声明的变量重新赋值
var y = 10
y = 20 // y 现在是 20
/*
Scala 是静态语言,但注意上面的声明方式,我们没有指定类型。
这是因为类型推导的语言特性。大多数情况, Scala 编译器可以推测变量的类型,
所以您不需要每次都输入。可以像这样明确声明变量类型:
*/
val z: Int = 10
val a: Double = 1.0
// 注意从 Int 到 Double 的自动转型,结果是 10.0, 不是 10
val b: Double = 10
// 布尔值
true
false
// 布尔操作
!true // false
!false // true
true == false // false
10 > 5 // true
// 数学运算像平常一样
1 + 1 // 2
2 - 1 // 1
5 * 3 // 15
6 / 2 // 3
6 / 4 // 1
6.0 / 4 // 1.5
// 在 REPL 计算一个表达式会返回给您结果的类型和值
1 + 7
/* 上行的结果是:
scala> 1 + 7
res29: Int = 8
这意味着计算 1 + 7 的结果是一个 Int 类型的对象,其值为 8
注意 "res29" 是一个连续生成的变量名,用以存储您输入的表达式结果,
您看到的输出可能不一样。
*/
"Scala strings are surrounded by double quotes"
'a' // Scala 的字符
// '不存在单引号字符串' <= 这会导致错误
// String 有常见的 Java 字符串方法
"hello world".length
"hello world".substring(2, 6)
"hello world".replace("C", "3")
// 也有一些额外的 Scala 方法,另请参见:scala.collection.immutable.StringOps
"hello world".take(5)
"hello world".drop(5)
// 字符串改写:留意前缀 "s"
val n = 45
s"We have $n apples" // => "We have 45 apples"
// 在要改写的字符串中使用表达式也是可以的
val a = Array(11, 9, 6)
s"My second daughter is ${a(0) - a(2)} years old." // => "My second daughter is 5 years old."
s"We have double the amount of ${n / 2.0} in apples." // => "We have double the amount of 22.5 in apples."
s"Power of 2: ${math.pow(2, 2)}" // => "Power of 2: 4"
// 添加 "f" 前缀对要改写的字符串进行格式化
f"Power of 5: ${math.pow(5, 2)}%1.0f" // "Power of 5: 25"
f"Square root of 122: ${math.sqrt(122)}%1.4f" // "Square root of 122: 11.0454"
// 未处理的字符串,忽略特殊字符。
raw"New line feed: \n. Carriage return: \r." // => "New line feed: \n. Carriage return: \r."
// 一些字符需要转义,比如字符串中的双引号
"They stood outside the \"Rose and Crown\"" // => "They stood outside the "Rose and Crown""
// 三个双引号可以使字符串跨越多行,并包含引号
val html = """<form id="daform">
<p>Press belo', Joe</p>
<input type="submit">
</form>"""
/////////////////////////////////////////////////
// 2. 函数
/////////////////////////////////////////////////
// 函数可以这样定义:
//
// def functionName(args...): ReturnType = { body... }
//
// 如果您以前学习过传统的编程语言,注意 return 关键字的省略。
// 在 Scala 中, 函数代码块最后一条表达式就是返回值。
def sumOfSquares(x: Int, y: Int): Int = {
val x2 = x * x
val y2 = y * y
x2 + y2
}
// 如果函数体是单行表达式,{ } 可以省略:
def sumOfSquaresShort(x: Int, y: Int): Int = x * x + y * y
// 函数调用的语法是熟知的:
sumOfSquares(3, 4) // => 25
// 在多数情况下 (递归函数是需要注意的例外), 函数返回值可以省略,
// 变量所用的类型推导一样会应用到函数返回值中:
def sq(x: Int) = x * x // 编译器会推断得知返回值是 Int
// 函数可以有默认参数
def addWithDefault(x: Int, y: Int = 5) = x + y
addWithDefault(1, 2) // => 3
addWithDefault(1) // => 6
// 匿名函数是这样的:
(x:Int) => x * x
// 和 def 不同,如果语义清晰,匿名函数的参数类型也可以省略。
// 类型 "Int => Int" 意味着这个函数接收一个 Int 并返回一个 Int。
val sq: Int => Int = x => x * x
// 匿名函数的调用也是类似的:
sq(10) // => 100
// 如果您的匿名函数中每个参数仅使用一次,
// Scala 提供一个更简洁的方式来定义他们。这样的匿名函数极为常见,
// 在数据结构部分会明显可见。
val addOne: Int => Int = _ + 1
val weirdSum: (Int, Int) => Int = (_ * 2 + _ * 3)
addOne(5) // => 6
weirdSum(2, 4) // => 16
// return 关键字是存在的,但它只从最里面包裹了 return 的 def 函数中返回。
// 警告: 在 Scala 中使用 return 容易出错,应该避免使用。
// 在匿名函数中没有效果,例如:
def foo(x: Int): Int = {
val anonFunc: Int => Int = { z =>
if (z > 5)
return z // 这一行令 z 成为 foo 函数的返回值!
else
z + 2 // 这一行是 anonFunc 函数的返回值
}
anonFunc(x) // 这一行是 foo 函数的返回值
}
/*
* 译者注:此处是指匿名函数中的 return z 成为最后执行的语句,
* 在 anonFunc(x) 下面的表达式(假设存在)不再执行。如果 anonFunc
* 是用 def 定义的函数, return z 仅返回到 anonFunc(x) ,
* 在 anonFunc(x) 下面的表达式(假设存在)会继续执行。
*/
/////////////////////////////////////////////////
// 3. 控制语句
/////////////////////////////////////////////////
1 to 5
val r = 1 to 5
r.foreach( println )
r foreach println
// 附注: Scala 对点和括号的要求想当宽松,注意其规则是不同的。
// 这有助于写出读起来像英语的 DSL(领域特定语言) 和 API(应用编程接口)。
(5 to 1 by -1) foreach ( println )
// while 循环
var i = 0
while (i < 10) { println("i " + i); i+=1 }
while (i < 10) { println("i " + i); i+=1 } // 没错,再执行一次,发生了什么?为什么?
i // 显示 i 的值。注意 while 是经典的循环方式,它连续执行并改变循环中的变量。
// while 执行很快,比 Java 的循环快,但像上面所看到的那样用组合子和推导式
// 更易于理解和并行化。
// do while 循环
do {
println("x is still less than 10");
x += 1
} while (x < 10)
// Scala 中尾递归是一种符合语言习惯的递归方式。
// 递归函数需要清晰的返回类型,编译器不能推断得知。
// 这是一个 Unit。
def showNumbersInRange(a:Int, b:Int):Unit = {
print(a)
if (a < b)
showNumbersInRange(a + 1, b)
}
showNumbersInRange(1,14)
// 条件语句
val x = 10
if (x == 1) println("yeah")
if (x == 10) println("yeah")
if (x == 11) println("yeah")
if (x == 11) println ("yeah") else println("nay")
println(if (x == 10) "yeah" else "nope")
val text = if (x == 10) "yeah" else "nope"
/////////////////////////////////////////////////
// 4. 数据结构
/////////////////////////////////////////////////
val a = Array(1, 2, 3, 5, 8, 13)
a(0)
a(3)
a(21) // 抛出异常
val m = Map("fork" -> "tenedor", "spoon" -> "cuchara", "knife" -> "cuchillo")
m("fork")
m("spoon")
m("bottle") // 抛出异常
val safeM = m.withDefaultValue("no lo se")
safeM("bottle")
val s = Set(1, 3, 7)
s(0)
s(1)
/* 这里查看 map 的文档 -
* http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.Map
* 并确保你会阅读
*/
// 元组
(1, 2)
(4, 3, 2)
(1, 2, "three")
(a, 2, "three")
// 为什么有这个?
val divideInts = (x:Int, y:Int) => (x / y, x % y)
divideInts(10,3) // 函数 divideInts 同时返回结果和余数
// 要读取元组的元素,使用 _._n,n是从1开始的元素索引
val d = divideInts(10,3)
d._1
d._2
/////////////////////////////////////////////////
// 5. 面向对象编程
/////////////////////////////////////////////////
/*
旁白: 教程中到现在为止我们所做的一切只是简单的表达式(值,函数等)。
这些表达式可以输入到命令行解释器中作为快速测试,但它们不能独立存在于 Scala
文件。举个例子,您不能在 Scala 文件上简单的写上 "val x = 5"。相反 Scala 文件
允许的顶级结构是:
- objects
- classes
- case classes
- traits
现在来解释这些是什么。
*/
// 类和其他语言的类相似,构造器参数在类名后声明,初始化在类结构体中完成。
class Dog(br: String) {
// 构造器代码在此
var breed: String = br
// 定义名为 bark 的方法,返回字符串
def bark = "Woof, woof!"
// 值和方法作用域假定为 public。"protected" 和 "private" 关键字也是可用的。
private def sleep(hours: Int) =
println(s"I'm sleeping for $hours hours")
// 抽象方法是没有方法体的方法。如果取消下面那行注释,Dog 类必须被声明为 abstract
// abstract class Dog(...) { ... }
// def chaseAfter(what: String): String
}
val mydog = new Dog("greyhound")
println(mydog.breed) // => "greyhound"
println(mydog.bark) // => "Woof, woof!"
// "object" 关键字创造一种类型和该类型的单例。
// Scala 的 class 常常也含有一个 “伴生对象”,class 中包含每个实例的行为,所有实例
// 共用的行为则放入 object 中。两者的区别和其他语言中类方法和静态方法类似。
// 请注意 object 和 class 可以同名。
object Dog {
def allKnownBreeds = List("pitbull", "shepherd", "retriever")
def createDog(breed: String) = new Dog(breed)
}
// Case 类是有额外内建功能的类。Scala 初学者常遇到的问题之一便是何时用类
// 和何时用 case 类。界线比较模糊,但通常类倾向于封装,多态和行为。类中的值
// 的作用域一般为 private , 只有方法是暴露的。case 类的主要目的是放置不可变
// 数据。它们通常只有几个方法,且方法几乎没有副作用。
case class Person(name: String, phoneNumber: String)
// 创造新实例,注意 case 类不需要使用 "new" 关键字
val george = Person("George", "1234")
val kate = Person("Kate", "4567")
// 使用 case 类,您可以轻松得到一些功能,像 getters:
george.phoneNumber // => "1234"
// 每个字段的相等性比较(无需覆盖 .equals)
Person("George", "1234") == Person("Kate", "1236") // => false
// 简单的拷贝方式
// otherGeorge == Person("george", "9876")
val otherGeorge = george.copy(phoneNumber = "9876")
// 还有很多。case 类同时可以用于模式匹配,接下来会看到。
// 敬请期待 Traits !
/////////////////////////////////////////////////
// 6. 模式匹配
/////////////////////////////////////////////////
// 模式匹配是一个强大和常用的 Scala 特性。这是用模式匹配一个 case 类的例子。
// 附注:不像其他语言, Scala 的 case 不需要 break, 其他语言中 switch 语句的
// fall-through 现象不会发生。
def matchPerson(person: Person): String = person match {
// Then you specify the patterns:
case Person("George", number) => "We found George! His number is " + number
case Person("Kate", number) => "We found Kate! Her number is " + number
case Person(name, number) => "We matched someone : " + name + ", phone : " + number
}
val email = "(.*)@(.*)".r // 定义下一个例子会用到的正则
// 模式匹配看起来和 C语言家族的 switch 语句相似,但更为强大。
// Scala 中您可以匹配很多东西:
def matchEverything(obj: Any): String = obj match {
// 匹配值:
case "Hello world" => "Got the string Hello world"
// 匹配类型:
case x: Double => "Got a Double: " + x
// 匹配时指定条件
case x: Int if x > 10000 => "Got a pretty big number!"
// 像之前一样匹配 case 类:
case Person(name, number) => s"Got contact info for $name!"
// 匹配正则表达式:
case email(name, domain) => s"Got email address $name@$domain"
// 匹配元组:
case (a: Int, b: Double, c: String) => s"Got a tuple: $a, $b, $c"
// 匹配数据结构:
case List(1, b, c) => s"Got a list with three elements and starts with 1: 1, $b, $c"
// 模式可以嵌套
case List(List((1, 2,"YAY"))) => "Got a list of list of tuple"
}
// 事实上,你可以对任何有 "unapply" 方法的对象进行模式匹配。
// 这个特性如此强大以致于 Scala 允许定义一个函数作为模式匹配:
val patternFunc: Person => String = {
case Person("George", number) => s"George's number: $number"
case Person(name, number) => s"Random person's number: $number"
}
/////////////////////////////////////////////////
// 7. 函数式编程
/////////////////////////////////////////////////
// Scala 允许方法和函数作为其他方法和函数的参数和返回值。
val add10: Int => Int = _ + 10 // 一个接受一个 Int 类型参数并返回一个 Int 类型值的函数
List(1, 2, 3) map add10 // List(11, 12, 13) - add10 被应用到每一个元素
// 匿名函数可以被使用来代替有命名的函数:
List(1, 2, 3) map (x => x + 10)
// 如果匿名函数只有一个参数可以用下划线作为变量
List(1, 2, 3) map (_ + 10)
// 如果您所应用的匿名块和匿名函数都接受一个参数,那么你甚至可以省略下划线
List("Dom", "Bob", "Natalia") foreach println
// 组合子
// 译注: val sq: Int => Int = x => x * x
s.map(sq)
val sSquared = s. map(sq)
sSquared.filter(_ < 10)
sSquared.reduce (_+_)
// filter 函数接受一个 predicate (函数根据条件 A 返回 Boolean)并选择
// 所有满足 predicate 的元素
List(1, 2, 3) filter (_ > 2) // List(3)
case class Person(name:String, age:Int)
List(
Person(name = "Dom", age = 23),
Person(name = "Bob", age = 30)
).filter(_.age > 25) // List(Person("Bob", 30))
// Scala 的 foreach 方法定义在某些集合中,接受一个函数并返回 Unit (void 方法)
// 另请参见:
// http://www.scala-lang.org/api/current/index.html#scala.collection.IterableLike@foreach(f:A=>Unit):Unit
val aListOfNumbers = List(1, 2, 3, 4, 10, 20, 100)
aListOfNumbers foreach (x => println(x))
aListOfNumbers foreach println
// For 推导式
for { n <- s } yield sq(n)
val nSquared2 = for { n <- s } yield sq(n)
for { n <- nSquared2 if n < 10 } yield n
for { n <- s; nSquared = n * n if nSquared < 10} yield nSquared
/* 注意,这些不是 for 循环,for 循环的语义是‘重复’,然而 for 推导式定义
两个数据集合的关系。 */
/////////////////////////////////////////////////
// 8. 隐式转换
/////////////////////////////////////////////////
/* 警告 警告: 隐式转换是 Scala 中一套强大的特性,因此容易被滥用。
* Scala 初学者在理解它们的工作原理和最佳实践之前,应抵制使用它的诱惑。
* 我们加入这一章节仅因为它们在 Scala 的库中太过常见,导致没有用隐式转换的库
* 就不可能做有意义的事情。这章节主要让你理解和使用隐式转换,而不是自己声明。
*/
// 可以通过 "implicit" 声明任何值(val, 函数,对象等)为隐式值,
// 请注意这些例子中,我们用到第5部分的 Dog 类。
implicit val myImplicitInt = 100
implicit def myImplicitFunction(breed: String) = new Dog("Golden " + breed)
// implicit 关键字本身不改变值的行为,所以上面的值可以照常使用。
myImplicitInt + 2 // => 102
myImplicitFunction("Pitbull").breed // => "Golden Pitbull"
// 区别在于,当另一段代码“需要”隐式值时,这些值现在有资格作为隐式值。
// 一种情况是隐式函数参数。
def sendGreetings(toWhom: String)(implicit howMany: Int) =
s"Hello $toWhom, $howMany blessings to you and yours!"
// 如果提供值给 “howMany”,函数正常运行
sendGreetings("John")(1000) // => "Hello John, 1000 blessings to you and yours!"
// 如果省略隐式参数,会传一个和参数类型相同的隐式值,
// 在这个例子中, 是 “myImplicitInt":
sendGreetings("Jane") // => "Hello Jane, 100 blessings to you and yours!"
// 隐式的函数参数使我们可以模拟其他函数式语言的 type 类(type classes)。
// 它经常被用到所以有特定的简写。这两行代码是一样的:
def foo[T](implicit c: C[T]) = ...
def foo[T : C] = ...
// 编译器寻找隐式值另一种情况是你调用方法时
// obj.method(...)
// 但 "obj" 没有一个名为 "method" 的方法。这样的话,如果有一个参数类型为 A
// 返回值类型为 B 的隐式转换,obj 的类型是 A,B 有一个方法叫 "method" ,这样
// 转换就会被应用。所以作用域里有上面的 myImplicitFunction, 我们可以这样做:
"Retriever".breed // => "Golden Retriever"
"Sheperd".bark // => "Woof, woof!"
// 这里字符串先被上面的函数转换为 Dog 对象,然后调用相应的方法。
// 这是相当强大的特性,但再次提醒,请勿轻率使用。
// 事实上,当你定义上面的隐式函数时,编译器会作出警告,除非你真的了解
// 你正在做什么否则不要使用。
/////////////////////////////////////////////////
// 9. 杂项
/////////////////////////////////////////////////
// 导入类
import scala.collection.immutable.List
// 导入所有子包
import scala.collection.immutable._
// 一条语句导入多个类
import scala.collection.immutable.{List, Map}
// 使用 ‘=>’ 对导入进行重命名
import scala.collection.immutable.{ List => ImmutableList }
// 导入所有类,排除其中一些。下面的语句排除了 Map 和 Set:
import scala.collection.immutable.{Map => _, Set => _, _}
// 在 Scala 文件用 object 和单一的 main 方法定义程序入口:
object Application {
def main(args: Array[String]): Unit = {
// stuff goes here.
}
}
// 文件可以包含多个 class 和 object,用 scalac 编译源文件
// 输入和输出
// 按行读文件
import scala.io.Source
for(line <- Source.fromFile("myfile.txt").getLines())
println(line)
// 用 Java 的 PrintWriter 写文件
val writer = new PrintWriter("myfile.txt")
writer.write("Writing line for line" + util.Properties.lineSeparator)
writer.write("Another line here" + util.Properties.lineSeparator)
writer.close()
更多的资源
加入 Scala 用户组
julia
# 单行注释只需要一个井号
#= 多行注释
只需要以 '#=' 开始 '=#' 结束
还可以嵌套.
=#
####################################################
## 1. 原始类型与操作符
####################################################
# Julia 中一切皆是表达式。
# 这是一些基本数字类型.
3 # => 3 (Int64)
3.2 # => 3.2 (Float64)
2 + 1im # => 2 + 1im (Complex{Int64})
2//3 # => 2//3 (Rational{Int64})
# 支持所有的普通中缀操作符。
1 + 1 # => 2
8 - 1 # => 7
10 * 2 # => 20
35 / 5 # => 7.0
5 / 2 # => 2.5 # 用 Int 除 Int 永远返回 Float
div(5, 2) # => 2 # 使用 div 截断小数点
5 \ 35 # => 7.0
2 ^ 2 # => 4 # 次方, 不是二进制 xor
12 % 10 # => 2
# 用括号提高优先级
(1 + 3) * 2 # => 8
# 二进制操作符
~2 # => -3 # 非
3 & 5 # => 1 # 与
2 | 4 # => 6 # 或
2 $ 4 # => 6 # 异或
2 >>> 1 # => 1 # 逻辑右移
2 >> 1 # => 1 # 算术右移
2 << 1 # => 4 # 逻辑/算术 右移
# 可以用函数 bits 查看二进制数。
bits(12345)
# => "0000000000000000000000000000000000000000000000000011000000111001"
bits(12345.0)
# => "0100000011001000000111001000000000000000000000000000000000000000"
# 布尔值是原始类型
true
false
# 布尔操作符
!true # => false
!false # => true
1 == 1 # => true
2 == 1 # => false
1 != 1 # => false
2 != 1 # => true
1 < 10 # => true
1 > 10 # => false
2 <= 2 # => true
2 >= 2 # => true
# 比较可以串联
1 < 2 < 3 # => true
2 < 3 < 2 # => false
# 字符串可以由 " 创建
"This is a string."
# 字符字面量可用 ' 创建
'a'
# 可以像取数组取值一样用 index 取出对应字符
"This is a string"[1] # => 'T' # Julia 的 index 从 1 开始 :(
# 但是对 UTF-8 无效,
# 因此建议使用遍历器 (map, for loops, 等).
# $ 可用于字符插值:
"2 + 2 = $(2 + 2)" # => "2 + 2 = 4"
# 可以将任何 Julia 表达式放入括号。
# 另一种格式化字符串的方式是 printf 宏.
@printf "%d is less than %f" 4.5 5.3 # 5 is less than 5.300000
# 打印字符串很容易
println("I'm Julia. Nice to meet you!")
####################################################
## 2. 变量与集合
####################################################
# 给变量赋值就是声明变量
some_var = 5 # => 5
some_var # => 5
# 访问未声明变量会抛出异常
try
some_other_var # => ERROR: some_other_var not defined
catch e
println(e)
end
# 变量名需要以字母开头.
# 之后任何字母,数字,下划线,叹号都是合法的。
SomeOtherVar123! = 6 # => 6
# 甚至可以用 unicode 字符
☃ = 8 # => 8
# 用数学符号非常方便
2 * π # => 6.283185307179586
# 注意 Julia 的命名规约:
#
# * 变量名为小写,单词之间以下划线连接('\_')。
#
# * 类型名以大写字母开头,单词以 CamelCase 方式连接。
#
# * 函数与宏的名字小写,无下划线。
#
# * 会改变输入的函数名末位为 !。
# 这类函数有时被称为 mutating functions 或 in-place functions.
# 数组存储一列值,index 从 1 开始。
a = Int64[] # => 0-element Int64 Array
# 一维数组可以以逗号分隔值的方式声明。
b = [4, 5, 6] # => 包含 3 个 Int64 类型元素的数组: [4, 5, 6]
b[1] # => 4
b[end] # => 6
# 二维数组以分号分隔维度。
matrix = [1 2; 3 4] # => 2x2 Int64 数组: [1 2; 3 4]
# 使用 push! 和 append! 往数组末尾添加元素
push!(a,1) # => [1]
push!(a,2) # => [1,2]
push!(a,4) # => [1,2,4]
push!(a,3) # => [1,2,4,3]
append!(a,b) # => [1,2,4,3,4,5,6]
# 用 pop 弹出末尾元素
pop!(b) # => 6 and b is now [4,5]
# 可以再放回去
push!(b,6) # b 又变成了 [4,5,6].
a[1] # => 1 # 永远记住 Julia 的 index 从 1 开始!
# 用 end 可以直接取到最后索引. 可用作任何索引表达式
a[end] # => 6
# 还支持 shift 和 unshift
shift!(a) # => 返回 1,而 a 现在时 [2,4,3,4,5,6]
unshift!(a,7) # => [7,2,4,3,4,5,6]
# 以叹号结尾的函数名表示它会改变参数的值
arr = [5,4,6] # => 包含三个 Int64 元素的数组: [5,4,6]
sort(arr) # => [4,5,6]; arr 还是 [5,4,6]
sort!(arr) # => [4,5,6]; arr 现在是 [4,5,6]
# 越界会抛出 BoundsError 异常
try
a[0] # => ERROR: BoundsError() in getindex at array.jl:270
a[end+1] # => ERROR: BoundsError() in getindex at array.jl:270
catch e
println(e)
end
# 错误会指出发生的行号,包括标准库
# 如果你有 Julia 源代码,你可以找到这些地方
# 可以用 range 初始化数组
a = [1:5] # => 5-element Int64 Array: [1,2,3,4,5]
# 可以切割数组
a[1:3] # => [1, 2, 3]
a[2:end] # => [2, 3, 4, 5]
# 用 splice! 切割原数组
arr = [3,4,5]
splice!(arr,2) # => 4 ; arr 变成了 [3,5]
# 用 append! 连接数组
b = [1,2,3]
append!(a,b) # a 变成了 [1, 2, 3, 4, 5, 1, 2, 3]
# 检查元素是否在数组中
in(1, a) # => true
# 用 length 获得数组长度
length(a) # => 8
# Tuples 是 immutable 的
tup = (1, 2, 3) # => (1,2,3) # an (Int64,Int64,Int64) tuple.
tup[1] # => 1
try:
tup[1] = 3 # => ERROR: no method setindex!((Int64,Int64,Int64),Int64,Int64)
catch e
println(e)
end
# 大多数组的函数同样支持 tuples
length(tup) # => 3
tup[1:2] # => (1,2)
in(2, tup) # => true
# 可以将 tuples 元素分别赋给变量
a, b, c = (1, 2, 3) # => (1,2,3) # a is now 1, b is now 2 and c is now 3
# 不用括号也可以
d, e, f = 4, 5, 6 # => (4,5,6)
# 单元素 tuple 不等于其元素值
(1,) == 1 # => false
(1) == 1 # => true
# 交换值
e, d = d, e # => (5,4) # d is now 5 and e is now 4
# 字典Dictionaries store mappings
empty_dict = Dict() # => Dict{Any,Any}()
# 也可以用字面量创建字典
filled_dict = ["one"=> 1, "two"=> 2, "three"=> 3]
# => Dict{ASCIIString,Int64}
# 用 [] 获得键值
filled_dict["one"] # => 1
# 获得所有键
keys(filled_dict)
# => KeyIterator{Dict{ASCIIString,Int64}}(["three"=>3,"one"=>1,"two"=>2])
# 注意,键的顺序不是插入时的顺序
# 获得所有值
values(filled_dict)
# => ValueIterator{Dict{ASCIIString,Int64}}(["three"=>3,"one"=>1,"two"=>2])
# 注意,值的顺序也一样
# 用 in 检查键值是否已存在,用 haskey 检查键是否存在
in(("one", 1), filled_dict) # => true
in(("two", 3), filled_dict) # => false
haskey(filled_dict, "one") # => true
haskey(filled_dict, 1) # => false
# 获取不存在的键的值会抛出异常
try
filled_dict["four"] # => ERROR: key not found: four in getindex at dict.jl:489
catch e
println(e)
end
# 使用 get 可以提供默认值来避免异常
# get(dictionary,key,default_value)
get(filled_dict,"one",4) # => 1
get(filled_dict,"four",4) # => 4
# 用 Sets 表示无序不可重复的值的集合
empty_set = Set() # => Set{Any}()
# 初始化一个 Set 并定义其值
filled_set = Set(1,2,2,3,4) # => Set{Int64}(1,2,3,4)
# 添加值
push!(filled_set,5) # => Set{Int64}(5,4,2,3,1)
# 检查是否存在某值
in(2, filled_set) # => true
in(10, filled_set) # => false
# 交集,并集,差集
other_set = Set(3, 4, 5, 6) # => Set{Int64}(6,4,5,3)
intersect(filled_set, other_set) # => Set{Int64}(3,4,5)
union(filled_set, other_set) # => Set{Int64}(1,2,3,4,5,6)
setdiff(Set(1,2,3,4),Set(2,3,5)) # => Set{Int64}(1,4)
####################################################
## 3. 控制流
####################################################
# 声明一个变量
some_var = 5
# 这是一个 if 语句,缩进不是必要的
if some_var > 10
println("some_var is totally bigger than 10.")
elseif some_var < 10 # elseif 是可选的.
println("some_var is smaller than 10.")
else # else 也是可选的.
println("some_var is indeed 10.")
end
# => prints "some var is smaller than 10"
# For 循环遍历
# Iterable 类型包括 Range, Array, Set, Dict, 以及 String.
for animal=["dog", "cat", "mouse"]
println("$animal is a mammal")
# 可用 $ 将 variables 或 expression 转换为字符串into strings
end
# prints:
# dog is a mammal
# cat is a mammal
# mouse is a mammal
# You can use 'in' instead of '='.
for animal in ["dog", "cat", "mouse"]
println("$animal is a mammal")
end
# prints:
# dog is a mammal
# cat is a mammal
# mouse is a mammal
for a in ["dog"=>"mammal","cat"=>"mammal","mouse"=>"mammal"]
println("$(a[1]) is a $(a[2])")
end
# prints:
# dog is a mammal
# cat is a mammal
# mouse is a mammal
for (k,v) in ["dog"=>"mammal","cat"=>"mammal","mouse"=>"mammal"]
println("$k is a $v")
end
# prints:
# dog is a mammal
# cat is a mammal
# mouse is a mammal
# While 循环
x = 0
while x < 4
println(x)
x += 1 # x = x + 1
end
# prints:
# 0
# 1
# 2
# 3
# 用 try/catch 处理异常
try
error("help")
catch e
println("caught it $e")
end
# => caught it ErrorException("help")
####################################################
## 4. 函数
####################################################
# 用关键字 'function' 可创建一个新函数
#function name(arglist)
# body...
#end
function add(x, y)
println("x is $x and y is $y")
# 最后一行语句的值为返回
x + y
end
add(5, 6) # => 在 "x is 5 and y is 6" 后会打印 11
# 还可以定义接收可变长参数的函数
function varargs(args...)
return args
# 关键字 return 可在函数内部任何地方返回
end
# => varargs (generic function with 1 method)
varargs(1,2,3) # => (1,2,3)
# 省略号 ... 被称为 splat.
# 刚刚用在了函数定义中
# 还可以用在函数的调用
# Array 或者 Tuple 的内容会变成参数列表
Set([1,2,3]) # => Set{Array{Int64,1}}([1,2,3]) # 获得一个 Array 的 Set
Set([1,2,3]...) # => Set{Int64}(1,2,3) # 相当于 Set(1,2,3)
x = (1,2,3) # => (1,2,3)
Set(x) # => Set{(Int64,Int64,Int64)}((1,2,3)) # 一个 Tuple 的 Set
Set(x...) # => Set{Int64}(2,3,1)
# 可定义可选参数的函数
function defaults(a,b,x=5,y=6)
return "$a $b and $x $y"
end
defaults('h','g') # => "h g and 5 6"
defaults('h','g','j') # => "h g and j 6"
defaults('h','g','j','k') # => "h g and j k"
try
defaults('h') # => ERROR: no method defaults(Char,)
defaults() # => ERROR: no methods defaults()
catch e
println(e)
end
# 还可以定义键值对的参数
function keyword_args(;k1=4,name2="hello") # note the ;
return ["k1"=>k1,"name2"=>name2]
end
keyword_args(name2="ness") # => ["name2"=>"ness","k1"=>4]
keyword_args(k1="mine") # => ["k1"=>"mine","name2"=>"hello"]
keyword_args() # => ["name2"=>"hello","k1"=>4]
# 可以组合各种类型的参数在同一个函数的参数列表中
function all_the_args(normal_arg, optional_positional_arg=2; keyword_arg="foo")
println("normal arg: $normal_arg")
println("optional arg: $optional_positional_arg")
println("keyword arg: $keyword_arg")
end
all_the_args(1, 3, keyword_arg=4)
# prints:
# normal arg: 1
# optional arg: 3
# keyword arg: 4
# Julia 有一等函数
function create_adder(x)
adder = function (y)
return x + y
end
return adder
end
# 这是用 "stabby lambda syntax" 创建的匿名函数
(x -> x > 2)(3) # => true
# 这个函数和上面的 create_adder 一模一样
function create_adder(x)
y -> x + y
end
# 你也可以给内部函数起个名字
function create_adder(x)
function adder(y)
x + y
end
adder
end
add_10 = create_adder(10)
add_10(3) # => 13
# 内置的高阶函数有
map(add_10, [1,2,3]) # => [11, 12, 13]
filter(x -> x > 5, [3, 4, 5, 6, 7]) # => [6, 7]
# 还可以使用 list comprehensions 替代 map
[add_10(i) for i=[1, 2, 3]] # => [11, 12, 13]
[add_10(i) for i in [1, 2, 3]] # => [11, 12, 13]
####################################################
## 5. 类型
####################################################
# Julia 有类型系统
# 所有的值都有类型;但变量本身没有类型
# 你可以用 `typeof` 函数获得值的类型
typeof(5) # => Int64
# 类型是一等值
typeof(Int64) # => DataType
typeof(DataType) # => DataType
# DataType 是代表类型的类型,也代表他自己的类型
# 类型可用作文档化,优化,以及调度
# 并不是静态检查类型
# 用户还可以自定义类型
# 跟其他语言的 records 或 structs 一样
# 用 `type` 关键字定义新的类型
# type Name
# field::OptionalType
# ...
# end
type Tiger
taillength::Float64
coatcolor # 不附带类型标注的相当于 `::Any`
end
# 构造函数参数是类型的属性
tigger = Tiger(3.5,"orange") # => Tiger(3.5,"orange")
# 用新类型作为构造函数还会创建一个类型
sherekhan = typeof(tigger)(5.6,"fire") # => Tiger(5.6,"fire")
# struct 类似的类型被称为具体类型
# 他们可被实例化但不能有子类型
# 另一种类型是抽象类型
# abstract Name
abstract Cat # just a name and point in the type hierarchy
# 抽象类型不能被实例化,但是可以有子类型
# 例如,Number 就是抽象类型
subtypes(Number) # => 6-element Array{Any,1}:
# Complex{Float16}
# Complex{Float32}
# Complex{Float64}
# Complex{T<:Real}
# ImaginaryUnit
# Real
subtypes(Cat) # => 0-element Array{Any,1}
# 所有的类型都有父类型; 可以用函数 `super` 得到父类型.
typeof(5) # => Int64
super(Int64) # => Signed
super(Signed) # => Real
super(Real) # => Number
super(Number) # => Any
super(super(Signed)) # => Number
super(Any) # => Any
# 所有这些类型,除了 Int64, 都是抽象类型.
# <: 是类型集成操作符
type Lion <: Cat # Lion 是 Cat 的子类型
mane_color
roar::String
end
# 可以继续为你的类型定义构造函数
# 只需要定义一个同名的函数
# 并调用已有的构造函数设置一个固定参数
Lion(roar::String) = Lion("green",roar)
# 这是一个外部构造函数,因为他再类型定义之外
type Panther <: Cat # Panther 也是 Cat 的子类型
eye_color
Panther() = new("green")
# Panthers 只有这个构造函数,没有默认构造函数
end
# 使用内置构造函数,如 Panther,可以让你控制
# 如何构造类型的值
# 应该尽可能使用外部构造函数而不是内部构造函数
####################################################
## 6. 多分派
####################################################
# 在Julia中, 所有的具名函数都是类属函数
# 这意味着他们都是有很大小方法组成的
# 每个 Lion 的构造函数都是类属函数 Lion 的方法
# 我们来看一个非构造函数的例子
# Lion, Panther, Tiger 的 meow 定义为
function meow(animal::Lion)
animal.roar # 使用点符号访问属性
end
function meow(animal::Panther)
"grrr"
end
function meow(animal::Tiger)
"rawwwr"
end
# 试试 meow 函数
meow(tigger) # => "rawwr"
meow(Lion("brown","ROAAR")) # => "ROAAR"
meow(Panther()) # => "grrr"
# 再看看层次结构
issubtype(Tiger,Cat) # => false
issubtype(Lion,Cat) # => true
issubtype(Panther,Cat) # => true
# 定义一个接收 Cats 的函数
function pet_cat(cat::Cat)
println("The cat says $(meow(cat))")
end
pet_cat(Lion("42")) # => prints "The cat says 42"
try
pet_cat(tigger) # => ERROR: no method pet_cat(Tiger,)
catch e
println(e)
end
# 在面向对象语言中,通常都是单分派
# 这意味着分派方法是通过第一个参数的类型决定的
# 在Julia中, 所有参数类型都会被考虑到
# 让我们定义有多个参数的函数,好看看区别
function fight(t::Tiger,c::Cat)
println("The $(t.coatcolor) tiger wins!")
end
# => fight (generic function with 1 method)
fight(tigger,Panther()) # => prints The orange tiger wins!
fight(tigger,Lion("ROAR")) # => prints The orange tiger wins!
# 让我们修改一下传入具体为 Lion 类型时的行为
fight(t::Tiger,l::Lion) = println("The $(l.mane_color)-maned lion wins!")
# => fight (generic function with 2 methods)
fight(tigger,Panther()) # => prints The orange tiger wins!
fight(tigger,Lion("ROAR")) # => prints The green-maned lion wins!
# 把 Tiger 去掉
fight(l::Lion,c::Cat) = println("The victorious cat says $(meow(c))")
# => fight (generic function with 3 methods)
fight(Lion("balooga!"),Panther()) # => prints The victorious cat says grrr
try
fight(Panther(),Lion("RAWR")) # => ERROR: no method fight(Panther,Lion)
catch
end
# 在试试让 Cat 在前面
fight(c::Cat,l::Lion) = println("The cat beats the Lion")
# => Warning: New definition
# fight(Cat,Lion) at none:1
# is ambiguous with
# fight(Lion,Cat) at none:2.
# Make sure
# fight(Lion,Lion)
# is defined first.
#fight (generic function with 4 methods)
# 警告说明了无法判断使用哪个 fight 方法
fight(Lion("RAR"),Lion("brown","rarrr")) # => prints The victorious cat says rarrr
# 结果在老版本 Julia 中可能会不一样
fight(l::Lion,l2::Lion) = println("The lions come to a tie")
fight(Lion("RAR"),Lion("brown","rarrr")) # => prints The lions come to a tie
# Under the hood
# 你还可以看看 llvm 以及生成的汇编代码
square_area(l) = l * l # square_area (generic function with 1 method)
square_area(5) #25
# 给 square_area 一个整形时发生什么
code_native(square_area, (Int32,))
# .section __TEXT,__text,regular,pure_instructions
# Filename: none
# Source line: 1 # Prologue
# push RBP
# mov RBP, RSP
# Source line: 1
# movsxd RAX, EDI # Fetch l from memory?
# imul RAX, RAX # Square l and store the result in RAX
# pop RBP # Restore old base pointer
# ret # Result will still be in RAX
code_native(square_area, (Float32,))
# .section __TEXT,__text,regular,pure_instructions
# Filename: none
# Source line: 1
# push RBP
# mov RBP, RSP
# Source line: 1
# vmulss XMM0, XMM0, XMM0 # Scalar single precision multiply (AVX)
# pop RBP
# ret
code_native(square_area, (Float64,))
# .section __TEXT,__text,regular,pure_instructions
# Filename: none
# Source line: 1
# push RBP
# mov RBP, RSP
# Source line: 1
# vmulsd XMM0, XMM0, XMM0 # Scalar double precision multiply (AVX)
# pop RBP
# ret
#
# 注意 只要参数中又浮点类型,Julia 就使用浮点指令
# 让我们计算一下圆的面积
circle_area(r) = pi * r * r # circle_area (generic function with 1 method)
circle_area(5) # 78.53981633974483
code_native(circle_area, (Int32,))
# .section __TEXT,__text,regular,pure_instructions
# Filename: none
# Source line: 1
# push RBP
# mov RBP, RSP
# Source line: 1
# vcvtsi2sd XMM0, XMM0, EDI # Load integer (r) from memory
# movabs RAX, 4593140240 # Load pi
# vmulsd XMM1, XMM0, QWORD PTR [RAX] # pi * r
# vmulsd XMM0, XMM0, XMM1 # (pi * r) * r
# pop RBP
# ret
#
code_native(circle_area, (Float64,))
# .section __TEXT,__text,regular,pure_instructions
# Filename: none
# Source line: 1
# push RBP
# mov RBP, RSP
# movabs RAX, 4593140496
# Source line: 1
# vmulsd XMM1, XMM0, QWORD PTR [RAX]
# vmulsd XMM0, XMM1, XMM0
# pop RBP
# ret
#
haskell
-- 单行注释以两个减号开头
{- 多行注释像这样
被一个闭合的块包围
-}
----------------------------------------------------
-- 1. 简单的数据类型和操作符
----------------------------------------------------
-- 数字
3 -- 3
-- 数学计算
1 + 1 -- 2
8 - 1 -- 7
10 * 2 -- 20
35 / 5 -- 7.0
-- 默认除法不是整除
35 / 4 -- 8.75
-- 整除
35 `div` 4 -- 8
-- 布尔值
True
False
-- 布尔操作
not True -- False
not False -- True
1 == 1 -- True
1 /= 1 -- False
1 < 10 -- True
-- 在上面的例子中,`not` 是一个接受一个参数的函数。
-- Haskell 不需要括号来调用函数,所有的参数都只是在函数名之后列出来
-- 因此,通常的函数调用模式是:
-- func arg1 arg2 arg3...
-- 你可以查看函数部分了解如何自行编写。
-- 字符串和字符
"This is a string." -- 字符串
'a' -- 字符
'对于字符串你不能使用单引号。' -- 错误!
-- 连接字符串
"Hello " ++ "world!" -- "Hello world!"
-- 一个字符串是一系列字符
['H', 'e', 'l', 'l', 'o'] -- "Hello"
"This is a string" !! 0 -- 'T'
----------------------------------------------------
-- 列表和元组
----------------------------------------------------
-- 一个列表中的每一个元素都必须是相同的类型。
-- 下面两个列表等价
[1, 2, 3, 4, 5]
[1..5]
-- 区间也可以这样
['A'..'F'] -- "ABCDEF"
-- 你可以在区间中指定步进
[0,2..10] -- [0, 2, 4, 6, 8, 10]
[5..1] -- 这样不行,因为 Haskell 默认递增
[5,4..1] -- [5, 4, 3, 2, 1]
-- 列表下标
[0..] !! 5 -- 5
-- 在 Haskell 你可以使用无限列表
[1..] -- 一个含有所有自然数的列表
-- 无限列表的原理是,Haskell 有“惰性求值”。
-- 这意味着 Haskell 只在需要时才会计算。
-- 所以当你获取列表的第 1000 项元素时,Haskell 会返回给你:
[1..] !! 999 -- 1000
-- Haskell 计算了列表中第 1 至 1000 项元素,但这个无限列表中剩下的元素还不存在。
-- Haskell 只有在需要时才会计算它们。
-- 连接两个列表
[1..5] ++ [6..10]
-- 往列表头增加元素
0:[1..5] -- [0, 1, 2, 3, 4, 5]
-- 其它列表操作
head [1..5] -- 1
tail [1..5] -- [2, 3, 4, 5]
init [1..5] -- [1, 2, 3, 4]
last [1..5] -- 5
-- 列表推导 (list comprehension)
[x*2 | x <- [1..5]] -- [2, 4, 6, 8, 10]
-- 附带条件
[x*2 | x <-[1..5], x*2 > 4] -- [6, 8, 10]
-- 元组中的每一个元素可以是不同类型,但是一个元组的长度是固定的
-- 一个元组
("haskell", 1)
-- 获取元组中的元素(例如,一个含有 2 个元素的元祖)
fst ("haskell", 1) -- "haskell"
snd ("haskell", 1) -- 1
----------------------------------------------------
-- 3. 函数
----------------------------------------------------
-- 一个接受两个变量的简单函数
add a b = a + b
-- 注意,如果你使用 ghci (Hakell 解释器),你需要使用 `let`,也就是
-- let add a b = a + b
-- 调用函数
add 1 2 -- 3
-- 你也可以使用反引号中置函数名:
1 `add` 2 -- 3
-- 你也可以定义不带字母的函数名,这样你可以定义自己的操作符。
-- 这里有一个做整除的操作符
(//) a b = a `div` b
35 // 4 -- 8
-- Guard:一个在函数中做条件判断的简单方法
fib x
| x < 2 = x
| otherwise = fib (x - 1) + fib (x - 2)
-- 模式匹配与 Guard 类似。
-- 这里给出了三个不同的 fib 定义。
-- Haskell 会自动调用第一个符合参数模式的声明
fib 1 = 1
fib 2 = 2
fib x = fib (x - 1) + fib (x - 2)
-- 元组的模式匹配
foo (x, y) = (x + 1, y + 2)
-- 列表的模式匹配
-- 这里 `x` 是列表中第一个元素,`xs` 是列表剩余的部分。
-- 我们可以实现自己的 map 函数:
myMap func [] = []
myMap func (x:xs) = func x:(myMap func xs)
-- 匿名函数带有一个反斜杠,后面跟着所有的参数
myMap (\x -> x + 2) [1..5] -- [3, 4, 5, 6, 7]
-- 在 fold(在一些语言称 为`inject`)中使用匿名函数
-- foldl1 意味着左折叠 (fold left), 并且使用列表中第一个值作为累加器的初始值。
foldl1 (\acc x -> acc + x) [1..5] -- 15
----------------------------------------------------
-- 4. 其它函数
----------------------------------------------------
-- 部分调用
-- 如果你调用函数时没有给出所有参数,它就被“部分调用”。
-- 它将返回一个接受余下参数的函数。
add a b = a + b
foo = add 10 -- foo 现在是一个接受一个数并对其加 10 的函数
foo 5 -- 15
-- 另一种等价写法
foo = (+10)
foo 5 -- 15
-- 函列表合
-- (.) 函数把其它函数链接到一起。
-- 例如,这里 foo 是一个接受一个值的函数。
-- 它对接受的值加 10,并对结果乘以 5,之后返回最后的值。
foo = (*5) . (+10)
-- (5 + 10) * 5 = 75
foo 5 -- 75
-- 修正优先级
-- Haskell 有另外一个函数 `$` 可以改变优先级。
-- `$` 使得 Haskell 先计算其右边的部分,然后调用左边的部分。
-- 你可以使用 `$` 来移除多余的括号。
-- 修改前
(even (fib 7)) -- False
-- 修改后
even . fib $ 7 -- False
-- 等价地
even $ fib 7 -- False
----------------------------------------------------
-- 5. 类型声明
----------------------------------------------------
-- Haskell 有一个非常强大的类型系统,一切都有一个类型声明。
-- 一些基本的类型:
5 :: Integer
"hello" :: String
True :: Bool
-- 函数也有类型
-- `not` 接受一个布尔型返回一个布尔型
-- not :: Bool -> Bool
-- 这是接受两个参数的函数
-- add :: Integer -> Integer -> Integer
-- 当你定义一个值,声明其类型是一个好做法
double :: Integer -> Integer
double x = x * 2
----------------------------------------------------
-- 6. 控制流和 If 语句
----------------------------------------------------
-- if 语句:
haskell = if 1 == 1 then "awesome" else "awful" -- haskell = "awesome"
-- if 语句也可以有多行,注意缩进:
haskell = if 1 == 1
then "awesome"
else "awful"
-- case 语句
-- 解析命令行参数:
case args of
"help" -> printHelp
"start" -> startProgram
_ -> putStrLn "bad args"
-- Haskell 没有循环,它使用递归
-- map 对一个列表中的每一个元素调用一个函数
map (*2) [1..5] -- [2, 4, 6, 8, 10]
-- 你可以使用 map 来编写 for 函数
for array func = map func array
-- 调用
for [0..5] $ \i -> show i
-- 我们也可以像这样写
for [0..5] show
-- 你可以使用 foldl 或者 foldr 来分解列表
-- foldl <fn> <initial value> <list>
foldl (\x y -> 2*x + y) 4 [1,2,3] -- 43
-- 等价于
(2 * (2 * (2 * 4 + 1) + 2) + 3)
-- foldl 从左开始,foldr 从右
foldr (\x y -> 2*x + y) 4 [1,2,3] -- 16
-- 现在它等价于
(2 * 3 + (2 * 2 + (2 * 1 + 4)))
----------------------------------------------------
-- 7. 数据类型
----------------------------------------------------
-- 在 Haskell 中声明你自己的数据类型:
data Color = Red | Blue | Green
-- 现在你可以在函数中使用它:
say :: Color -> String
say Red = "You are Red!"
say Blue = "You are Blue!"
say Green = "You are Green!"
-- 你的数据类型也可以有参数:
data Maybe a = Nothing | Just a
-- 这些都是 Maybe 类型:
Just "hello" -- `Maybe String` 类型
Just 1 -- `Maybe Int` 类型
Nothing -- 对任意 `a` 为 `Maybe a` 类型
----------------------------------------------------
-- 8. Haskell IO
----------------------------------------------------
-- 虽然不解释 Monads 就无法完全解释 IO,但大致了解并不难。
-- 当执行一个 Haskell 程序时,函数 `main` 就被调用。
-- 它必须返回一个类型 `IO ()` 的值。例如:
main :: IO ()
main = putStrLn $ "Hello, sky! " ++ (say Blue)
-- putStrLn 的类型是 String -> IO ()
-- 如果你的程序输入 String 返回 String,那样编写 IO 是最简单的。
-- 函数
-- interact :: (String -> String) -> IO ()
-- 输入一些文本,对其调用一个函数,并打印输出。
countLines :: String -> String
countLines = show . length . lines
main' = interact countLines
-- 你可以认为一个 `IO ()` 类型的值是表示计算机做的一系列操作,类似命令式语言。
-- 我们可以使用 `do` 声明来把动作连接到一起。
-- 举个列子
sayHello :: IO ()
sayHello = do
putStrLn "What is your name?"
name <- getLine -- 这里接受一行输入并绑定至 "name"
putStrLn $ "Hello, " ++ name
-- 练习:编写只读取一行输入的 `interact`
-- 然而,`sayHello` 中的代码将不会被执行。唯一被执行的动作是 `main` 的值。
-- 为了运行 `sayHello`,注释上面 `main` 的定义,替换为:
-- main = sayHello
-- 让我们来更进一步理解刚才所使用的函数 `getLine` 是怎样工作的。它的类型是:
-- getLine :: IO String
-- 你可以认为一个 `IO a` 类型的值代表了一个运行时会生成一个 `a` 类型值的程序。
-- (可能伴随其它行为)
-- 我们可以通过 `<-` 保存和重用这个值。
-- 我们也可以实现自己的 `IO String` 类型函数:
action :: IO String
action = do
putStrLn "This is a line. Duh"
input1 <- getLine
input2 <- getLine
-- `do` 语句的类型是它的最后一行
-- `return` 不是关键字,只是一个普通函数
return (input1 ++ "\n" ++ input2) -- return :: String -> IO String
-- 我们可以像调用 `getLine` 一样调用它
main'' = do
putStrLn "I will echo two lines!"
result <- action
putStrLn result
putStrLn "This was all, folks!"
-- `IO` 类型是一个 "Monad" 的例子。
-- Haskell 通过使用 Monad 使得其本身为纯函数式语言。
-- 任何与外界交互的函数(即 IO)都在它的类型声明中标记为 `IO`。
-- 这告诉我们什么样的函数是“纯洁的”(不与外界交互,不修改状态) ,
-- 什么样的函数不是 “纯洁的”。
-- 这个功能非常强大,因为纯函数并发非常容易,由此在 Haskell 中做并发非常容易。
----------------------------------------------------
-- 9. Haskell REPL
----------------------------------------------------
-- 键入 `ghci` 开始 REPL。
-- 现在你可以键入 Haskell 代码。
-- 任何新值都需要通过 `let` 来创建
let foo = 5
-- 你可以通过命令 `:t` 查看任何值的类型
>:t foo
foo :: Integer
-- 你也可以运行任何 `IO ()`类型的动作
> sayHello
What is your name?
Friend!
Hello, Friend!
Haskell 还有许多内容,包括类型类 (typeclasses) 与 Monads。这些都是令 Haskell 编程非常有趣的好东西。我们最后给出 Haskell 的一个例子,一个快速排序的实现:
qsort [] = []
qsort (p:xs) = qsort lesser ++ [p] ++ qsort greater
where lesser = filter (< p) xs
greater = filter (>= p) xs
安装 Haskell 很简单。你可以从这里获得。
你可以从优秀的
Learn you a Haskell 或者
Real World Haskell
找到更平缓的入门介绍。
ruby
# 这是单行注释
=begin
这是多行注释
没人用这个
你也不该用
=end
# 首先,也是最重要的,所有东西都是对象
# 数字是对象
3.class #=> Fixnum
3.to_s #=> "3"
# 一些基本的算术符号
1 + 1 #=> 2
8 - 1 #=> 7
10 * 2 #=> 20
35 / 5 #=> 7
# 算术符号只是语法糖而已
# 实际上是调用对象的方法
1.+(3) #=> 4
10.* 5 #=> 50
# 特殊的值也是对象
nil # 空
true # 真
false # 假
nil.class #=> NilClass
true.class #=> TrueClass
false.class #=> FalseClass
# 相等运算符
1 == 1 #=> true
2 == 1 #=> false
# 不等运算符
1 != 1 #=> false
2 != 1 #=> true
!true #=> false
!false #=> true
# 除了false自己,nil是唯一的值为false的对象
!nil #=> true
!false #=> true
!0 #=> false
# 更多比较
1 < 10 #=> true
1 > 10 #=> false
2 <= 2 #=> true
2 >= 2 #=> true
# 字符串是对象
'I am a string'.class #=> String
"I am a string too".class #=> String
placeholder = "use string interpolation"
"I can #{placeholder} when using double quoted strings"
#=> "I can use string interpolation when using double quoted strings"
# 输出值
puts "I'm printing!"
# 变量
x = 25 #=> 25
x #=> 25
# 注意赋值语句返回了赋的值
# 这意味着你可以用多重赋值语句
x = y = 10 #=> 10
x #=> 10
y #=> 10
# 按照惯例,用 snake_case 作为变量名
snake_case = true
# 使用具有描述性的运算符
path_to_project_root = '/good/name/'
path = '/bad/name/'
# 符号(Symbols,也是对象)
# 符号是不可变的,内部用整数类型表示的可重用的值。
# 通常用它代替字符串来有效地表示有意义的值。
:pending.class #=> Symbol
status = :pending
status == :pending #=> true
status == 'pending' #=> false
status == :approved #=> false
# 数组
# 这是一个数组
array = [1, 2, 3, 4, 5] #=> [1, 2, 3, 4, 5]
# 数组可以包含不同类型的元素
[1, "hello", false] #=> [1, "hello", false]
# 数组可以被索引
# 从前面开始
array[0] #=> 1
array[12] #=> nil
# 像运算符一样,[var]形式的访问
# 也就是一个语法糖
# 实际上是调用对象的[] 方法
array.[] 0 #=> 1
array.[] 12 #=> nil
# 从尾部开始
array[-1] #=> 5
# 同时指定开始的位置和长度
array[2, 3] #=> [3, 4, 5]
# 或者指定一个范围
array[1..3] #=> [2, 3, 4]
# 像这样往数组增加一个元素
array << 6 #=> [1, 2, 3, 4, 5, 6]
# 哈希表是Ruby的键值对的基本数据结构
# 哈希表由大括号定义
hash = {'color' => 'green', 'number' => 5}
hash.keys #=> ['color', 'number']
# 哈希表可以通过键快速地查询
hash['color'] #=> 'green'
hash['number'] #=> 5
# 查询一个不存在地键将会返回nil
hash['nothing here'] #=> nil
# 用 #each 方法来枚举哈希表:
hash.each do |k, v|
puts "#{k} is #{v}"
end
# 从Ruby 1.9开始, 用符号作为键的时候有特别的记号表示:
new_hash = { defcon: 3, action: true}
new_hash.keys #=> [:defcon, :action]
# 小贴士:数组和哈希表都是可枚举的
# 它们可以共享一些有用的方法,比如each, map, count 等等
# 控制流
if true
"if statement"
elsif false
"else if, optional"
else
"else, also optional"
end
for counter in 1..5
puts "iteration #{counter}"
end
#=> iteration 1
#=> iteration 2
#=> iteration 3
#=> iteration 4
#=> iteration 5
# 然而
# 没人用for循环
# 用`each`来代替,就像这样
(1..5).each do |counter|
puts "iteration #{counter}"
end
#=> iteration 1
#=> iteration 2
#=> iteration 3
#=> iteration 4
#=> iteration 5
counter = 1
while counter <= 5 do
puts "iteration #{counter}"
counter += 1
end
#=> iteration 1
#=> iteration 2
#=> iteration 3
#=> iteration 4
#=> iteration 5
grade = 'B'
case grade
when 'A'
puts "Way to go kiddo"
when 'B'
puts "Better luck next time"
when 'C'
puts "You can do better"
when 'D'
puts "Scraping through"
when 'F'
puts "You failed!"
else
puts "Alternative grading system, eh?"
end
# 函数
def double(x)
x * 2
end
# 函数 (以及所有的方法块) 隐式地返回了最后语句的值
double(2) #=> 4
# 当不存在歧义的时候括号是可有可无的
double 3 #=> 6
double double 3 #=> 12
def sum(x,y)
x + y
end
# 方法的参数通过逗号分隔
sum 3, 4 #=> 7
sum sum(3,4), 5 #=> 12
# yield
# 所有的方法都有一个隐式的块参数
# 可以用yield参数调用
def surround
puts "{"
yield
puts "}"
end
surround { puts 'hello world' }
# {
# hello world
# }
# 用class关键字定义一个类
class Human
# 一个类变量,它被这个类地所有实例变量共享
@@species = "H. sapiens"
# 构造函数
def initialize(name, age=0)
# 将参数name的值赋给实例变量@name
@name = name
# 如果没有给出age, 那么会采用参数列表中地默认地值
@age = age
end
# 基本的 setter 方法
def name=(name)
@name = name
end
# 基本地 getter 方法
def name
@name
end
# 一个类方法以self.开头
# 它可以被类调用,但不能被类的实例调用
def self.say(msg)
puts "#{msg}"
end
def species
@@species
end
end
# 类的例子
jim = Human.new("Jim Halpert")
dwight = Human.new("Dwight K. Schrute")
# 让我们来调用一些方法
jim.species #=> "H. sapiens"
jim.name #=> "Jim Halpert"
jim.name = "Jim Halpert II" #=> "Jim Halpert II"
jim.name #=> "Jim Halpert II"
dwight.species #=> "H. sapiens"
dwight.name #=> "Dwight K. Schrute"
# 调用对象的方法
Human.say("Hi") #=> "Hi"