summaryrefslogtreecommitdiffhomepage
path: root/zh-cn
diff options
context:
space:
mode:
Diffstat (limited to 'zh-cn')
-rw-r--r--zh-cn/bash-cn.html.markdown189
-rw-r--r--zh-cn/c++-cn.html.markdown572
-rw-r--r--zh-cn/c-cn.html.markdown4
-rw-r--r--zh-cn/common-lisp-cn.html.markdown2
-rw-r--r--zh-cn/go-cn.html.markdown2
-rw-r--r--zh-cn/haskell-cn.html.markdown265
-rw-r--r--zh-cn/java-cn.html.markdown15
-rw-r--r--zh-cn/javascript-cn.html.markdown516
-rw-r--r--zh-cn/julia-cn.html.markdown2
-rw-r--r--zh-cn/livescript-cn.html.markdown322
-rw-r--r--zh-cn/lua-cn.html.markdown9
-rw-r--r--zh-cn/markdown-cn.html.markdown240
-rw-r--r--zh-cn/matlab-cn.html.markdown491
-rw-r--r--zh-cn/php-cn.html.markdown2
-rw-r--r--zh-cn/python3-cn.html.markdown629
-rw-r--r--zh-cn/r-cn.html.markdown2
-rw-r--r--zh-cn/ruby-cn.html.markdown2
-rw-r--r--zh-cn/scala-cn.html.markdown560
-rw-r--r--zh-cn/swift-cn.html.markdown592
19 files changed, 3723 insertions, 693 deletions
diff --git a/zh-cn/bash-cn.html.markdown b/zh-cn/bash-cn.html.markdown
index 6afa659a..558d9110 100644
--- a/zh-cn/bash-cn.html.markdown
+++ b/zh-cn/bash-cn.html.markdown
@@ -5,7 +5,14 @@ contributors:
- ["Max Yankov", "https://github.com/golergka"]
- ["Darren Lin", "https://github.com/CogBear"]
- ["Alexandre Medeiros", "http://alemedeiros.sdf.org"]
+ - ["Denis Arh", "https://github.com/darh"]
+ - ["akirahirose", "https://twitter.com/akirahirose"]
+ - ["Anton Strömkvist", "http://lutic.org/"]
+ - ["Rahil Momin", "https://github.com/iamrahil"]
+ - ["Gregrory Kielian", "https://github.com/gskielian"]
+ - ["Etan Reisner", "https://github.com/deryni"]
translators:
+ - ["Jinchang Ye", "https://github.com/Alwayswithme"]
- ["Chunyang Xu", "https://github.com/XuChunyang"]
filename: LearnBash-cn.sh
lang: zh-cn
@@ -23,31 +30,45 @@ Bash 是一个为 GNU 计划编写的 Unix shell,是 Linux 和 Mac OS X 下的
# 如你所见,注释以 # 开头,shebang 也是注释。
# 显示 “Hello world!”
-echo Hello, world!
+echo Hello world!
# 每一句指令以换行或分号隔开:
echo 'This is the first line'; echo 'This is the second line'
# 声明一个变量:
-VARIABLE="Some string"
+Variable="Some string"
# 下面是错误的做法:
-VARIABLE = "Some string"
-# Bash 会把 VARIABLE 当做一个指令,由于找不到该指令,因此这里会报错。
+Variable = "Some string"
+# Bash 会把 Variable 当做一个指令,由于找不到该指令,因此这里会报错。
+# 也不可以这样:
+Variable= 'Some string'
+# Bash 会认为 'Some string' 是一条指令,由于找不到该指令,这里再次报错。
+# (这个例子中 'Variable=' 这部分会被当作仅对 'Some string' 起作用的赋值。)
# 使用变量:
-echo $VARIABLE
-echo "$VARIABLE"
-echo '$VARIABLE'
+echo $Variable
+echo "$Variable"
+echo '$Variable'
# 当你赋值 (assign) 、导出 (export),或者以其他方式使用变量时,变量名前不加 $。
# 如果要使用变量的值, 则要加 $。
# 注意: ' (单引号) 不会展开变量(即会屏蔽掉变量)。
# 在变量内部进行字符串代换
-echo ${VARIABLE/Some/A}
-# 会把 VARIABLE 中首次出现的 "some" 替换成 “A”。
+echo ${Variable/Some/A}
+# 会把 Variable 中首次出现的 "some" 替换成 “A”。
+
+# 变量的截取
+Length=7
+echo ${Variable:0:Length}
+# 这样会仅返回变量值的前7个字符
+
+# 变量的默认值
+echo ${Foo:-"DefaultValueIfFooIsMissingOrEmpty"}
+# 对 null (Foo=) 和空串 (Foo="") 起作用; 零(Foo=0)时返回0
+# 注意这仅返回默认值而不是改变变量的值
# 内置变量:
# 下面的内置变量很有用
@@ -55,26 +76,37 @@ echo "Last program return value: $?"
echo "Script's PID: $$"
echo "Number of arguments: $#"
echo "Scripts arguments: $@"
-echo "Scripts arguments separeted in different variables: $1 $2..."
+echo "Scripts arguments separated in different variables: $1 $2..."
# 读取输入:
echo "What's your name?"
-read NAME # 这里不需要声明新变量
-echo Hello, $NAME!
+read Name # 这里不需要声明新变量
+echo Hello, $Name!
# 通常的 if 结构看起来像这样:
# 'man test' 可查看更多的信息
-if [ $NAME -ne $USER ]
+if [ $Name -ne $USER ]
then
- echo "Your name is you username"
+ echo "Your name isn't your username"
else
- echo "Your name isn't you username"
+ echo "Your name is your username"
fi
# 根据上一个指令执行结果决定是否执行下一个指令
-echo "Always executed" || echo "Only executed if first command fail"
+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 ))
@@ -88,18 +120,54 @@ ls -l # 列出文件和目录的详细信息
# 用下面的指令列出当前目录下所有的 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
+
# 重定向可以到输出,输入和错误输出。
-python2 hello.py < "input.in"
-python2 hello.py > "output.out"
-python2 hello.py 2> "error.err"
+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
+case "$Variable" in
# 列出需要匹配的字符串
0) echo "There is a zero.";;
1) echo "There is a one.";;
@@ -107,11 +175,37 @@ case "$VARIABLE" in
esac
# 循环遍历给定的参数序列:
-# 变量$VARIABLE 的值会被打印 3 次。
-# 注意 ` ` 和 $( ) 等价。seq 返回长度为 3 的数组。
-for VARIABLE in `seq 3`
+# 变量$Variable 的值会被打印 3 次。
+for Variable in {1..3}
+do
+ echo "$Variable"
+done
+
+# 或传统的 “for循环” :
+for ((a=1; a <= 3; a++))
do
- echo "$VARIABLE"
+ 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
# 你也可以使用函数
@@ -132,17 +226,52 @@ bar ()
}
# 调用函数
-foo "My name is" $NAME
+foo "My name is" $Name
# 有很多有用的指令需要学习:
-tail -n 10 file.txt
# 打印 file.txt 的最后 10 行
-head -n 10 file.txt
+tail -n 10 file.txt
# 打印 file.txt 的前 10 行
-sort file.txt
+head -n 10 file.txt
# 将 file.txt 按行排序
-uniq -d file.txt
+sort file.txt
# 报告或忽略重复的行,用选项 -d 打印重复的行
-cut -d ',' -f 1 file.txt
+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 .
+
+# 用 mam 指令阅读相关的 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
```
diff --git a/zh-cn/c++-cn.html.markdown b/zh-cn/c++-cn.html.markdown
new file mode 100644
index 00000000..e1551e2b
--- /dev/null
+++ b/zh-cn/c++-cn.html.markdown
@@ -0,0 +1,572 @@
+---
+language: c++
+filename: learncpp-cn.cpp
+contributors:
+ - ["Steven Basart", "http://github.com/xksteven"]
+ - ["Matt Kline", "https://github.com/mrkline"]
+translators:
+ - ["Arnie97", "https://github.com/Arnie97"]
+lang: zh-cn
+---
+
+C++是一种系统编程语言。用它的发明者,
+[Bjarne Stroustrup的话](http://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2014/Keynote)来说,C++的设计目标是:
+
+- 成为“更好的C语言”
+- 支持数据的抽象与封装
+- 支持面向对象编程
+- 支持泛型编程
+
+C++提供了对硬件的紧密控制(正如C语言一样),
+能够编译为机器语言,由处理器直接执行。
+与此同时,它也提供了泛型、异常和类等高层功能。
+虽然C++的语法可能比某些出现较晚的语言更复杂,它仍然得到了人们的青睞——
+功能与速度的平衡使C++成为了目前应用最广泛的系统编程语言之一。
+
+```c++
+////////////////
+// 与C语言的比较
+////////////////
+
+// C++_几乎_是C语言的一个超集,它与C语言的基本语法有许多相同之处,
+// 例如变量和函数的声明,原生数据类型等等。
+
+// 和C语言一样,在C++中,你的程序会从main()开始执行,
+// 该函数的返回值应当为int型,这个返回值会作为程序的退出状态值。
+// 不过,大多数的编译器(gcc,clang等)也接受 void main() 的函数原型。
+// (参见 http://en.wikipedia.org/wiki/Exit_status 来获取更多信息)
+int main(int argc, char** argv)
+{
+ // 和C语言一样,命令行参数通过argc和argv传递。
+ // argc代表命令行参数的数量,
+ // 而argv是一个包含“C语言风格字符串”(char *)的数组,
+ // 其中每个字符串代表一个命令行参数的内容,
+ // 首个命令行参数是调用该程序时所使用的名称。
+ // 如果你不关心命令行参数的值,argc和argv可以被忽略。
+ // 此时,你可以用int main()作为函数原型。
+
+ // 退出状态值为0时,表示程序执行成功
+ return 0;
+}
+
+// 然而,C++和C语言也有一些区别:
+
+// 在C++中,字符字面量的大小是一个字节。
+sizeof('c') == 1
+
+// 在C语言中,字符字面量的大小与int相同。
+sizeof('c') == sizeof(10)
+
+
+// C++的函数原型与函数定义是严格匹配的
+void func(); // 这个函数不能接受任何参数
+
+// 而在C语言中
+void func(); // 这个函数能接受任意数量的参数
+
+// 在C++中,用nullptr代替C语言中的NULL
+int* ip = nullptr;
+
+// C++也可以使用C语言的标准头文件,
+// 但是需要加上前缀“c”并去掉末尾的“.h”。
+#include <cstdio>
+
+int main()
+{
+ printf("Hello, world!\n");
+ return 0;
+}
+
+///////////
+// 函数重载
+///////////
+
+// C++支持函数重载,你可以定义一组名称相同而参数不同的函数。
+
+void print(char const* myString)
+{
+ printf("String %s\n", myString);
+}
+
+void print(int myInt)
+{
+ printf("My int is %d", myInt);
+}
+
+int main()
+{
+ print("Hello"); // 解析为 void print(const char*)
+ print(15); // 解析为 void print(int)
+}
+
+///////////////////
+// 函数参数的默认值
+///////////////////
+
+// 你可以为函数的参数指定默认值,
+// 它们将会在调用者没有提供相应参数时被使用。
+
+void doSomethingWithInts(int a = 1, int b = 4)
+{
+ // 对两个参数进行一些操作
+}
+
+int main()
+{
+ doSomethingWithInts(); // a = 1, b = 4
+ doSomethingWithInts(20); // a = 20, b = 4
+ doSomethingWithInts(20, 5); // a = 20, b = 5
+}
+
+// 默认参数必须放在所有的常规参数之后。
+
+void invalidDeclaration(int a = 1, int b) // 这是错误的!
+{
+}
+
+
+///////////
+// 命名空间
+///////////
+
+// 命名空间为变量、函数和其他声明提供了分离的的作用域。
+// 命名空间可以嵌套使用。
+
+namespace First {
+ namespace Nested {
+ void foo()
+ {
+ printf("This is First::Nested::foo\n");
+ }
+ } // 结束嵌套的命名空间Nested
+} // 结束命名空间First
+
+namespace Second {
+ void foo()
+ {
+ printf("This is Second::foo\n")
+ }
+}
+
+void foo()
+{
+ printf("This is global foo\n");
+}
+
+int main()
+{
+ // 如果没有特别指定,就从“Second”中取得所需的内容。
+ using namespace Second;
+
+ foo(); // 显示“This is Second::foo”
+ First::Nested::foo(); // 显示“This is First::Nested::foo”
+ ::foo(); // 显示“This is global foo”
+}
+
+////////////
+// 输入/输出
+////////////
+
+// C++使用“流”来输入输出。<<是流的插入运算符,>>是流提取运算符。
+// cin、cout、和cerr分别代表
+// stdin(标准输入)、stdout(标准输出)和stderr(标准错误)。
+
+#include <iostream> // 引入包含输入/输出流的头文件
+
+using namespace std; // 输入输出流在std命名空间(也就是标准库)中。
+
+int main()
+{
+ int myInt;
+
+ // 在标准输出(终端/显示器)中显示
+ cout << "Enter your favorite number:\n";
+ // 从标准输入(键盘)获得一个值
+ cin >> myInt;
+
+ // cout也提供了格式化功能
+ cout << "Your favorite number is " << myInt << "\n";
+ // 显示“Your favorite number is <myInt>”
+
+ cerr << "Used for error messages";
+}
+
+/////////
+// 字符串
+/////////
+
+// C++中的字符串是对象,它们有很多成员函数
+#include <string>
+
+using namespace std; // 字符串也在std命名空间(标准库)中。
+
+string myString = "Hello";
+string myOtherString = " World";
+
+// + 可以用于连接字符串。
+cout << myString + myOtherString; // "Hello World"
+
+cout << myString + " You"; // "Hello You"
+
+// C++中的字符串是可变的,具有“值语义”。
+myString.append(" Dog");
+cout << myString; // "Hello Dog"
+
+
+/////////////
+// 引用
+/////////////
+
+// 除了支持C语言中的指针类型以外,C++还提供了_引用_。
+// 引用是一种特殊的指针类型,一旦被定义就不能重新赋值,并且不能被设置为空值。
+// 使用引用时的语法与原变量相同:
+// 也就是说,对引用类型进行解引用时,不需要使用*;
+// 赋值时也不需要用&来取地址。
+
+using namespace std;
+
+string foo = "I am foo";
+string bar = "I am bar";
+
+
+string& fooRef = foo; // 建立了一个对foo的引用。
+fooRef += ". Hi!"; // 通过引用来修改foo的值
+cout << fooRef; // "I am foo. Hi!"
+
+// 这句话的并不会改变fooRef的指向,其效果与“foo = bar”相同。
+// 也就是说,在执行这条语句之后,foo == "I am bar"。
+fooRef = bar;
+
+const string& barRef = bar; // 建立指向bar的常量引用。
+// 和C语言中一样,(指针和引用)声明为常量时,对应的值不能被修改。
+barRef += ". Hi!"; // 这是错误的,不能修改一个常量引用的值。
+
+///////////////////
+// 类与面向对象编程
+///////////////////
+
+// 有关类的第一个示例
+#include <iostream>
+
+// 声明一个类。
+// 类通常在头文件(.h或.hpp)中声明。
+class Dog {
+ // 成员变量和成员函数默认情况下是私有(private)的。
+ std::string name;
+ int weight;
+
+// 在这个标签之后,所有声明都是公有(public)的,
+// 直到重新指定“private:”(私有继承)或“protected:”(保护继承)为止
+public:
+
+ // 默认的构造器
+ Dog();
+
+ // 这里是成员函数声明的一个例子。
+ // 可以注意到,我们在此处使用了std::string,而不是using namespace std
+ // 语句using namespace绝不应当出现在头文件当中。
+ void setName(const std::string& dogsName);
+
+ void setWeight(int dogsWeight);
+
+ // 如果一个函数不对对象的状态进行修改,
+ // 应当在声明中加上const。
+ // 这样,你就可以对一个以常量方式引用的对象执行该操作。
+ // 同时可以注意到,当父类的成员函数需要被子类重写时,
+ // 父类中的函数必须被显式声明为_虚函数(virtual)_。
+ // 考虑到性能方面的因素,函数默认情况下不会被声明为虚函数。
+ virtual void print() const;
+
+ // 函数也可以在class body内部定义。
+ // 这样定义的函数会自动成为内联函数。
+ void bark() const { std::cout << name << " barks!\n" }
+
+ // 除了构造器以外,C++还提供了析构器。
+ // 当一个对象被删除或者脱离其定义域时,它的析构函数会被调用。
+ // 这使得RAII这样的强大范式(参见下文)成为可能。
+ // 为了衍生出子类来,基类的析构函数必须定义为虚函数。
+ virtual ~Dog();
+
+}; // 在类的定义之后,要加一个分号
+
+// 类的成员函数通常在.cpp文件中实现。
+void Dog::Dog()
+{
+ std::cout << "A dog has been constructed\n";
+}
+
+// 对象(例如字符串)应当以引用的形式传递,
+// 对于不需要修改的对象,最好使用常量引用。
+void Dog::setName(const std::string& dogsName)
+{
+ name = dogsName;
+}
+
+void Dog::setWeight(int dogsWeight)
+{
+ weight = dogsWeight;
+}
+
+// 虚函数的virtual关键字只需要在声明时使用,不需要在定义时重复
+void Dog::print() const
+{
+ std::cout << "Dog is " << name << " and weighs " << weight << "kg\n";
+}
+
+void Dog::~Dog()
+{
+ cout << "Goodbye " << name << "\n";
+}
+
+int main() {
+ Dog myDog; // 此时显示“A dog has been constructed”
+ myDog.setName("Barkley");
+ myDog.setWeight(10);
+ myDog.printDog(); // 显示“Dog is Barkley and weighs 10 kg”
+ return 0;
+} // 显示“Goodbye Barkley”
+
+// 继承:
+
+// 这个类继承了Dog类中的公有(public)和保护(protected)对象
+class OwnedDog : public Dog {
+
+ void setOwner(const std::string& dogsOwner)
+
+ // 重写OwnedDogs类的print方法。
+ // 如果你不熟悉子类多态的话,可以参考这个页面中的概述:
+ // http://zh.wikipedia.org/wiki/%E5%AD%90%E7%B1%BB%E5%9E%8B
+
+ // override关键字是可选的,它确保你所重写的是基类中的方法。
+ void print() const override;
+
+private:
+ std::string owner;
+};
+
+// 与此同时,在对应的.cpp文件里:
+
+void OwnedDog::setOwner(const std::string& dogsOwner)
+{
+ owner = dogsOwner;
+}
+
+void OwnedDog::print() const
+{
+ Dog::print(); // 调用基类Dog中的print方法
+ // "Dog is <name> and weights <weight>"
+
+ std::cout << "Dog is owned by " << owner << "\n";
+ // "Dog is owned by <owner>"
+}
+
+/////////////////////
+// 初始化与运算符重载
+/////////////////////
+
+// 在C++中,通过定义一些特殊名称的函数,
+// 你可以重载+、-、*、/等运算符的行为。
+// 当运算符被使用时,这些特殊函数会被调用,从而实现运算符重载。
+
+#include <iostream>
+using namespace std;
+
+class Point {
+public:
+ // 可以以这样的方式为成员变量设置默认值。
+ double x = 0;
+ double y = 0;
+
+ // 定义一个默认的构造器。
+ // 除了将Point初始化为(0, 0)以外,这个函数什么都不做。
+ Point() { };
+
+ // 下面使用的语法称为初始化列表,
+ // 这是初始化类中成员变量的正确方式。
+ Point (double a, double b) :
+ x(a),
+ y(b)
+ { /* 除了初始化成员变量外,什么都不做 */ }
+
+ // 重载 + 运算符
+ Point operator+(const Point& rhs) const;
+
+ // 重载 += 运算符
+ Point& operator+=(const Point& rhs);
+
+ // 增加 - 和 -= 运算符也是有意义的,但这里不再赘述。
+};
+
+Point Point::operator+(const Point& rhs) const
+{
+ // 创建一个新的点,
+ // 其横纵坐标分别为这个点与另一点在对应方向上的坐标之和。
+ return Point(x + rhs.x, y + rhs.y);
+}
+
+Point& Point::operator+=(const Point& rhs)
+{
+ x += rhs.x;
+ y += rhs.y;
+ return *this;
+}
+
+int main () {
+ Point up (0,1);
+ Point right (1,0);
+ // 这里使用了Point类型的运算符“+”
+ // 调用up(Point类型)的“+”方法,并以right作为函数的参数
+ Point result = up + right;
+ // 显示“Result is upright (1,1)”
+ cout << "Result is upright (" << result.x << ',' << result.y << ")\n";
+ return 0;
+}
+
+///////////
+// 异常处理
+///////////
+
+// 标准库中提供了一些基本的异常类型
+// (参见http://en.cppreference.com/w/cpp/error/exception)
+// 但是,其他任何类型也可以作为一个异常被拋出
+#include <exception>
+
+// 在_try_代码块中拋出的异常可以被随后的_catch_捕获。
+try {
+ // 不要用 _new_关键字在堆上为异常分配空间。
+ throw std::exception("A problem occurred");
+}
+// 如果拋出的异常是一个对象,可以用常量引用来捕获它
+catch (const std::exception& ex)
+{
+ std::cout << ex.what();
+// 捕获尚未被_catch_处理的所有错误
+} catch (...)
+{
+ std::cout << "Unknown exception caught";
+ throw; // 重新拋出异常
+}
+
+///////
+// RAII
+///////
+
+// RAII指的是“资源获取就是初始化”(Resource Allocation Is Initialization),
+// 它被视作C++中最强大的编程范式之一。
+// 简单说来,它指的是,用构造函数来获取一个对象的资源,
+// 相应的,借助析构函数来释放对象的资源。
+
+// 为了理解这一范式的用处,让我们考虑某个函数使用文件句柄时的情况:
+void doSomethingWithAFile(const char* filename)
+{
+ // 首先,让我们假设一切都会顺利进行。
+
+ FILE* fh = fopen(filename, "r"); // 以只读模式打开文件
+
+ doSomethingWithTheFile(fh);
+ doSomethingElseWithIt(fh);
+
+ fclose(fh); // 关闭文件句柄
+}
+
+// 不幸的是,随着错误处理机制的引入,事情会变得复杂。
+// 假设fopen函数有可能执行失败,
+// 而doSomethingWithTheFile和doSomethingElseWithIt会在失败时返回错误代码。
+// (虽然异常是C++中处理错误的推荐方式,
+// 但是某些程序员,尤其是有C语言背景的,并不认可异常捕获机制的作用)。
+// 现在,我们必须检查每个函数调用是否成功执行,并在问题发生的时候关闭文件句柄。
+bool doSomethingWithAFile(const char* filename)
+{
+ FILE* fh = fopen(filename, "r"); // 以只读模式打开文件
+ if (fh == nullptr) // 当执行失败是,返回的指针是nullptr
+ return false; // 向调用者汇报错误
+
+ // 假设每个函数会在执行失败时返回false
+ if (!doSomethingWithTheFile(fh)) {
+ fclose(fh); // 关闭文件句柄,避免造成内存泄漏。
+ return false; // 反馈错误
+ }
+ if (!doSomethingElseWithIt(fh)) {
+ fclose(fh); // 关闭文件句柄
+ return false; // 反馈错误
+ }
+
+ fclose(fh); // 关闭文件句柄
+ return true; // 指示函数已成功执行
+}
+
+// C语言的程序员通常会借助goto语句简化上面的代码:
+bool doSomethingWithAFile(const char* filename)
+{
+ FILE* fh = fopen(filename, "r");
+ if (fh == nullptr)
+ return false;
+
+ if (!doSomethingWithTheFile(fh))
+ goto failure;
+
+ if (!doSomethingElseWithIt(fh))
+ goto failure;
+
+ fclose(fh); // 关闭文件
+ return true; // 执行成功
+
+failure:
+ fclose(fh);
+ return false; // 反馈错误
+}
+
+// 如果用异常捕获机制来指示错误的话,
+// 代码会变得清晰一些,但是仍然有优化的余地。
+void doSomethingWithAFile(const char* filename)
+{
+ FILE* fh = fopen(filename, "r"); // 以只读模式打开文件
+ if (fh == nullptr)
+ throw std::exception("Could not open the file.");
+
+ try {
+ doSomethingWithTheFile(fh);
+ doSomethingElseWithIt(fh);
+ }
+ catch (...) {
+ fclose(fh); // 保证出错的时候文件被正确关闭
+ throw; // 之后,重新抛出这个异常
+ }
+
+ fclose(fh); // 关闭文件
+ // 所有工作顺利完成
+}
+
+// 相比之下,使用C++中的文件流类(fstream)时,
+// fstream会利用自己的析构器来关闭文件句柄。
+// 只要离开了某一对象的定义域,它的析构函数就会被自动调用。
+void doSomethingWithAFile(const std::string& filename)
+{
+ // ifstream是输入文件流(input file stream)的简称
+ std::ifstream fh(filename); // 打开一个文件
+
+ // 对文件进行一些操作
+ doSomethingWithTheFile(fh);
+ doSomethingElseWithIt(fh);
+
+} // 文件已经被析构器自动关闭
+
+// 与上面几种方式相比,这种方式有着_明显_的优势:
+// 1. 无论发生了什么情况,资源(此例当中是文件句柄)都会被正确关闭。
+// 只要你正确使用了析构器,就_不会_因为忘记关闭句柄,造成资源的泄漏。
+// 2. 可以注意到,通过这种方式写出来的代码十分简洁。
+// 析构器会在后台关闭文件句柄,不再需要你来操心这些琐事。
+// 3. 这种方式的代码具有异常安全性。
+// 无论在函数中的何处拋出异常,都不会阻碍对文件资源的释放。
+
+// 地道的C++代码应当把RAII的使用扩展到各种类型的资源上,包括:
+// - 用unique_ptr和shared_ptr管理的内存
+// - 各种数据容器,例如标准库中的链表、向量(容量自动扩展的数组)、散列表等;
+// 当它们脱离作用域时,析构器会自动释放其中储存的内容。
+// - 用lock_guard和unique_lock实现的互斥
+```
+扩展阅读:
+
+<http://cppreference.com/w/cpp> 提供了最新的语法参考。
+
+可以在 <http://cplusplus.com> 找到一些补充资料。
diff --git a/zh-cn/c-cn.html.markdown b/zh-cn/c-cn.html.markdown
index 223f6e35..1e10416e 100644
--- a/zh-cn/c-cn.html.markdown
+++ b/zh-cn/c-cn.html.markdown
@@ -566,7 +566,7 @@ typedef void (*my_fnp_type)(char *);
'\'' // 单引号
'\"' // 双引号
'\xhh' // 十六进制数字. 例子: '\xb' = vertical tab
-'\ooo' // 十进制数字. 例子: '\013' = vertical tab
+'\ooo' // 八进制数字. 例子: '\013' = vertical tab
// 打印格式:
"%d" // 整数
@@ -579,7 +579,7 @@ typedef void (*my_fnp_type)(char *);
"%c" // 字母
"%p" // 指针
"%x" // 十六进制
-"%o" // 十进制
+"%o" // 八进制
"%%" // 打印 %
///////////////////////////////////////
diff --git a/zh-cn/common-lisp-cn.html.markdown b/zh-cn/common-lisp-cn.html.markdown
index c4dc3274..b82829a9 100644
--- a/zh-cn/common-lisp-cn.html.markdown
+++ b/zh-cn/common-lisp-cn.html.markdown
@@ -17,7 +17,7 @@ ANSI Common Lisp 是一个广泛通用于各个工业领域的、支持多种范
另外还有一本热门的近期出版的
[Land of Lisp](http://landoflisp.com/).
-```scheme
+```common-lisp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; 0. 语法
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
diff --git a/zh-cn/go-cn.html.markdown b/zh-cn/go-cn.html.markdown
index 4a87dc21..9f6a8c15 100644
--- a/zh-cn/go-cn.html.markdown
+++ b/zh-cn/go-cn.html.markdown
@@ -68,7 +68,7 @@ func learnTypes() {
can include line breaks.` // 同样是String类型
// 非ascii字符。Go使用UTF-8编码。
- g := 'Σ' // rune类型,uint32的别名,使用UTF-8编码
+ g := 'Σ' // rune类型,int32的别名,使用UTF-8编码
f := 3.14195 // float64类型,IEEE-754 64位浮点数
c := 3 + 4i // complex128类型,内部使用两个float64表示
diff --git a/zh-cn/haskell-cn.html.markdown b/zh-cn/haskell-cn.html.markdown
index 8d51f144..8904970f 100644
--- a/zh-cn/haskell-cn.html.markdown
+++ b/zh-cn/haskell-cn.html.markdown
@@ -1,28 +1,29 @@
---
-language: haskell
+language: Haskell
filename: learn-haskell-zh.hs
contributors:
- ["Adit Bhargava", "http://adit.io"]
translators:
- ["Peiyong Lin", ""]
+ - ["chad luo", "http://yuki.rocks"]
lang: zh-cn
---
-Haskell 被设计成一种实用的纯函数式编程语言。它因为 monads 及其类型系统而出名,但是我回归到它本身因为。Haskell 使得编程对于我而言是一种真正的快乐。
+Haskell 是一门实用的函数式编程语言,因其 Monads 与类型系统而闻名。而我使用它则是因为它异常优雅。用 Haskell 编程令我感到非常快乐。
```haskell
--- 单行注释以两个破折号开头
+-- 单行注释以两个减号开头
{- 多行注释像这样
- 被一个闭合的块包围
+ 被一个闭合的块包围
-}
----------------------------------------------------
-- 1. 简单的数据类型和操作符
----------------------------------------------------
--- 你有数字
+-- 数字
3 -- 3
--- 数学计算就像你所期待的那样
+-- 数学计算
1 + 1 -- 2
8 - 1 -- 7
10 * 2 -- 20
@@ -34,7 +35,7 @@ Haskell 被设计成一种实用的纯函数式编程语言。它因为 monads
-- 整除
35 `div` 4 -- 8
--- 布尔值也简单
+-- 布尔值
True
False
@@ -45,21 +46,22 @@ not False -- True
1 /= 1 -- False
1 < 10 -- True
--- 在上述的例子中,`not` 是一个接受一个值的函数。
--- Haskell 不需要括号来调用函数。。。所有的参数
--- 都只是在函数名之后列出来。因此,通常的函数调用模式是:
--- func arg1 arg2 arg3...
--- 查看关于函数的章节以获得如何写你自己的函数的相关信息。
+-- 在上面的例子中,`not` 是一个接受一个参数的函数。
+-- Haskell 不需要括号来调用函数,所有的参数都只是在函数名之后列出来
+-- 因此,通常的函数调用模式是:
+-- func arg1 arg2 arg3...
+-- 你可以查看函数部分了解如何自行编写。
-- 字符串和字符
-"This is a string."
+"This is a string." -- 字符串
'a' -- 字符
'对于字符串你不能使用单引号。' -- 错误!
--- 连结字符串
+-- 连接字符串
"Hello " ++ "world!" -- "Hello world!"
-- 一个字符串是一系列字符
+['H', 'e', 'l', 'l', 'o'] -- "Hello"
"This is a string" !! 0 -- 'T'
@@ -67,162 +69,164 @@ not False -- True
-- 列表和元组
----------------------------------------------------
--- 一个列表中的每一个元素都必须是相同的类型
--- 下面两个列表一样
+-- 一个列表中的每一个元素都必须是相同的类型。
+-- 下面两个列表等价
[1, 2, 3, 4, 5]
[1..5]
--- 在 Haskell 你可以拥有含有无限元素的列表
-[1..] -- 一个含有所有自然数的列表
+-- 区间也可以这样
+['A'..'F'] -- "ABCDEF"
--- 因为 Haskell 有“懒惰计算”,所以无限元素的列表可以正常运作。这意味着
--- Haskell 可以只在它需要的时候计算。所以你可以请求
--- 列表中的第1000个元素,Haskell 会返回给你
+-- 你可以在区间中指定步进
+[0,2..10] -- [0, 2, 4, 6, 8, 10]
+[5..1] -- 这样不行,因为 Haskell 默认递增
+[5,4..1] -- [5, 4, 3, 2, 1]
-[1..] !! 999 -- 1000
+-- 列表下标
+[0..] !! 5 -- 5
--- Haskell 计算了列表中 1 - 1000 个元素。。。但是
--- 这个无限元素的列表中剩下的元素还不存在! Haskell 不会
--- 真正地计算它们知道它需要。
+-- 在 Haskell 你可以使用无限列表
+[1..] -- 一个含有所有自然数的列表
-<FS>- 连接两个列表
+-- 无限列表的原理是,Haskell 有“惰性求值”。
+-- 这意味着 Haskell 只在需要时才会计算。
+-- 所以当你获取列表的第 1000 项元素时,Haskell 会返回给你:
+[1..] !! 999 -- 1000
+-- Haskell 计算了列表中第 1 至 1000 项元素,但这个无限列表中剩下的元素还不存在。
+-- Haskell 只有在需要时才会计算它们。
+
+-- 连接两个列表
[1..5] ++ [6..10]
-- 往列表头增加元素
0:[1..5] -- [0, 1, 2, 3, 4, 5]
--- 列表中的下标
-[0..] !! 5 -- 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`,也就是
+-- 注意,如果你使用 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)
--- 模式匹配是类型的。这里有三种不同的 fib
--- 定义。Haskell 将自动调用第一个
--- 匹配值的模式的函数。
+-- 模式匹配与 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 函数:
+-- 列表的模式匹配
+-- 这里 `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), 并且使用列表中第一个值
--- 作为累加器的初始化值。
+-- 在 fold(在一些语言称 为`inject`)中使用匿名函数
+-- foldl1 意味着左折叠 (fold left), 并且使用列表中第一个值作为累加器的初始值。
foldl1 (\acc x -> acc + x) [1..5] -- 15
----------------------------------------------------
--- 4. 更多的函数
+-- 4. 其它函数
----------------------------------------------------
--- 柯里化(currying):如果你不传递函数中所有的参数,
--- 它就变成“柯里化的”。这意味着,它返回一个接受剩余参数的函数。
-
+-- 部分调用
+-- 如果你调用函数时没有给出所有参数,它就被“部分调用”。
+-- 它将返回一个接受余下参数的函数。
add a b = a + b
foo = add 10 -- foo 现在是一个接受一个数并对其加 10 的函数
foo 5 -- 15
--- 另外一种方式去做同样的事
+-- 另一种等价写法
foo = (+10)
foo 5 -- 15
--- 函数组合
--- (.) 函数把其它函数链接到一起
--- 举个列子,这里 foo 是一个接受一个值的函数。它对接受的值加 10,
--- 并对结果乘以 5,之后返回最后的值。
+-- 函列表合
+-- (.) 函数把其它函数链接到一起。
+-- 例如,这里 foo 是一个接受一个值的函数。
+-- 它对接受的值加 10,并对结果乘以 5,之后返回最后的值。
foo = (*5) . (+10)
-- (5 + 10) * 5 = 75
foo 5 -- 75
--- 修复优先级
--- Haskell 有另外一个函数称为 `$`。它改变优先级
--- 使得其左侧的每一个操作先计算然后应用到
--- 右侧的每一个操作。你可以使用 `.` 和 `$` 来除去很多
--- 括号:
+-- 修正优先级
+-- Haskell 有另外一个函数 `$` 可以改变优先级。
+-- `$` 使得 Haskell 先计算其右边的部分,然后调用左边的部分。
+-- 你可以使用 `$` 来移除多余的括号。
--- before
+-- 修改前
(even (fib 7)) -- true
--- after
+-- 修改后
even . fib $ 7 -- true
+-- 等价地
+even $ fib 7 -- true
+
----------------------------------------------------
--- 5. 类型签名
+-- 5. 类型声明
----------------------------------------------------
--- Haskell 有一个非常强壮的类型系统,一切都有一个类型签名。
+-- Haskell 有一个非常强大的类型系统,一切都有一个类型声明。
-- 一些基本的类型:
5 :: Integer
"hello" :: String
True :: Bool
--- 函数也有类型。
--- `not` 接受一个布尔型返回一个布尔型:
+-- 函数也有类型
+-- `not` 接受一个布尔型返回一个布尔型
-- not :: Bool -> Bool
--- 这是接受两个参数的函数:
+-- 这是接受两个参数的函数
-- add :: Integer -> Integer -> Integer
--- 当你定义一个值,在其上写明它的类型是一个好实践:
+-- 当你定义一个值,声明其类型是一个好做法
double :: Integer -> Integer
double x = x * 2
@@ -230,159 +234,148 @@ double x = x * 2
-- 6. 控制流和 If 语句
----------------------------------------------------
--- if 语句
+-- if 语句:
haskell = if 1 == 1 then "awesome" else "awful" -- haskell = "awesome"
--- if 语句也可以有多行,缩进是很重要的
+-- if 语句也可以有多行,注意缩进:
haskell = if 1 == 1
then "awesome"
else "awful"
--- case 语句:这里是你可以怎样去解析命令行参数
+-- case 语句
+-- 解析命令行参数:
case args of
"help" -> printHelp
"start" -> startProgram
_ -> putStrLn "bad args"
--- Haskell 没有循环因为它使用递归取代之。
--- map 应用一个函数到一个数组中的每一个元素
-
+-- 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 是右手边的-
+-- foldl 从左开始,foldr 从右
foldr (\x y -> 2*x + y) 4 [1,2,3] -- 16
--- 这和下面是一样的
+-- 现在它等价于
(2 * 3 + (2 * 2 + (2 * 1 + 4)))
----------------------------------------------------
-- 7. 数据类型
----------------------------------------------------
--- 这里展示在 Haskell 中你怎样编写自己的数据类型
-
+-- 在 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" -- of type `Maybe String`
-Just 1 -- of type `Maybe Int`
-Nothing -- of type `Maybe a` for any `a`
+-- 这些都是 Maybe 类型:
+Just "hello" -- `Maybe String` 类型
+Just 1 -- `Maybe Int` 类型
+Nothing -- 对任意 `a` 为 `Maybe a` 类型
----------------------------------------------------
-- 8. Haskell IO
----------------------------------------------------
--- 虽然在没有解释 monads 的情况下 IO不能被完全地解释,
--- 着手解释到位并不难。
-
--- 当一个 Haskell 程序被执行,函数 `main` 就被调用。
--- 它必须返回一个类型 `IO ()` 的值。举个列子:
+-- 虽然不解释 Monads 就无法完全解释 IO,但大致了解并不难。
+-- 当执行一个 Haskell 程序时,函数 `main` 就被调用。
+-- 它必须返回一个类型 `IO ()` 的值。例如:
main :: IO ()
main = putStrLn $ "Hello, sky! " ++ (say Blue)
--- putStrLn has type String -> IO ()
+-- putStrLn 的类型是 String -> IO ()
--- 如果你能实现你的程序依照函数从 String 到 String,那样编写 IO 是最简单的。
+-- 如果你的程序输入 String 返回 String,那样编写 IO 是最简单的。
-- 函数
-- interact :: (String -> String) -> IO ()
--- 输入一些文本,在其上运行一个函数,并打印出输出
+-- 输入一些文本,对其调用一个函数,并打印输出。
countLines :: String -> String
countLines = show . length . lines
main' = interact countLines
--- 你可以考虑一个 `IO()` 类型的值,当做一系列计算机所完成的动作的代表,
--- 就像一个以命令式语言编写的计算机程序。我们可以使用 `do` 符号来把动作链接到一起。
--- 举个列子:
-
+-- 你可以认为一个 `IO ()` 类型的值是表示计算机做的一系列操作,类似命令式语言。
+-- 我们可以使用 `do` 声明来把动作连接到一起。
+-- 举个列子
sayHello :: IO ()
sayHello = do
putStrLn "What is your name?"
- name <- getLine -- this gets a line and gives it the name "input"
+ name <- getLine -- 这里接受一行输入并绑定至 "name"
putStrLn $ "Hello, " ++ name
-- 练习:编写只读取一行输入的 `interact`
-- 然而,`sayHello` 中的代码将不会被执行。唯一被执行的动作是 `main` 的值。
--- 为了运行 `sayHello`,注释上面 `main` 的定义,并代替它:
+-- 为了运行 `sayHello`,注释上面 `main` 的定义,替换为:
-- main = sayHello
--- 让我们来更好地理解刚才所使用的函数 `getLine` 是怎样工作的。它的类型是:
+-- 让我们来更进一步理解刚才所使用的函数 `getLine` 是怎样工作的。它的类型是:
-- getLine :: IO String
--- 你可以考虑一个 `IO a` 类型的值,代表一个当被执行的时候
--- 将产生一个 `a` 类型的值的计算机程序(除了它所做的任何事之外)。我们可以保存和重用这个值通过 `<-`。
--- 我们也可以写自己的 `IO String` 类型的动作:
-
+-- 你可以认为一个 `IO a` 类型的值代表了一个运行时会生成一个 `a` 类型值的程序。
+-- (可能伴随其它行为)
+-- 我们可以通过 `<-` 保存和重用这个值。
+-- 我们也可以实现自己的 `IO String` 类型函数:
action :: IO String
action = do
putStrLn "This is a line. Duh"
input1 <- getLine
input2 <- getLine
- -- The type of the `do` statement is that of its last line.
- -- `return` is not a keyword, but merely a function
+ -- `do` 语句的类型是它的最后一行
+ -- `return` 不是关键字,只是一个普通函数
return (input1 ++ "\n" ++ input2) -- return :: String -> IO String
--- 我们可以使用这个动作就像我们使用 `getLine`:
-
+-- 我们可以像调用 `getLine` 一样调用它
main'' = do
putStrLn "I will echo two lines!"
result <- action
putStrLn result
putStrLn "This was all, folks!"
--- `IO` 类型是一个 "monad" 的例子。Haskell 使用一个 monad 来做 IO的方式允许它是一门纯函数式语言。
--- 任何与外界交互的函数(也就是 IO) 都在它的类型签名处做一个 `IO` 标志
--- 着让我们推出 什么样的函数是“纯洁的”(不与外界交互,不修改状态) 和 什么样的函数不是 “纯洁的”
-
--- 这是一个强有力的特征,因为并发地运行纯函数是简单的;因此,Haskell 中并发是非常简单的。
-
+-- `IO` 类型是一个 "Monad" 的例子。
+-- Haskell 通过使用 Monad 使得其本身为纯函数式语言。
+-- 任何与外界交互的函数(即 IO)都在它的类型声明中标记为 `IO`。
+-- 这告诉我们什么样的函数是“纯洁的”(不与外界交互,不修改状态) ,
+-- 什么样的函数不是 “纯洁的”。
+-- 这个功能非常强大,因为纯函数并发非常容易,由此在 Haskell 中做并发非常容易。
----------------------------------------------------
--- 9. The Haskell REPL
+-- 9. Haskell REPL
----------------------------------------------------
--- 键入 `ghci` 开始 repl。
+-- 键入 `ghci` 开始 REPL。
-- 现在你可以键入 Haskell 代码。
--- 任何新值都需要通过 `let` 来创建:
-
+-- 任何新值都需要通过 `let` 来创建
let foo = 5
--- 你可以查看任何值的类型,通过命令 `:t`:
-
+-- 你可以通过命令 `:t` 查看任何值的类型
>:t foo
foo :: Integer
-- 你也可以运行任何 `IO ()`类型的动作
-
> sayHello
What is your name?
Friend!
@@ -390,7 +383,7 @@ Hello, Friend!
```
-还有很多关于 Haskell,包括类型类和 monads。这些是使得编码 Haskell 是如此有趣的主意。我用一个最后的 Haskell 例子来结束:一个 Haskell 的快排实现:
+Haskell 还有许多内容,包括类型类 (typeclasses) 与 Monads。这些都是令 Haskell 编程非常有趣的好东西。我们最后给出 Haskell 的一个例子,一个快速排序的实现:
```haskell
qsort [] = []
@@ -399,9 +392,9 @@ qsort (p:xs) = qsort lesser ++ [p] ++ qsort greater
greater = filter (>= p) xs
```
-安装 Haskell 是简单的。你可以从[这里](http://www.haskell.org/platform/)获得它。
+安装 Haskell 很简单。你可以[从这里获得](http://www.haskell.org/platform/)。
你可以从优秀的
[Learn you a Haskell](http://learnyouahaskell.com/) 或者
[Real World Haskell](http://book.realworldhaskell.org/)
-找到优雅不少的入门介绍。
+找到更平缓的入门介绍。
diff --git a/zh-cn/java-cn.html.markdown b/zh-cn/java-cn.html.markdown
index f7d319e6..f08d3507 100644
--- a/zh-cn/java-cn.html.markdown
+++ b/zh-cn/java-cn.html.markdown
@@ -124,7 +124,7 @@ public class LearnJava {
// HashMaps
///////////////////////////////////////
- // 操作符
+ // 操作符
///////////////////////////////////////
System.out.println("\n->Operators");
@@ -161,10 +161,13 @@ public class LearnJava {
// 自增
int i = 0;
System.out.println("\n->Inc/Dec-rementation");
- System.out.println(i++); //i = 1 后自增
- System.out.println(++i); //i = 2 前自增
- System.out.println(i--); //i = 1 后自减
- System.out.println(--i); //i = 0 前自减
+ // ++ 和 -- 操作符使变量加或减1。放在变量前面或者后面的区别是整个表达
+ // 式的返回值。操作符在前面时,先加减,后取值。操作符在后面时,先取值
+ // 后加减。
+ System.out.println(i++); // 后自增 i = 1, 输出0
+ System.out.println(++i); // 前自增 i = 2, 输出2
+ System.out.println(i--); // 后自减 i = 1, 输出2
+ System.out.println(--i); // 前自减 i = 0, 输出0
///////////////////////////////////////
// 控制结构
@@ -192,7 +195,7 @@ public class LearnJava {
}
System.out.println("fooWhile Value: " + fooWhile);
- // Do While循环
+ // Do While循环
int fooDoWhile = 0;
do
{
diff --git a/zh-cn/javascript-cn.html.markdown b/zh-cn/javascript-cn.html.markdown
index 7dee9cc4..b450ab84 100644
--- a/zh-cn/javascript-cn.html.markdown
+++ b/zh-cn/javascript-cn.html.markdown
@@ -5,17 +5,19 @@ name: javascript
filename: javascript-zh.js
contributors:
- ["Adam Brenecki", "http://adam.brenecki.id.au"]
+ - ["Ariel Krakowski", "http://www.learneroo.com"]
translators:
- ["Chenbo Li", "http://binarythink.net"]
+ - ["Guodong Qu", "https://github.com/jasonqu"]
lang: zh-cn
---
Javascript于1995年由网景公司的Brendan Eich发明。
最初发明的目的是作为一个简单的网站脚本语言,来作为
-复杂网站应用java的补充。但由于javascript和网站结合度很高
-所以javascript逐渐变得比java在前端更为流行了。
+复杂网站应用java的补充。但由于它与网页结合度很高并且由浏览器内置支持,
+所以javascript变得比java在前端更为流行了。
-JavaScript 不仅仅只可以用于浏览器, 也可用于 Node.js 等后台环境。
+不过 JavaScript 可不仅仅只用于浏览器: Node.js,一个基于Google Chrome V8引擎的独立运行时环境,也越来越流行。
很欢迎来自您的反馈,您可以通过下列方式联系到我:
[@adambrenecki](https://twitter.com/adambrenecki), 或者
@@ -29,145 +31,167 @@ JavaScript 不仅仅只可以用于浏览器, 也可用于 Node.js 等后台环
// 语句可以以分号结束
doStuff();
-// ... 但是分号也可以省略,每当遇到一个新行时,分号会自动插入
+// ... 但是分号也可以省略,每当遇到一个新行时,分号会自动插入(除了一些特殊情况)。
doStuff()
-// 我们在这里会去掉分号,但是否添加最后的分号取决于你个人的习惯
-// 及你所在团队的编程风格
+// 因为这些特殊情况会导致意外的结果,所以我们在这里保留分号。
///////////////////////////////////
// 1. 数字、字符串与操作符
-// Javascript 只有一种数字类型 (即 64位 IEEE 754 双精度浮点).
-3 // = 3
-1.5 // = 1.5
+// Javascript 只有一种数字类型(即 64位 IEEE 754 双精度浮点 double)。
+// double 有 52 位表示尾数,足以精确存储大到 9✕10¹⁵ 的整数。
+3; // = 3
+1.5; // = 1.5
-// 所有基本的算数运算
-1 + 1 // = 2
-8 - 1 // = 7
-10 * 2 // = 20
-35 / 5 // = 7
+// 所有基本的算数运算都如你预期。
+1 + 1; // = 2
+0.1 + 0.2; // = 0.30000000000000004
+8 - 1; // = 7
+10 * 2; // = 20
+35 / 5; // = 7
-// 包括无法整除的除法
-5 / 2 // = 2.5
+// 包括无法整除的除法。
+5 / 2; // = 2.5
-// 位运算也和其他语言一样。当你对浮点数进行位运算时,
-// 浮点数会转换为至多 32 位的无符号整数
-1 << 2 // = 4
+// 位运算也和其他语言一样;当你对浮点数进行位运算时,
+// 浮点数会转换为*至多* 32 位的无符号整数。
+1 << 2; // = 4
-// 括号可以决定优先级
-(1 + 3) * 2 // = 8
+// 括号可以决定优先级。
+(1 + 3) * 2; // = 8
// 有三种非数字的数字类型
-Infinity // 1/0 的结果
--Infinity // -1/0 的结果
-NaN // 0/0 的结果
+Infinity; // 1/0 的结果
+-Infinity; // -1/0 的结果
+NaN; // 0/0 的结果
-// 也有布尔值
-true
-false
+// 也有布尔值。
+true;
+false;
-// 可以通过单引号或双引号来构造字符串
-'abc'
-"Hello, world"
+// 可以通过单引号或双引号来构造字符串。
+'abc';
+"Hello, world";
// 用!来取非
-!true // = false
-!false // = true
+!true; // = false
+!false; // = true
-// 相等 ==
-1 == 1 // = true
-2 == 1 // = false
+// 相等 ===
+1 === 1; // = true
+2 === 1; // = false
// 不等 !=
-1 != 1 // = false
-2 != 1 // = true
+1 !== 1; // = false
+2 !== 1; // = true
// 更多的比较操作符
-1 < 10 // = true
-1 > 10 // = false
-2 <= 2 // = true
-2 >= 2 // = true
+1 < 10; // = true
+1 > 10; // = false
+2 <= 2; // = true
+2 >= 2; // = true
// 字符串用+连接
-"Hello " + "world!" // = "Hello world!"
+"Hello " + "world!"; // = "Hello world!"
// 字符串也可以用 < 、> 来比较
-"a" < "b" // = true
+"a" < "b"; // = true
-// 比较时会进行类型转换...
-"5" == 5 // = true
+// 使用“==”比较时会进行类型转换...
+"5" == 5; // = true
+null == undefined; // = true
// ...除非你是用 ===
-"5" === 5 // = false
+"5" === 5; // = false
+null === undefined; // = false
-// 你可以用charAt来得到字符串中的字符
-"This is a string".charAt(0)
+// ...但会导致奇怪的行为
+13 + !0; // 14
+"13" + !0; // '13true'
-// 还有两个特殊的值:null和undefined
-null // 用来表示刻意设置成的空值
-undefined // 用来表示还没有设置的值
+// 你可以用`charAt`来得到字符串中的字符
+"This is a string".charAt(0); // = 'T'
-// null, undefined, NaN, 0 和 "" 都是假的(false),其他的都视作逻辑真
-// 注意 0 是逻辑假而 "0"是逻辑真, 尽管 0 == "0".
+// ...或使用 `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`关键字声明。Javascript是动态类型语言,
+// 所以你无需指定类型。 赋值需要用 `=`
+var someVar = 5;
-// 如果你在声明时没有加var关键字,你也不会得到错误
-someOtherVar = 10
+// 如果你在声明时没有加var关键字,你也不会得到错误...
+someOtherVar = 10;
-// ...但是此时这个变量就会拥有全局的作用域,而非当前作用域
+// ...但是此时这个变量就会在全局作用域被创建,而非你定义的当前作用域
-// 没有被赋值的变量都会返回undefined这个值
-var someThirdVar // = undefined
+// 没有被赋值的变量都会被设置为undefined
+var someThirdVar; // = undefined
-// 对变量进行数学运算有一些简写法
-someVar += 5 // 等价于 someVar = someVar + 5; someVar 现在是 10
-someVar *= 10 // 现在 someVar 是 100
+// 对变量进行数学运算有一些简写法:
+someVar += 5; // 等价于 someVar = someVar + 5; someVar 现在是 10
+someVar *= 10; // 现在 someVar 是 100
// 自增和自减也有简写
-someVar++ // someVar 是 101
-someVar-- // 回到 100
+someVar++; // someVar 是 101
+someVar--; // 回到 100
// 数组是任意类型组成的有序列表
-var myArray = ["Hello", 45, true]
+var myArray = ["Hello", 45, true];
+
+// 数组的元素可以用方括号下标来访问。
+// 数组的索引从0开始。
+myArray[1]; // = 45
-// 数组的元素可以用方括号下标来访问
-// 数组的索引从0开始
-myArray[1] // = 45
+// 数组是可变的,并拥有变量 length。
+myArray.push("World");
+myArray.length; // = 4
-// javascript中的对象相当于其他语言中的字典或映射:是键-值的集合
-{key1: "Hello", key2: "World"}
+// 在指定下标添加/修改
+myArray[3] = "Hello";
-// 键是字符串,但是引号也并非是必须的,如果键本身是合法的js标识符
-// 而值则可以是任意类型的值
-var myObj = {myKey: "myValue", "my other key": 4}
+// javascript中的对象相当于其他语言中的“字典”或“映射”:是键-值对的无序集合。
+var myObj = {key1: "Hello", key2: "World"};
-// 对象的访问可以通过下标
-myObj["my other key"] // = 4
+// 键是字符串,但如果键本身是合法的js标识符,则引号并非是必须的。
+// 值可以是任意类型。
+var myObj = {myKey: "myValue", "my other key": 4};
+
+// 对象属性的访问可以通过下标
+myObj["my other key"]; // = 4
// ... 或者也可以用 . ,如果属性是合法的标识符
-myObj.myKey // = "myValue"
+myObj.myKey; // = "myValue"
-// 对象是可变的,键和值也可以被更改或增加
-myObj.myThirdKey = true
+// 对象是可变的;值也可以被更改或增加新的键
+myObj.myThirdKey = true;
-// 如果你想要访问一个还没有被定义的属性,那么会返回undefined
-myObj.myFourthKey // = undefined
+// 如果你想要获取一个还没有被定义的值,那么会返回undefined
+myObj.myFourthKey; // = undefined
///////////////////////////////////
// 3. 逻辑与控制结构
-// if语句和其他语言中一样
-var count = 1
+// 本节介绍的语法与Java的语法几乎完全相同
+
+// `if`语句和其他语言中一样。
+var count = 1;
if (count == 3){
// count 是 3 时执行
-} else if (count == 4) {
+} else if (count == 4){
// count 是 4 时执行
} else {
// 其他情况下执行
@@ -179,219 +203,273 @@ while (true) {
}
// Do-while 和 While 循环很像 ,但前者会至少执行一次
-var input
+var input;
do {
- input = getInput()
+ input = getInput();
} while (!isValid(input))
-// for循环和C、Java中的一样
-// 初始化; 继续执行的条件; 遍历后执行.
+// `for`循环和C、Java中的一样:
+// 初始化; 继续执行的条件; 迭代。
for (var i = 0; i < 5; i++){
// 遍历5次
}
// && 是逻辑与, || 是逻辑或
if (house.size == "big" && house.colour == "blue"){
- house.contains = "bear"
+ house.contains = "bear";
}
if (colour == "red" || colour == "blue"){
// colour是red或者blue时执行
}
-// && 和 || 是“短路”语句,在初始化值时会变得有用
-var name = otherName || "default"
+// && 和 || 是“短路”语句,它在设定初始化值时特别有用
+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关键字定义
+// JavaScript 函数由`function`关键字定义
function myFunction(thing){
- return thing.toUpperCase()
+ return thing.toUpperCase();
}
-myFunction("foo") // = "FOO"
-
-// 函数也可以是匿名的:
-function(thing){
- return thing.toLowerCase()
+myFunction("foo"); // = "FOO"
+
+// 注意被返回的值必须开始于`return`关键字的那一行,
+// 否则由于自动的分号补齐,你将返回`undefined`。
+// 在使用Allman风格的时候要注意.
+function myFunction()
+{
+ return // <- 分号自动插在这里
+ {
+ thisIsAn: 'object literal'
+ }
}
-// (我们无法调用此函数,因为我们不知道这个函数的名字)
+myFunction(); // = undefined
-// javascript中的函数也是对象,所以函数也能够赋给一个变量,并且被传递
-// 比如一个事件处理函数:
+// javascript中函数是一等对象,所以函数也能够赋给一个变量,
+// 并且被作为参数传递 —— 比如一个事件处理函数:
function myFunction(){
- // this code will be called in 5 seconds' time
+ // 这段代码将在5秒钟后被调用
}
-setTimeout(myFunction, 5000)
-
-// 你甚至可以直接把一个函数写到另一个函数的参数中
+setTimeout(myFunction, 5000);
+// 注意:setTimeout不是js语言的一部分,而是由浏览器和Node.js提供的。
-setTimeout(function myFunction(){
- // 5秒之后会执行这里的代码
-}, 5000)
+// 函数对象甚至不需要声明名称 —— 你可以直接把一个函数定义写到另一个函数的参数中
+setTimeout(function(){
+ // 这段代码将在5秒钟后被调用
+}, 5000);
-// JavaScript 仅有函数作用于,而其他的语句则没有作用域
+// JavaScript 有函数作用域;函数有其自己的作用域而其他的代码块则没有。
if (true){
- var i = 5
+ var i = 5;
}
-i // = 5 - 并非我们在其他语言中所得到的undefined
-
-// 这就导致了人们经常用一种叫做“即使执行匿名函数”的模式
-// 这样可以避免一些临时变量扩散到外边去
-function(){
- var temporary = 5
- // 我们可以访问一个全局对象来访问全局作用域
- // 在浏览器中是 'window' 这个对象。
- // 在Node.js中这个对象的名字可能会不同。
- window.permanent = 10
- // 或者,我们也可以把var去掉就行了
- permanent2 = 15
-}()
-temporary // 抛出引用异常
-permanent // = 10
-permanent2 // = 15
-
-// javascript最强大的功能之一就是闭包
-// 如果一个函数在另一个函数中定义,那么这个函数就拥有外部函数的所有访问权
+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 prompt = "Hello, " + name + "!";
+ // 内部函数默认是放在局部作用域的,
+ // 就像是用`var`声明的。
function inner(){
- alert(prompt)
+ alert(prompt);
}
- setTimeout(inner, 5000)
- // setTimeout 是异步的,所以这个函数会马上终止不会等待。
- // 然而,在5秒结束后,inner函数仍然会弹出prompt信息。
+ setTimeout(inner, 5000);
+ // setTimeout是异步的,所以 sayHelloInFiveSeconds 函数会立即退出,
+ // 而 setTimeout 会在后面调用inner
+ // 然而,由于inner是由sayHelloInFiveSeconds“闭合包含”的,
+ // 所以inner在其最终被调用时仍然能够访问`prompt`变量。
}
-sayHelloInFiveSeconds("Adam") // 会在5秒后弹出 "Hello, Adam!"
+sayHelloInFiveSeconds("Adam"); // 会在5秒后弹出 "Hello, Adam!"
+
///////////////////////////////////
// 5. 对象、构造函数与原型
-// 对象包含方法
+// 对象可以包含方法。
var myObj = {
myFunc: function(){
- return "Hello world!"
+ return "Hello world!";
}
-}
-myObj.myFunc() // = "Hello world!"
+};
+myObj.myFunc(); // = "Hello world!"
-// 当对象中的函数被调用时,这个函数就可以通过this关键字访问这个对象
+// 当对象中的函数被调用时,这个函数可以通过`this`关键字访问其依附的这个对象。
myObj = {
myString: "Hello world!",
myFunc: function(){
- return this.myString
+ return this.myString;
}
-}
-myObj.myFunc() // = "Hello world!"
+};
+myObj.myFunc(); // = "Hello world!"
-// 但这个函数访问的其实是其运行时环境,而非定义时环境
-// 所以如果函数所在的环境不在当前对象的环境中运行时,就运行不成功了
-var myFunc = myObj.myFunc
-myFunc() // = undefined
+// 但这个函数访问的其实是其运行时环境,而非定义时环境,即取决于函数是如何调用的。
+// 所以如果函数被调用时不在这个对象的上下文中,就不会运行成功了。
+var myFunc = myObj.myFunc;
+myFunc(); // = undefined
-// 相应的,一个函数也可以被指定为一个对象的方法,并且用过this可以访问
-// 这个对象的成员,即使在定义时并没有绑定任何值
+// 相应的,一个函数也可以被指定为一个对象的方法,并且可以通过`this`访问
+// 这个对象的成员,即使在函数被定义时并没有依附在对象上。
var myOtherFunc = function(){
- return this.myString.toUpperCase()
+ return this.myString.toUpperCase();
+}
+myObj.myOtherFunc = myOtherFunc;
+myObj.myOtherFunc(); // = "HELLO WORLD!"
+
+// 当我们通过`call`或者`apply`调用函数的时候,也可以为其指定一个执行上下文。
+var anotherFunc = function(s){
+ return this.myString + s;
}
-myObj.myOtherFunc = myOtherFunc
-myObj.myOtherFunc() // = "HELLO WORLD!"
+anotherFunc.call(myObj, " And Hello Moon!"); // = "Hello World! And Hello Moon!"
-// 当你通过new关键字调用一个函数时,就会生成一个对象
-// 而对象的成员需要通过this来定义。
-// 这样的函数就叫做构造函数
+// `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
+ this.myNumber = 5;
}
-myNewObj = new MyConstructor() // = {myNumber: 5}
-myNewObj.myNumber // = 5
+myNewObj = new MyConstructor(); // = {myNumber: 5}
+myNewObj.myNumber; // = 5
-// 每一个js对象都有一个原型,当你要访问一个没有定义过的成员时,
-// 解释器就回去找这个对象的原型
+// 每一个js对象都有一个‘原型’。当你要访问一个实际对象中没有定义的一个属性时,
+// 解释器就回去找这个对象的原型。
-// 有一些JS实现会让你通过一个对象的__proto__方法访问这个原型。
-// 这虽然对理解这个对象很有用,但是这并不是标准的一部分
-// 我们之后会通过标准方式来访问原型。
+// 一些JS实现会让你通过`__proto__`属性访问一个对象的原型。
+// 这虽然对理解原型很有用,但是它并不是标准的一部分;
+// 我们后面会介绍使用原型的标准方式。
var myObj = {
- myString: "Hello world!",
-}
+ myString: "Hello world!"
+};
var myPrototype = {
meaningOfLife: 42,
myFunc: function(){
return this.myString.toLowerCase()
}
-}
-myObj.__proto__ = myPrototype
-myObj.meaningOfLife // = 42
+};
-// This works for functions, too.
+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 = {
+};
+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
+ return this.myNumber;
}
-}
-var myNewObj2 = new myConstructor()
-myNewObj2.getMyNumber() // = 5
+};
+var myNewObj2 = new MyConstructor();
+myNewObj2.getMyNumber(); // = 5
+myNewObj2.myNumber = 6
+myNewObj2.getMyNumber(); // = 6
// 字符串和数字等内置类型也有通过构造函数来创建的包装类型
-var myNumber = 12
-var myNumberObj = new Number(12)
-myNumber == myNumberObj // = true
+var myNumber = 12;
+var myNumberObj = new Number(12);
+myNumber == myNumberObj; // = true
// 但是它们并非严格等价
-typeof myNumber // = 'number'
-typeof myNumberObj // = 'object'
-myNumber === myNumberObj // = false
+typeof myNumber; // = 'number'
+typeof myNumberObj; // = 'object'
+myNumber === myNumberObj; // = false
if (0){
// 这段代码不会执行,因为0代表假
}
if (Number(0)){
- // 这段代码会执行,因为Number(0)代表真
+ // 这段代码*会*执行,因为Number(0)代表真
}
-// 但是,包装类型和内置类型共享一个原型
-// 这样你就可以给内置类型也增加一些功能
+// 不过,包装类型和内置类型共享一个原型,
+// 所以你实际可以给内置类型也增加一些功能,例如对string:
String.prototype.firstCharacter = function(){
- return this.charAt(0)
+ return this.charAt(0);
}
-"abc".firstCharacter() // = "a"
+"abc".firstCharacter(); // = "a"
-// 这个技巧可以用来用老版本的javascript子集来是实现新版本js的功能
+// 这个技巧经常用在“代码填充”中,来为老版本的javascript子集增加新版本js的特性,
// 这样就可以在老的浏览器中使用新功能了。
-// 比如,我们知道Object.create并没有在所有的版本中都实现
-// 但是我们仍然可以通过这个技巧来使用
+// 比如,我们知道Object.create并没有在所有的版本中都实现,
+// 但是我们仍然可以通过“代码填充”来实现兼容:
if (Object.create === undefined){ // 如果存在则不覆盖
Object.create = function(proto){
// 用正确的原型来创建一个临时构造函数
- var Constructor = function(){}
- Constructor.prototype = proto
+ var Constructor = function(){};
+ Constructor.prototype = proto;
// 之后用它来创建一个新的对象
- return new Constructor()
+ return new Constructor();
}
}
```
@@ -399,19 +477,23 @@ if (Object.create === undefined){ // 如果存在则不覆盖
## 更多阅读
[Mozilla 开发者
-网络](https://developer.mozilla.org/en-US/docs/Web/JavaScript) 提供了很好的
-Javascript文档,并且由于是wiki,所以你也可以自行编辑来分享你的知识。
+网络](https://developer.mozilla.org/en-US/docs/Web/JavaScript) 提供了优秀的介绍
+Javascript如何在浏览器中使用的文档。而且它是wiki,所以你也可以自行编辑来分享你的知识。
MDN的 [A re-introduction to
JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/A_re-introduction_to_JavaScript)
-覆盖了这里提到的绝大多数话题,大多数只是Javascript这个语言本身。
+覆盖了这里提到的绝大多数话题的细节。该导引的大多数内容被限定在只是Javascript这个语言本身;
如果你想了解Javascript是如何在网页中被应用的,那么可以查看
[Document Object
Model](https://developer.mozilla.org/en-US/docs/Using_the_W3C_DOM_Level_1_Core)
+[Learn Javascript by Example and with Challenges](http://www.learneroo.com/modules/64/nodes/350) 是本参考的另一个版本,并包含了挑战习题。
+
[Javascript Garden](http://bonsaiden.github.io/JavaScript-Garden/) 是一个深入
-讲解所有Javascript反直觉部分的一本书
+讲解所有Javascript反直觉部分的导引。
+
+[JavaScript: The Definitive Guide](http://www.amazon.com/gp/product/0596805527/) 是一个经典的指导参考书。
除了这篇文章的直接贡献者之外,这篇文章也参考了这个网站上
Louie Dinh 的 Python 教程,以及 Mozilla开发者网络上的[JS
-Tutorial](https://developer.mozilla.org/en-US/docs/Web/JavaScript/A_re-introduction_to_JavaScript)
+Tutorial](https://developer.mozilla.org/en-US/docs/Web/JavaScript/A_re-introduction_to_JavaScript)。
diff --git a/zh-cn/julia-cn.html.markdown b/zh-cn/julia-cn.html.markdown
index 7afc9043..1f91d52c 100644
--- a/zh-cn/julia-cn.html.markdown
+++ b/zh-cn/julia-cn.html.markdown
@@ -1,5 +1,5 @@
---
-language: julia
+language: Julia
filename: learn-julia-zh.jl
contributors:
- ["Jichao Ouyang", "http://oyanglul.us"]
diff --git a/zh-cn/livescript-cn.html.markdown b/zh-cn/livescript-cn.html.markdown
new file mode 100644
index 00000000..fea00bc1
--- /dev/null
+++ b/zh-cn/livescript-cn.html.markdown
@@ -0,0 +1,322 @@
+---
+language: LiveScript
+filename: learnLivescript.ls
+contributors:
+ - ["Christina Whyte", "http://github.com/kurisuwhyte/"]
+translators:
+ - ["ShengDa Lyu", "http://github.com/SDLyu/"]
+lang: zh-cn
+---
+
+LiveScript 是一种具有函数式特性且编译成 JavaScript 的语言,能对应 JavaScript 的基本语法。
+还有些额外的特性如:柯里化,组合函数,模式匹配,还有借镜于 Haskell,F# 和 Scala 的许多特点。
+
+LiveScript 诞生于 [Coco][],而 Coco 诞生于 [CoffeeScript][]。
+LiveScript 目前已释出稳定版本,开发中的新版本将会加入更多特性。
+
+[Coco]: http://satyr.github.io/coco/
+[CoffeeScript]: http://coffeescript.org/
+
+非常期待您的反馈,你可以通过
+[@kurisuwhyte](https://twitter.com/kurisuwhyte) 与我连系 :)
+
+
+```coffeescript
+# 与 CoffeeScript 一样,LiveScript 使用 # 单行注解。
+
+/*
+ 多行注解与 C 相同。使用注解可以避免被当成 JavaScript 输出。
+*/
+```
+```coffeescript
+# 语法的部份,LiveScript 使用缩进取代 {} 来定义区块,
+# 使用空白取代 () 来执行函数。
+
+
+########################################################################
+## 1. 值类型
+########################################################################
+
+# `void` 取代 `undefined` 表示未定义的值
+void # 与 `undefined` 等价但更安全(不会被覆写)
+
+# 空值则表示成 Null。
+null
+
+
+# 最基本的值类型数据是罗辑类型:
+true
+false
+
+# 罗辑类型的一些别名,等价于前者:
+on; off
+yes; no
+
+
+# 数字与 JS 一样,使用倍精度浮点数表示。
+10
+0.4 # 开头的 0 是必要的
+
+
+# 可以使用底线及单位后缀提高可读性,编译器会自动略过底线及单位后缀。
+12_344km
+
+
+# 字串与 JS 一样,是一种不可变的字元序列:
+"Christina" # 单引号也可以!
+"""Multi-line
+ strings
+ are
+ okay
+ too."""
+
+# 在前面加上 \ 符号也可以表示字串:
+\keyword # => 'keyword'
+
+
+# 数组是值的有序集合。
+fruits =
+ * \apple
+ * \orange
+ * \pear
+
+# 可以用 [] 简洁地表示数组:
+fruits = [ \apple, \orange, \pear ]
+
+
+# 你可以更方便地建立字串数组,并使用空白区隔元素。
+fruits = <[ apple orange pear ]>
+
+# 以 0 为起始值的数组下标获取元素:
+fruits[0] # => "apple"
+
+
+# 对象是无序键值对集合(更多给节将在下面章节讨论)。
+person =
+ name: "Christina"
+ likes:
+ * "kittens"
+ * "and other cute stuff"
+
+# 你也可以用更简洁的方式表示对象:
+person = {name: "Christina", likes: ["kittens", "and other cute stuff"]}
+
+# 可以通过键值获取值:
+person.name # => "Christina"
+person["name"] # => "Christina"
+
+
+# 正则表达式的使用跟 JavaScript 一样:
+trailing-space = /\s$/ # dashed-words 变成 dashedWords
+
+# 你也可以用多行描述表达式!(注解和空白会被忽略)
+funRE = //
+ function\s+(.+) # name
+ \s* \((.*)\) \s* # arguments
+ { (.*) } # body
+ //
+
+
+########################################################################
+## 2. 基本运算
+########################################################################
+
+# 数值操作符与 JavaScript 一样:
+1 + 2 # => 3
+2 - 1 # => 1
+2 * 3 # => 6
+4 / 2 # => 2
+3 % 2 # => 1
+
+
+# 比较操作符大部份也一样,除了 `==` 等价于 JS 中的 `===`,
+# JS 中的 `==` 在 LiveScript 里等价于 `~=`,
+# `===` 能进行对象、数组和严格比较。
+2 == 2 # => true
+2 == "2" # => false
+2 ~= "2" # => true
+2 === "2" # => false
+
+[1,2,3] == [1,2,3] # => false
+[1,2,3] === [1,2,3] # => true
+
++0 == -0 # => true
++0 === -0 # => false
+
+# 其它关系操作符包括 <、<=、> 和 >=
+
+# 罗辑值可以通过 `or`、`and` 和 `not` 结合:
+true and false # => false
+false or true # => true
+not false # => true
+
+
+# 集合也有一些便利的操作符
+[1, 2] ++ [3, 4] # => [1, 2, 3, 4]
+'a' in <[ a b c ]> # => true
+'name' of { name: 'Chris' } # => true
+
+
+########################################################################
+## 3. 函数
+########################################################################
+
+# 因为 LiveScript 是函数式特性的语言,你可以期待函数在语言里被高规格的对待。
+add = (left, right) -> left + right
+add 1, 2 # => 3
+
+# 加上 ! 防止函数执行后的返回值
+two = -> 2
+two!
+
+# LiveScript 与 JavaScript 一样使用函式作用域,且一样拥有闭包的特性。
+# 与 JavaScript 不同的地方在于,`=` 变量赋值时,左边的对象永远不用变量宣告。
+
+# `:=` 操作符允许*重新賦值*父作用域里的变量。
+
+
+# 你可以解构函数的参数,从不定长度的参数结构里获取感兴趣的值。
+tail = ([head, ...rest]) -> rest
+tail [1, 2, 3] # => [2, 3]
+
+# 你也可以使用一元或二元操作符转换参数。当然也可以预设传入的参数值。
+foo = (a = 1, b = 2) -> a + b
+foo! # => 3
+
+# 你可以以拷贝的方式传入参数来避免副作用,例如:
+copy = (^^target, source) ->
+ for k,v of source => target[k] = v
+ target
+a = { a: 1 }
+copy a, { b: 2 } # => { a: 1, b: 2 }
+a # => { a: 1 }
+
+
+# 使用长箭号取代短箭号来柯里化一个函数:
+add = (left, right) --> left + right
+add1 = add 1
+add1 2 # => 3
+
+# 函式里有一个隐式的 `it` 变量,意谓着你不用宣告它。
+identity = -> it
+identity 1 # => 1
+
+# 操作符在 LiveScript 里不是一個函数,但你可以简单地将它们转换成函数!
+# Enter the operator sectioning:
+divide-by-2 = (/ 2)
+[2, 4, 8, 16].map(divide-by-2) .reduce (+)
+
+
+# LiveScript 里不只有应用函数,如同其它良好的函数式语言,你可以合并函数获得更多发挥:
+double-minus-one = (- 1) . (* 2)
+
+# 除了普通的数学公式合并 `f . g` 之外,还有 `>>` 和 `<<` 操作符定义函数的合并顺序。
+double-minus-one = (* 2) >> (- 1)
+double-minus-one = (- 1) << (* 2)
+
+
+# 说到合并函数的参数, LiveScript 使用 `|>` 和 `<|` 操作符将参数传入:
+map = (f, xs) --> xs.map f
+[1 2 3] |> map (* 2) # => [2 4 6]
+
+# 你也可以选择填入值的位置,只需要使用底线 _ 标记:
+reduce = (f, xs, initial) --> xs.reduce f, initial
+[1 2 3] |> reduce (+), _, 0 # => 6
+
+
+# 你也能使 _ 让任何函数变成偏函数应用:
+div = (left, right) -> left / right
+div-by-2 = div _, 2
+div-by-2 4 # => 2
+
+
+# 最后,也很重要的,LiveScript 拥有後呼叫特性, 可以是基於回调的代码
+# (你可以试试其它函数式特性的解法,比如 Promises):
+readFile = (name, f) -> f name
+a <- readFile 'foo'
+b <- readFile 'bar'
+console.log a + b
+
+# 等同於:
+readFile 'foo', (a) -> readFile 'bar', (b) -> console.log a + b
+
+
+########################################################################
+## 4. 模式、判断和流程控制
+########################################################################
+
+# 流程控制可以使用 `if...else` 表达式:
+x = if n > 0 then \positive else \negative
+
+# 除了 `then` 你也可以使用 `=>`
+x = if n > 0 => \positive
+ else \negative
+
+# 过於复杂的流程可以用 `switch` 表达式代替:
+y = {}
+x = switch
+ | (typeof y) is \number => \number
+ | (typeof y) is \string => \string
+ | 'length' of y => \array
+ | otherwise => \object # `otherwise` 和 `_` 是等价的。
+
+# 函数主体、宣告式和赋值式可以表式成 `switch`,这可以省去一些代码:
+take = (n, [x, ...xs]) -->
+ | n == 0 => []
+ | _ => [x] ++ take (n - 1), xs
+
+
+########################################################################
+## 5. 推导式
+########################################################################
+
+# 在 JavaScript 的标准函式库里有一些辅助函数能帮助处理列表及对象
+#(LiveScript 则带有一个 prelude.ls ,作为标准函式库的补充 ),
+# 推导式能让你使用优雅的语法且快速地处理这些事:
+oneToTwenty = [1 to 20]
+evens = [x for x in oneToTwenty when x % 2 == 0]
+
+# 在推导式里 `when` 和 `unless` 可以当成过滤器使用。
+
+# 对象推导式在使用上也是同样的方式,差别在于你使用的是对象而不是数组:
+copy = { [k, v] for k, v of source }
+
+
+########################################################################
+## 6. OOP
+########################################################################
+
+# 虽然 LiveScript 是一门函数式语言,但具有一些命令式及面向对象的特性。
+# 像是 class 语法和一些借镜於 CoffeeScript 的类别继承语法糖:
+class Animal
+ (@name, kind) ->
+ @kind = kind
+ action: (what) -> "*#{@name} (a #{@kind}) #{what}*"
+
+class Cat extends Animal
+ (@name) -> super @name, 'cat'
+ purr: -> @action 'purrs'
+
+kitten = new Cat 'Mei'
+kitten.purr! # => "*Mei (a cat) purrs*"
+
+# 除了类别的单一继承模式之外,还提供了像混入 (Mixins) 这种特性。
+# Mixins 在语言里被当成普通对象:
+Huggable =
+ hug: -> @action 'is hugged'
+
+class SnugglyCat extends Cat implements Huggable
+
+kitten = new SnugglyCat 'Purr'
+kitten.hug! # => "*Mei (a cat) is hugged*"
+```
+
+## 延伸阅读
+
+LiveScript 还有许多强大之处,但这些应该足够启发你写些小型函数式程式了。
+[LiveScript](http://livescript.net/)有更多关于 LiveScript 的资讯
+和线上编译器等着你来试!
+
+你也可以参考
+[prelude.ls](http://gkz.github.io/prelude-ls/),和一些 `#livescript`
+的网络聊天室频道。
diff --git a/zh-cn/lua-cn.html.markdown b/zh-cn/lua-cn.html.markdown
index 95a94c76..098d0ab5 100644
--- a/zh-cn/lua-cn.html.markdown
+++ b/zh-cn/lua-cn.html.markdown
@@ -1,5 +1,5 @@
---
-language: lua
+language: Lua
lang: zh-cn
contributors:
- ["Tyler Neylon", "http://tylerneylon.com/"]
@@ -9,6 +9,7 @@ contributors:
- ["Amr Tamimi", "https://amrtamimi.com"]
translators:
- ["Jakukyo Friel", "http://weakish.github.io"]
+filename: lua-cn.lua
---
```lua
@@ -62,8 +63,8 @@ foo = anUnknownVariable -- 现在 foo = nil.
aBoolValue = false
---只有nil和false为假; 0和 ''都均为真!
-if not aBoolValue then print('twas false') end
+--只有nil和false为假; 0和 ''均为真!
+if not aBoolValue then print('false') end
-- 'or'和 'and'短路
-- 类似于C/js里的 a?b:c 操作符:
@@ -148,7 +149,7 @@ print {} -- 一样可以工作。
-- Table = Lua唯一的组合数据结构;
-- 它们是关联数组。
-- 类似于PHP的数组或者js的对象,
--- 它们是哈希表或者字典,也可以当初列表使用。
+-- 它们是哈希表或者字典,也可以当列表使用。
-- 按字典/map的方式使用Table:
diff --git a/zh-cn/markdown-cn.html.markdown b/zh-cn/markdown-cn.html.markdown
new file mode 100644
index 00000000..b1143dac
--- /dev/null
+++ b/zh-cn/markdown-cn.html.markdown
@@ -0,0 +1,240 @@
+---
+language: markdown
+contributors:
+ - ["Dan Turkel", "http://danturkel.com/"]
+translators:
+ - ["Fangzhou Chen","https://github.com/FZSS"]
+filename: learnmarkdown-cn.md
+lang: zh-cn
+---
+
+Markdown 由 John Gruber 于 2004年创立. 它旨在成为一门容易读写的语法结构,并可以便利地转换成 HTML(以及其他很多)格式。
+
+欢迎您多多反馈以及分支和请求合并。
+
+
+```markdown
+<!-- Markdown 是 HTML 的父集,所以任何 HTML 文件都是有效的 Markdown。
+这意味着我们可以在 Markdown 里使用任何 HTML 元素,比如注释元素,
+且不会被 Markdown 解析器所影响。不过如果你在 Markdown 文件内创建了 HTML 元素,
+你将无法在 HTML 元素的内容中使用 Markdown 语法。-->
+
+<!-- 在不同的解析器中,Markdown 的实现方法有所不同。
+此教程会指出当某功能是否通用及是否只对某一解析器有效。 -->
+
+<!-- 标头 -->
+<!-- 通过在文本前加上不同数量的hash(#), 你可以创建相对应的 <h1>
+到 <h6> HTML元素。-->
+
+# 这是一个 <h1>
+## 这是一个 <h2>
+### 这是一个 <h3>
+#### 这是一个 <h4>
+##### 这是一个 <h5>
+###### 这是一个 <h6>
+
+<!-- 对于 <h1> 和 <h2> 元素,Markdown 额外提供了两种添加方式。 -->
+这是一个 h1
+=============
+
+这是一个 h2
+-------------
+
+<!-- 简易文本样式 -->
+<!-- 文本的斜体,粗体,和删除线在 Markdown 中可以轻易地被实现。-->
+
+*此文本为斜体。*
+_此文本也是。_
+
+**此文本为粗体。**
+__此文本也是__
+
+***此文本是斜体加粗体。***
+**_或者这样。_**
+*__这个也是!__*
+
+<!-- 在 Github 采用的 Markdown 中 -->
+
+~~此文本为删除线效果。~~
+
+<!-- 单个段落由一句或多句邻近的句子组成,这些句子由一个或多个空格分隔。-->
+
+这是第一段落. 这句话在同一个段落里,好玩么?
+
+现在我是第二段落。
+这句话也在第二段落!
+
+这句话在第三段落!
+
+<!-- 如果你插入一个 HTML中的<br />标签,你可以在段末加入两个以上的空格,
+然后另起一段。-->
+
+此段落结尾有两个空格(选中以显示)。
+
+上文有一个 <br /> !
+
+<!-- 段落引用可由 > 字符轻松实现。-->
+
+> 这是一个段落引用. 你可以
+> 手动断开你的句子,然后在每句句子前面添加 “>” 字符。或者让你的句子变得很长,以至于他们自动得断开。
+> 只要你的文字以“>” 字符开头,两种方式无异。
+
+> 你也对文本进行
+>> 多层引用
+> 这多机智啊!
+
+<!-- 序列 -->
+<!-- 无序序列可由星号,加号或者减号来建立 -->
+
+* 项目
+* 项目
+* 另一个项目
+
+或者
+
++ 项目
++ 项目
++ 另一个项目
+
+或者
+
+- 项目
+- 项目
+- 最后一个项目
+
+<!-- 有序序列可由数字加点来实现 -->
+
+1. 项目一
+2. 项目二
+3. 项目三
+
+<!-- 即使你的标签数字有误,Markdown 依旧会呈现出正确的序号,
+不过这并不是一个好主意-->
+
+1. 项目一
+1. 项目二
+1. 项目三
+<!-- (此段与前例一模一样) -->
+
+<!-- 你也可以使用子序列 -->
+
+1. 项目一
+2. 项目二
+3. 项目三
+ * 子项目
+ * 子项目
+4. 项目四
+
+<!-- 代码段落 -->
+<!-- 代码段落(HTML中 <code>标签)可以由缩进四格(spaces)
+或者一个制表符(tab)实现-->
+
+ This is code
+ So is this
+
+<!-- 在你的代码中,你仍然使用tab可以进行缩进操作 -->
+
+ my_array.each do |item|
+ puts item
+ end
+
+<!-- 内联代码可由反引号 ` 实现 -->
+
+John 甚至不知道 `go_to()` 方程是干嘛的!
+
+<!-- 在Github的 Markdown中,对于代码你可以使用特殊的语法 -->
+
+\`\`\`ruby <!-- 插入时记得移除反斜线, 仅留```ruby ! -->
+def foobar
+ puts "Hello world!"
+end
+\`\`\` <!-- 这里也是,移除反斜线,仅留 ``` -->
+
+<!-- 以上代码不需要缩进,而且 Github 会根据```后表明的语言来进行语法高亮 -->
+
+<!-- 水平线 (<hr />) -->
+<!-- 水平线可由三个或以上的星号或者减号创建,可带可不带空格。 -->
+
+***
+---
+- - -
+****************
+
+<!-- 链接 -->
+<!-- Markdown 最棒的地方就是简易的链接制作。链接文字放在中括号[]内,
+在随后的括弧()内加入url。-->
+
+[点我点我!](http://test.com/)
+
+<!-- 你也可以为链接加入一个标题:在括弧内使用引号 -->
+
+[点我点我!](http://test.com/ "连接到Test.com")
+
+<!-- 相对路径也可以有 -->
+
+[去 music](/music/).
+
+<!-- Markdown同样支持引用样式的链接 -->
+
+[点此链接][link1]以获取更多信息!
+[看一看这个链接][foobar] 如果你愿意的话.
+
+[link1]: http://test.com/ "Cool!"
+[foobar]: http://foobar.biz/ "Alright!"
+
+<!-- 链接的标题可以处于单引号中,括弧中或是被忽略。引用名可以在文档的任意何处,
+并且可以随意命名,只要名称不重复。-->
+
+<!-- “隐含式命名” 的功能可以让链接文字作为引用名 -->
+
+[This][] is a link.
+
+[this]: http://thisisalink.com/
+
+<!-- 但这并不常用 -->
+
+<!-- 图像 -->
+<!-- 图像与链接相似,只需在前添加一个感叹号 -->
+
+![这是我图像的悬停文本(alt text)](http://imgur.com/myimage.jpg "可选命名")
+
+<!-- 引用样式也同样起作用 -->
+
+![这是我的悬停文本.][myimage]
+
+[myimage]: relative/urls/cool/image.jpg "在此输入标题"
+
+<!-- 杂项 -->
+<!-- 自动链接 -->
+
+<http://testwebsite.com/> 与
+[http://testwebsite.com/](http://testwebsite.com/) 等同
+
+<!-- 电子邮件的自动链接 -->
+
+<foo@bar.com>
+
+<!-- 转义字符 -->
+
+我希望 *将这段文字置于星号之间* 但是我不希望它被
+斜体化, 所以我就: \*这段置文字于星号之间\*。
+
+<!-- 表格 -->
+<!-- 表格只被 Github 的 Markdown 支持,并且有一点笨重,但如果你真的要用的话: -->
+
+| 第一列 | 第二列 | 第三列 |
+| :---------- | :------: | ----------: |
+| 左对齐 | 居个中 | 右对齐 |
+| 某某某 | 某某某 | 某某某 |
+
+<!-- 或者, 同样的 -->
+
+第一列 | 第二列 | 第三列
+:-- | :-: | --:
+这太丑了 | 药不能 | 停
+
+<!-- 结束! -->
+
+```
+
+更多信息, 请于[此处](http://daringfireball.net/projects/Markdown/syntax)参见 John Gruber 关于语法的官方帖子,及于[此处](https://github.com/adam-p/Markdown-here/wiki/Markdown-Cheatsheet) 参见 Adam Pritchard 的摘要笔记。
diff --git a/zh-cn/matlab-cn.html.markdown b/zh-cn/matlab-cn.html.markdown
new file mode 100644
index 00000000..77ba765a
--- /dev/null
+++ b/zh-cn/matlab-cn.html.markdown
@@ -0,0 +1,491 @@
+---
+language: Matlab
+contributors:
+ - ["mendozao", "http://github.com/mendozao"]
+ - ["jamesscottbrown", "http://jamesscottbrown.com"]
+translators:
+ - ["sunxb10", "https://github.com/sunxb10"]
+lang: zh-cn
+---
+
+MATLAB 是 MATrix LABoratory (矩阵实验室)的缩写,它是一种功能强大的数值计算语言,在工程和数学领域中应用广泛。
+
+如果您有任何需要反馈或交流的内容,请联系本教程作者[@the_ozzinator](https://twitter.com/the_ozzinator)、[osvaldo.t.mendoza@gmail.com](mailto:osvaldo.t.mendoza@gmail.com)。
+
+```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/products/matlab/)
+* 官方论坛:[http://www.mathworks.com/matlabcentral/answers/](http://www.mathworks.com/matlabcentral/answers/)
diff --git a/zh-cn/php-cn.html.markdown b/zh-cn/php-cn.html.markdown
index 24939681..2def7f1c 100644
--- a/zh-cn/php-cn.html.markdown
+++ b/zh-cn/php-cn.html.markdown
@@ -1,5 +1,5 @@
---
-language: php
+language: PHP
contributors:
- ["Malcolm Fell", "http://emarref.net/"]
- ["Trismegiste", "https://github.com/Trismegiste"]
diff --git a/zh-cn/python3-cn.html.markdown b/zh-cn/python3-cn.html.markdown
new file mode 100644
index 00000000..c223297c
--- /dev/null
+++ b/zh-cn/python3-cn.html.markdown
@@ -0,0 +1,629 @@
+---
+language: python3
+contributors:
+ - ["Louie Dinh", "http://pythonpracticeprojects.com"]
+ - ["Steven Basart", "http://github.com/xksteven"]
+ - ["Andre Polykanine", "https://github.com/Oire"]
+translators:
+ - ["Geoff Liu", "http://geoffliu.me"]
+filename: learnpython3-cn.py
+lang: zh-cn
+---
+
+Python是由吉多·范罗苏姆(Guido Van Rossum)在90年代早期设计。它是如今最常用的编程
+语言之一。它的语法简洁且优美,几乎就是可执行的伪代码。
+
+欢迎大家斧正。英文版原作Louie Dinh [@louiedinh](http://twitter.com/louiedinh)
+或着Email louiedinh [at] [谷歌的信箱服务]。中文翻译Geoff Liu。
+
+注意:这篇教程是特别为Python3写的。如果你想学旧版Python2,我们特别有另一篇教程。
+
+```python
+
+# 用井字符开头的是单行注释
+
+""" 多行字符串用三个引号
+ 包裹,也常被用来做多
+ 行注释
+"""
+
+####################################################
+## 1. 原始数据类型和运算符
+####################################################
+
+# 整数
+3 # => 3
+
+# 算术没有什么出乎意料的
+1 + 1 # => 2
+8 - 1 # => 7
+10 * 2 # => 20
+
+# 但是除法例外,会自动转换成浮点数
+35 / 5 # => 7.0
+5 / 3 # => 1.6666666666666667
+
+# 整数除法的结果都是向下取整
+5 // 3 # => 1
+5.0 // 3.0 # => 1.0 # 浮点数也可以
+-5 // 3 # => -2
+-5.0 // 3.0 # => -2.0
+
+# 浮点数的运算结果也是浮点数
+3 * 2.0 # => 6.0
+
+# 模除
+7 % 3 # => 1
+
+# x的y次方
+2**4 # => 16
+
+# 用括号决定优先级
+(1 + 3) * 2 # => 8
+
+# 布尔值
+True
+False
+
+# 用not取非
+not True # => False
+not False # => True
+
+# 逻辑运算符,注意and和or都是小写
+True and False #=> False
+False or True #=> True
+
+# 整数也可以当作布尔值
+0 and 2 #=> 0
+-5 or 0 #=> -5
+0 == False #=> True
+2 == True #=> False
+1 == True #=> 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
+
+# 字符串用单引双引都可以
+"这是个字符串"
+'这也是个字符串'
+
+# 用加号连接字符串
+"Hello " + "world!" # => "Hello world!"
+
+# 字符串可以被当作字符列表
+"This is a string"[0] # => 'T'
+
+# 用.format来格式化字符串
+"{} can be {}".format("strings", "interpolated")
+
+# 可以重复参数以节省时间
+"{0} be nimble, {0} be quick, {0} jump over the {1}".format("Jack", "candle stick")
+#=> "Jack be nimble, Jack be quick, Jack jump over the candle stick"
+
+# 如果不想数参数,可以用关键字
+"{name} wants to eat {food}".format(name="Bob", food="lasagna") #=> "Bob wants to eat lasagna"
+
+# 如果你的Python3程序也要在Python2.5以下环境运行,也可以用老式的格式化语法
+"%s can be %s the %s way" % ("strings", "interpolated", "old")
+
+# None是一个对象
+None # => None
+
+# 当与None进行比较时不要用 ==,要用is。is是用来比较两个变量是否指向同一个对象。
+"etc" is None # => False
+None is None # => True
+
+# None,0,空字符串,空列表,空字典都算是False
+# 所有其他值都是True
+bool(0) # => False
+bool("") # => False
+bool([]) #=> False
+bool({}) #=> False
+
+
+####################################################
+## 2. 变量和集合
+####################################################
+
+# print是内置的打印函数
+print("I'm Python. Nice to meet you!")
+
+# 在给变量赋值前不用提前声明
+# 传统的变量命名是小写,用下划线分隔单词
+some_var = 5
+some_var # => 5
+
+# 访问未赋值的变量会抛出异常
+# 参考流程控制一段来学习异常处理
+some_unknown_var # 抛出NameError
+
+# 用列表(list)储存序列
+li = []
+# 创建列表时也可以同时赋给元素
+other_li = [4, 5, 6]
+
+# 用append在列表最后追加元素
+li.append(1) # li现在是[1]
+li.append(2) # li现在是[1, 2]
+li.append(4) # li现在是[1, 2, 4]
+li.append(3) # li现在是[1, 2, 4, 3]
+# 用pop从列表尾部删除
+li.pop() # => 3 且li现在是[1, 2, 4]
+# 把3再放回去
+li.append(3) # li变回[1, 2, 4, 3]
+
+# 列表存取跟数组一样
+li[0] # => 1
+# 取出最后一个元素
+li[-1] # => 3
+
+# 越界存取会造成IndexError
+li[4] # 抛出IndexError
+
+# 列表有切割语法
+li[1:3] # => [2, 4]
+# 取尾
+li[2:] # => [4, 3]
+# 取头
+li[:3] # => [1, 2, 4]
+# 隔一个取一个
+li[::2] # =>[1, 4]
+# 倒排列表
+li[::-1] # => [3, 4, 2, 1]
+# 可以用三个参数的任何组合来构建切割
+# li[始:终:步伐]
+
+# 用del删除任何一个元素
+del li[2] # li is now [1, 2, 3]
+
+# 列表可以相加
+# 注意:li和other_li的值都不变
+li + other_li # => [1, 2, 3, 4, 5, 6]
+
+# 用extend拼接列表
+li.extend(other_li) # li现在是[1, 2, 3, 4, 5, 6]
+
+# 用in测试列表是否包含值
+1 in li # => True
+
+# 用len取列表长度
+len(li) # => 6
+
+
+# 元组是不可改变的序列
+tup = (1, 2, 3)
+tup[0] # => 1
+tup[0] = 3 # 抛出TypeError
+
+# 列表允许的操作元组大都可以
+len(tup) # => 3
+tup + (4, 5, 6) # => (1, 2, 3, 4, 5, 6)
+tup[:2] # => (1, 2)
+2 in tup # => True
+
+# 可以把元组合列表解包,赋值给变量
+a, b, c = (1, 2, 3) # 现在a是1,b是2,c是3
+# 元组周围的括号是可以省略的
+d, e, f = 4, 5, 6
+# 交换两个变量的值就这么简单
+e, d = d, e # 现在d是5,e是4
+
+
+# 用字典表达映射关系
+empty_dict = {}
+# 初始化的字典
+filled_dict = {"one": 1, "two": 2, "three": 3}
+
+# 用[]取值
+filled_dict["one"] # => 1
+
+
+# 用keys获得所有的键。因为keys返回一个可迭代对象,所以在这里把结果包在list里。我们下面会详细介绍可迭代。
+# 注意:字典键的顺序是不定的,你得到的结果可能和以下不同。
+list(filled_dict.keys()) # => ["three", "two", "one"]
+
+
+# 用values获得所有的值。跟keys一样,要用list包起来,顺序也可能不同。
+list(filled_dict.values()) # => [3, 2, 1]
+
+
+# 用in测试一个字典是否包含一个键
+"one" in filled_dict # => True
+1 in filled_dict # => False
+
+# 访问不存在的键会导致KeyError
+filled_dict["four"] # KeyError
+
+# 用get来避免KeyError
+filled_dict.get("one") # => 1
+filled_dict.get("four") # => None
+# 当键不存在的时候get方法可以返回默认值
+filled_dict.get("one", 4) # => 1
+filled_dict.get("four", 4) # => 4
+
+# setdefault方法只有当键不存在的时候插入新值
+filled_dict.setdefault("five", 5) # filled_dict["five"]设为5
+filled_dict.setdefault("five", 6) # filled_dict["five"]还是5
+
+# 字典赋值
+filled_dict.update({"four":4}) #=> {"one": 1, "two": 2, "three": 3, "four": 4}
+filled_dict["four"] = 4 # 另一种赋值方法
+
+# 用del删除
+del filled_dict["one"] # 从filled_dict中把one删除
+
+
+# 用set表达集合
+empty_set = set()
+# 初始化一个集合,语法跟字典相似。
+some_set = {1, 1, 2, 2, 3, 4} # some_set现在是{1, 2, 3, 4}
+
+# 可以把集合赋值于变量
+filled_set = some_set
+
+# 为集合添加元素
+filled_set.add(5) # filled_set现在是{1, 2, 3, 4, 5}
+
+# & 取交集
+other_set = {3, 4, 5, 6}
+filled_set & other_set # => {3, 4, 5}
+
+# | 取并集
+filled_set | other_set # => {1, 2, 3, 4, 5, 6}
+
+# - 取补集
+{1, 2, 3, 4} - {2, 3, 5} # => {1, 4}
+
+# in 测试集合是否包含元素
+2 in filled_set # => True
+10 in filled_set # => False
+
+
+####################################################
+## 3. 流程控制和迭代器
+####################################################
+
+# 先随便定义一个变量
+some_var = 5
+
+# 这是个if语句。注意缩进在Python里是有意义的
+# 印出"some_var比10小"
+if some_var > 10:
+ print("some_var比10大")
+elif some_var < 10: # elif句是可选的
+ print("some_var比10小")
+else: # else也是可选的
+ print("some_var就是10")
+
+
+"""
+用for循环语句遍历列表
+打印:
+ dog is a mammal
+ cat is a mammal
+ mouse is a mammal
+"""
+for animal in ["dog", "cat", "mouse"]:
+ print("{} is a mammal".format(animal))
+
+"""
+"range(number)"返回数字列表从0到给的数字
+打印:
+ 0
+ 1
+ 2
+ 3
+"""
+for i in range(4):
+ print(i)
+
+"""
+while循环直到条件不满足
+打印:
+ 0
+ 1
+ 2
+ 3
+"""
+x = 0
+while x < 4:
+ print(x)
+ x += 1 # x = x + 1 的简写
+
+# 用try/except块处理异常状况
+try:
+ # 用raise抛出异常
+ raise IndexError("This is an index error")
+except IndexError as e:
+ pass # pass是无操作,但是应该在这里处理错误
+except (TypeError, NameError):
+ pass # 可以同时处理不同类的错误
+else: # else语句是可选的,必须在所有的except之后
+ print("All good!") # 只有当try运行完没有错误的时候这句才会运行
+
+
+# Python提供一个叫做可迭代(iterable)的基本抽象。一个可迭代对象是可以被当作序列
+# 的对象。比如说上面range返回的对象就是可迭代的。
+
+filled_dict = {"one": 1, "two": 2, "three": 3}
+our_iterable = filled_dict.keys()
+print(our_iterable) # => range(1,10) 是一个实现可迭代接口的对象
+
+# 可迭代对象可以遍历
+for i in our_iterable:
+ print(i) # 打印 one, two, three
+
+# 但是不可以随机访问
+our_iterable[1] # 抛出TypeError
+
+# 可迭代对象知道怎么生成迭代器
+our_iterator = iter(our_iterable)
+
+# 迭代器是一个可以记住遍历的位置的对象
+# 用__next__可以取得下一个元素
+our_iterator.__next__() #=> "one"
+
+# 再一次调取__next__时会记得位置
+our_iterator.__next__() #=> "two"
+our_iterator.__next__() #=> "three"
+
+# 当迭代器所有元素都取出后,会抛出StopIteration
+our_iterator.__next__() # 抛出StopIteration
+
+# 可以用list一次取出迭代器所有的元素
+list(filled_dict.keys()) #=> Returns ["one", "two", "three"]
+
+
+
+####################################################
+## 4. 函数
+####################################################
+
+# 用def定义新函数
+def add(x, y):
+ print("x is {} and y is {}".format(x, y))
+ return x + y # 用return语句返回
+
+# 调用函数
+add(5, 6) # => 印出"x is 5 and y is 6"并且返回11
+
+# 也可以用关键字参数来调用函数
+add(y=6, x=5) # 关键字参数可以用任何顺序
+
+
+# 我们可以定义一个可变参数函数
+def varargs(*args):
+ return args
+
+varargs(1, 2, 3) # => (1, 2, 3)
+
+
+# 我们也可以定义一个关键字可变参数函数
+def keyword_args(**kwargs):
+ return kwargs
+
+# 我们来看看结果是什么:
+keyword_args(big="foot", loch="ness") # => {"big": "foot", "loch": "ness"}
+
+
+# 这两种可变参数可以混着用
+def all_the_args(*args, **kwargs):
+ print(args)
+ print(kwargs)
+"""
+all_the_args(1, 2, a=3, b=4) prints:
+ (1, 2)
+ {"a": 3, "b": 4}
+"""
+
+# 调用可变参数函数时可以做跟上面相反的,用*展开序列,用**展开字典。
+args = (1, 2, 3, 4)
+kwargs = {"a": 3, "b": 4}
+all_the_args(*args) # 相当于 foo(1, 2, 3, 4)
+all_the_args(**kwargs) # 相当于 foo(a=3, b=4)
+all_the_args(*args, **kwargs) # 相当于 foo(1, 2, 3, 4, a=3, b=4)
+
+
+# 函数作用域
+x = 5
+
+def setX(num):
+ # 局部作用域的x和全局域的x是不同的
+ x = num # => 43
+ print (x) # => 43
+
+def setGlobalX(num):
+ global x
+ print (x) # => 5
+ x = num # 现在全局域的x被赋值
+ print (x) # => 6
+
+setX(43)
+setGlobalX(6)
+
+
+# 函数在Python是一等公民
+def create_adder(x):
+ def adder(y):
+ return x + y
+ return adder
+
+add_10 = create_adder(10)
+add_10(3) # => 13
+
+# 也有匿名函数
+(lambda x: x > 2)(3) # => True
+
+# 内置的高阶函数
+map(add_10, [1, 2, 3]) # => [11, 12, 13]
+filter(lambda x: x > 5, [3, 4, 5, 6, 7]) # => [6, 7]
+
+# 用列表推导式可以简化映射和过滤。列表推导式的返回值是另一个列表。
+[add_10(i) for i in [1, 2, 3]] # => [11, 12, 13]
+[x for x in [3, 4, 5, 6, 7] if x > 5] # => [6, 7]
+
+####################################################
+## 5. 类
+####################################################
+
+
+# 定义一个继承object的类
+class Human(object):
+
+ # 类属性,被所有此类的实例共用。
+ species = "H. sapiens"
+
+ # 构造方法,当实例被初始化时被调用。注意名字前后的双下划线,这是表明这个属
+ # 性或方法对Python有特殊意义,但是允许用户自行定义。你自己取名时不应该用这
+ # 种格式。
+ def __init__(self, name):
+ # Assign the argument to the instance's name attribute
+ self.name = name
+
+ # 实例方法,第一个参数总是self,就是这个实例对象
+ def say(self, msg):
+ return "{name}: {message}".format(name=self.name, message=msg)
+
+ # 类方法,被所有此类的实例共用。第一个参数是这个类对象。
+ @classmethod
+ def get_species(cls):
+ return cls.species
+
+ # 静态方法。调用时没有实例或类的绑定。
+ @staticmethod
+ def grunt():
+ return "*grunt*"
+
+
+# 构造一个实例
+i = Human(name="Ian")
+print(i.say("hi")) # 印出 "Ian: hi"
+
+j = Human("Joel")
+print(j.say("hello")) # 印出 "Joel: hello"
+
+# 调用一个类方法
+i.get_species() # => "H. sapiens"
+
+# 改一个共用的类属性
+Human.species = "H. neanderthalensis"
+i.get_species() # => "H. neanderthalensis"
+j.get_species() # => "H. neanderthalensis"
+
+# 调用静态方法
+Human.grunt() # => "*grunt*"
+
+
+####################################################
+## 6. 模块
+####################################################
+
+# 用import导入模块
+import math
+print(math.sqrt(16)) # => 4
+
+# 也可以从模块中导入个别值
+from math import ceil, floor
+print(ceil(3.7)) # => 4.0
+print(floor(3.7)) # => 3.0
+
+# 可以导入一个模块中所有值
+# 警告:不建议这么做
+from math import *
+
+# 如此缩写模块名字
+import math as m
+math.sqrt(16) == m.sqrt(16) # => True
+
+# Python模块其实就是普通的Python文件。你可以自己写,然后导入,
+# 模块的名字就是文件的名字。
+
+# 你可以这样列出一个模块里所有的值
+import math
+dir(math)
+
+
+####################################################
+## 7. 高级用法
+####################################################
+
+# 用生成器(generators)方便地写惰性运算
+def double_numbers(iterable):
+ for i in iterable:
+ yield i + i
+
+# 生成器只有在需要时才计算下一个值。它们每一次循环只生成一个值,而不是把所有的
+# 值全部算好。这意味着double_numbers不会生成大于15的数字。
+#
+# range的返回值也是一个生成器,不然一个1到900000000的列表会花很多时间和内存。
+#
+# 如果你想用一个Python的关键字当作变量名,可以加一个下划线来区分。
+range_ = range(1, 900000000)
+# 当找到一个 >=30 的结果就会停
+for i in double_numbers(range_):
+ print(i)
+ if i >= 30:
+ break
+
+
+# 装饰器(decorators)
+# 这个例子中,beg装饰say
+# beg会先调用say。如果返回的say_please为真,beg会改变返回的字符串。
+from functools import wraps
+
+
+def beg(target_function):
+ @wraps(target_function)
+ def wrapper(*args, **kwargs):
+ msg, say_please = target_function(*args, **kwargs)
+ if say_please:
+ return "{} {}".format(msg, "Please! I am poor :(")
+ return msg
+
+ return wrapper
+
+
+@beg
+def say(say_please=False):
+ msg = "Can you buy me a beer?"
+ return msg, say_please
+
+
+print(say()) # Can you buy me a beer?
+print(say(say_please=True)) # Can you buy me a beer? Please! I am poor :(
+```
+
+## 想继续学吗?
+
+### 线上免费材料(英文)
+
+* [Learn Python The Hard Way](http://learnpythonthehardway.org/book/)
+* [Dive Into Python](http://www.diveintopython.net/)
+* [Ideas for Python Projects](http://pythonpracticeprojects.com)
+
+* [The Official Docs](http://docs.python.org/3/)
+* [Hitchhiker's Guide to Python](http://docs.python-guide.org/en/latest/)
+* [Python Module of the Week](http://pymotw.com/3/)
+* [A Crash Course in Python for Scientists](http://nbviewer.ipython.org/5920182)
+
+### 书籍(也是英文)
+
+* [Programming Python](http://www.amazon.com/gp/product/0596158106/ref=as_li_qf_sp_asin_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596158106&linkCode=as2&tag=homebits04-20)
+* [Dive Into Python](http://www.amazon.com/gp/product/1441413022/ref=as_li_tf_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1441413022&linkCode=as2&tag=homebits04-20)
+* [Python Essential Reference](http://www.amazon.com/gp/product/0672329786/ref=as_li_tf_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0672329786&linkCode=as2&tag=homebits04-20)
+
diff --git a/zh-cn/r-cn.html.markdown b/zh-cn/r-cn.html.markdown
index 19c5f25d..0c46bc22 100644
--- a/zh-cn/r-cn.html.markdown
+++ b/zh-cn/r-cn.html.markdown
@@ -13,7 +13,7 @@ lang: zh-cn
R 是一门统计语言。它有很多数据分析和挖掘程序包。可以用来统计、分析和制图。
你也可以在 LaTeX 文档中运行 `R` 命令。
-```python
+```r
# 评论以 # 开始
# R 语言原生不支持 多行注释
diff --git a/zh-cn/ruby-cn.html.markdown b/zh-cn/ruby-cn.html.markdown
index 3c47f3f9..99250b43 100644
--- a/zh-cn/ruby-cn.html.markdown
+++ b/zh-cn/ruby-cn.html.markdown
@@ -40,7 +40,7 @@ translators:
1.+(3) #=> 4
10.* 5 #=> 50
-# 特殊的只也是对象
+# 特殊的值也是对象
nil # 空
true # 真
false # 假
diff --git a/zh-cn/scala-cn.html.markdown b/zh-cn/scala-cn.html.markdown
index 28af8ddc..508dd58e 100644
--- a/zh-cn/scala-cn.html.markdown
+++ b/zh-cn/scala-cn.html.markdown
@@ -4,36 +4,47 @@ filename: learnscala-zh.scala
contributors:
- ["George Petrov", "http://github.com/petrovg"]
- ["Dominic Bou-Samra", "http://dbousamra.github.com"]
+ - ["Geoff Liu", "http://geoffliu.me"]
translators:
- ["Peiyong Lin", ""]
+ - ["Jinchang Ye", "http://github.com/alwayswithme"]
+ - ["Guodong Qu", "https://github.com/jasonqu"]
lang: zh-cn
---
-Scala - 一门可拓展性的语言
+Scala - 一门可拓展的语言
-```cpp
+```scala
/*
自行设置:
1) 下载 Scala - http://www.scala-lang.org/downloads
- 2) unzip/untar 到你喜欢的地方,放在路径中的 bin 目录下
- 3) 在终端输入 scala,开启 Scala 的 REPL,你会看到提示符:
+ 2) unzip/untar 到您喜欢的地方,并把 bin 子目录添加到 path 环境变量
+ 3) 在终端输入 scala,启动 Scala 的 REPL,您会看到提示符:
scala>
- 这就是所谓的 REPL,你现在可以在其中运行命令,让我们做到这一点:
+ 这就是所谓的 REPL (读取-求值-输出循环,英语: Read-Eval-Print Loop),
+ 您可以在其中输入合法的表达式,结果会被打印。
+ 在教程中我们会进一步解释 Scala 文件是怎样的,但现在先了解一点基础。
*/
-println(10) // 打印整数 10
-println("Boo!") // 打印字符串 "BOO!"
+/////////////////////////////////////////////////
+// 1. 基础
+/////////////////////////////////////////////////
+// 单行注释开始于两个斜杠
-// 一些基础
+/*
+ 多行注释,如您之前所见,看起来像这样
+*/
// 打印并强制换行
println("Hello world!")
+println(10)
+
// 没有强制换行的打印
print("Hello world")
@@ -41,13 +52,19 @@ print("Hello world")
// val 声明是不可变的,var 声明是可修改的。不可变性是好事。
val x = 10 // x 现在是 10
x = 20 // 错误: 对 val 声明的变量重新赋值
-var x = 10
-x = 20 // x 现在是 20
+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
@@ -64,9 +81,11 @@ true == false // false
2 - 1 // 1
5 * 3 // 15
6 / 2 // 3
+6 / 4 // 1
+6.0 / 4 // 1.5
-// 在 REPL 计算一个命令会返回给你结果的类型和值
+// 在 REPL 计算一个表达式会返回给您结果的类型和值
1 + 7
@@ -77,58 +96,190 @@ true == false // false
这意味着计算 1 + 7 的结果是一个 Int 类型的对象,其值为 8
- 1+7 的结果是一样的
+ 注意 "res29" 是一个连续生成的变量名,用以存储您输入的表达式结果,
+ 您看到的输出可能不一样。
*/
+"Scala strings are surrounded by double quotes"
+'a' // Scala 的字符
+// '不存在单引号字符串' <= 这会导致错误
-// 包括函数在内,每一个事物都是对象。在 REPL 中输入:
+// String 有常见的 Java 字符串方法
+"hello world".length
+"hello world".substring(2, 6)
+"hello world".replace("C", "3")
-7 // 结果 res30: Int = 7 (res30 是一个生成的结果的 var 命名)
+// 也有一些额外的 Scala 方法,另请参见:scala.collection.immutable.StringOps
+"hello world".take(5)
+"hello world".drop(5)
-// 下一行给你一个接收一个 Int 类型并返回该数的平方的函数
-(x:Int) => x * x
+// 字符串改写:留意前缀 "s"
+val n = 45
+s"We have $n apples" // => "We have 45 apples"
-// 你可以分配给函数一个标识符,像这样:
-val sq = (x:Int) => x * x
+// 在要改写的字符串中使用表达式也是可以的
+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"
-/* 上面的例子说明
-
- sq: Int => Int = <function1>
+// 添加 "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"
- 意味着这次我们给予了 sq 这样一个显式的名字给一个接受一个 Int 类型值并返回 一个 Int 类型值的函数
+// 未处理的字符串,忽略特殊字符。
+raw"New line feed: \n. Carriage return: \r." // => "New line feed: \n. Carriage return: \r."
- sq 可以像下面那样被执行:
-*/
+// 一些字符需要转义,比如字符串中的双引号
+"They stood outside the \"Rose and Crown\"" // => "They stood outside the "Rose and Crown""
-sq(10) // 返回给你:res33: Int = 100.
+// 三个双引号可以使字符串跨越多行,并包含引号
+val html = """<form id="daform">
+ <p>Press belo', Joe</p>
+ <input type="submit">
+ </form>"""
-// Scala 允许方法和函数返回或者接受其它的函数或者方法作为参数。
-val add10: Int => Int = _ + 10 // 一个接受一个 Int 类型参数并返回一个 Int 类型值的函数
-List(1, 2, 3) map add10 // List(11, 12, 13) - add10 被应用到每一个元素
+/////////////////////////////////////////////////
+// 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
+}
-// 匿名函数可以被使用来代替有命名的函数:
-List(1, 2, 3) map (x => x + 10)
+// 如果函数体是单行表达式,{ } 可以省略:
+def sumOfSquaresShort(x: Int, y: Int): Int = x * x + y * y
-// 下划线标志,如果匿名函数只有一个参数可以被使用来表示该参数变量
-List(1, 2, 3) map (_ + 10)
+// 函数调用的语法是熟知的:
+sumOfSquares(3, 4) // => 25
-// 如果你所应用的匿名块和匿名函数都接受一个参数,那么你甚至可以省略下划线
-List("Dom", "Bob", "Natalia") foreach println
+// 在多数情况下 (递归函数是需要注意的例外), 函数返回值可以省略,
+// 变量所用的类型推导一样会应用到函数返回值中:
+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) // 这会抛出一个异常
+a(21) // 抛出异常
val m = Map("fork" -> "tenedor", "spoon" -> "cuchara", "knife" -> "cuchillo")
m("fork")
m("spoon")
-m("bottle") // 这会抛出一个异常
+m("bottle") // 抛出异常
val safeM = m.withDefaultValue("no lo se")
safeM("bottle")
@@ -137,9 +288,9 @@ val s = Set(1, 3, 7)
s(0)
s(1)
-/* 查看 map 的文档
- * 点击[这里](http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.Map)
- * 确保你可以读它
+/* 这里查看 map 的文档 -
+ * http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.Map
+ * 并确保你会阅读
*/
@@ -154,13 +305,11 @@ s(1)
(a, 2, "three")
// 为什么有这个?
-
val divideInts = (x:Int, y:Int) => (x / y, x % y)
-divideInts(10,3) // 函数 divideInts 返回你结果和余数
+divideInts(10,3) // 函数 divideInts 同时返回结果和余数
// 要读取元组的元素,使用 _._n,n是从1开始的元素索引
-
val d = divideInts(10,3)
d._1
@@ -168,234 +317,289 @@ d._1
d._2
+/////////////////////////////////////////////////
+// 5. 面向对象编程
+/////////////////////////////////////////////////
-// 选择器
-
-s.map(sq)
-
-val sSquared = s. map(sq)
-
-sSquared.filter(_ < 10)
+/*
+ 旁白: 教程中到现在为止我们所做的一切只是简单的表达式(值,函数等)。
+ 这些表达式可以输入到命令行解释器中作为快速测试,但它们不能独立存在于 Scala
+ 文件。举个例子,您不能在 Scala 文件上简单的写上 "val x = 5"。相反 Scala 文件
+ 允许的顶级结构是:
-sSquared.reduce (_+_)
+ - objects
+ - classes
+ - case classes
+ - traits
-// filter 函数接受一个预测(一个函数,形式为 A -> Boolean) 并选择出所有的元素满足这个预测
+ 现在来解释这些是什么。
+*/
-List(1, 2, 3) filter (_ > 2) // List(3)
-List(
- Person(name = "Dom", age = 23),
- Person(name = "Bob", age = 30)
-).filter(_.age > 25) // List(Person("Bob", 30))
+// 类和其他语言的类相似,构造器参数在类名后声明,初始化在类结构体中完成。
+class Dog(br: String) {
+ // 构造器代码在此
+ var breed: String = br
+ // 定义名为 bark 的方法,返回字符串
+ def bark = "Woof, woof!"
-// Scala 的 foreach 方法定义在特定的接受一个类型的集合上
-// 返回 Unit(一个 void 方法)
-aListOfNumbers foreach (x => println(x))
-aListOfNumbers foreach println
+ // 值和方法作用域假定为 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!"
-// For 包含
+// "object" 关键字创造一种类型和该类型的单例。
+// Scala 的 class 常常也含有一个 “伴生对象”,class 中包含每个实例的行为,所有实例
+// 共用的行为则放入 object 中。两者的区别和其他语言中类方法和静态方法类似。
+// 请注意 object 和 class 可以同名。
+object Dog {
+ def allKnownBreeds = List("pitbull", "shepherd", "retriever")
+ def createDog(breed: String) = new Dog(breed)
+}
-for { n <- s } yield sq(n)
-val nSquared2 = for { n <- s } yield sq(n)
+// Case 类是有额外内建功能的类。Scala 初学者常遇到的问题之一便是何时用类
+// 和何时用 case 类。界线比较模糊,但通常类倾向于封装,多态和行为。类中的值
+// 的作用域一般为 private , 只有方向是暴露的。case 类的主要目的是放置不可变
+// 数据。它们通常只有几个方法,且方法几乎没有副作用。
+case class Person(name: String, phoneNumber: String)
-for { n <- nSquared2 if n < 10 } yield n
+// 创造新实例,注意 case 类不需要使用 "new" 关键字
+val george = Person("George", "1234")
+val kate = Person("Kate", "4567")
-for { n <- s; nSquared = n * n if nSquared < 10} yield nSquared
+// 使用 case 类,您可以轻松得到一些功能,像 getters:
+george.phoneNumber // => "1234"
-/* 注意:这些不是 for 循环. 一个 for 循环的语义是 '重复'('repeat'),
- 然而,一个 for-包含 定义了一个两个数据结合间的关系 */
+// 每个字段的相等性比较(无需覆盖 .equals)
+Person("George", "1234") == Person("Kate", "1236") // => false
+// 简单的拷贝方式
+// otherGeorge == Person("george", "9876")
+val otherGeorge = george.copy(phoneNumber = "9876")
+// 还有很多。case 类同时可以用于模式匹配,接下来会看到。
-// 循环和迭代
-1 to 5
-val r = 1 to 5
-r.foreach( println )
+// 敬请期待 Traits !
-r foreach println
-// 注意:Scala 是相当宽容的当它遇到点和括号 - 分别地学习这些规则。
-// 这帮助你编写读起来像英语的 DSLs 和 APIs
-(5 to 1 by -1) foreach ( println )
+/////////////////////////////////////////////////
+// 6. 模式匹配
+/////////////////////////////////////////////////
-// 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 then 10");
- x += 1
-} while (x < 10)
+// 模式匹配是一个强大和常用的 Scala 特性。这是用模式匹配一个 case 类的例子。
+// 附注:不像其他语言, Scala 的 case 不需要 break, 其他语言中 switch 语句的
+// fall-through 现象不会发生。
-// 在 Scala中,尾递归是一种惯用的执行循环的方式。
-// 递归函数需要显示的返回类型,编译器不能推断出类型。
-// 这里它是 Unit。
-def showNumbersInRange(a:Int, b:Int):Unit = {
- print(a)
- if (a < b)
- showNumbersInRange(a + 1, b)
+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"
-// 条件语句
-
-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")
+ // 匹配类型:
+ case x: Double => "Got a Double: " + x
-println(if (x == 10) "yeah" else "nope")
-val text = if (x == 10) "yeah" else "nope"
+ // 匹配时指定条件
+ case x: Int if x > 10000 => "Got a pretty big number!"
-var i = 0
-while (i < 10) { println("i " + i); i+=1 }
+ // 像之前一样匹配 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"
-// 类名是 Dog
-class Dog {
- //bark 方法,返回字符串
- def bark: String = {
- // the body of the method
- "Woof, woof!"
- }
+ // 模式可以嵌套
+ case List(List((1, 2,"YAY"))) => "Got a list of list of tuple"
}
-// 类可以包含几乎其它的构造,包括其它的类,
-// 函数,方法,对象,case 类,特性等等。
-
-
+// 事实上,你可以对任何有 "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"
+}
-// Case 类
-case class Person(name:String, phoneNumber:String)
+/////////////////////////////////////////////////
+// 7. 函数式编程
+/////////////////////////////////////////////////
-Person("George", "1234") == Person("Kate", "1236")
+// 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 me = Person("George", "1234")
-me match { case Person(name, number) => {
- "We matched someone : " + name + ", phone : " + number }}
+// 组合子
-me match { case Person(name, number) => "Match : " + name; case _ => "Hm..." }
+// 译注: val sq: Int => Int = x => x * x
+s.map(sq)
-me match { case Person("George", number) => "Match"; case _ => "Hm..." }
+val sSquared = s. map(sq)
-me match { case Person("Kate", number) => "Match"; case _ => "Hm..." }
+sSquared.filter(_ < 10)
-me match { case Person("Kate", _) => "Girl"; case Person("George", _) => "Boy" }
+sSquared.reduce (_+_)
-val kate = Person("Kate", "1234")
+// 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))
-kate match { case Person("Kate", _) => "Girl"; case Person("George", _) => "Boy" }
+// 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 email = "(.*)@(.*)".r // 在字符串上调用 r 会使它变成一个正则表达式
+val nSquared2 = for { n <- s } yield sq(n)
-val email(user, domain) = "henry@zkpr.com"
+for { n <- nSquared2 if n < 10 } yield n
-"mrbean@pyahoo.com" match {
- case email(name, domain) => "I know your name, " + name
-}
+for { n <- s; nSquared = n * n if nSquared < 10} yield nSquared
+/* 注意,这些不是 for 循环,for 循环的语义是‘重复’,然而 for 推导式定义
+ 两个数据集合的关系。 */
-// 字符串
+/////////////////////////////////////////////////
+// 8. 隐式转换
+/////////////////////////////////////////////////
-"Scala 字符串被双引号包围" //
-'a' // Scala 字符
-'单引号的字符串不存在' // 错误
-"字符串拥有通常的 Java 方法定义在其上".length
-"字符串也有额外的 Scala 方法".reverse
+/* 警告 警告: 隐式转换是 Scala 中一套强大的特性,因此容易被滥用。
+ * Scala 初学者在理解它们的工作原理和最佳实践之前,应抵制使用它的诱惑。
+ * 我们加入这一章节仅因为它们在 Scala 的库中太过常见,导致没有用隐式转换的库
+ * 就不可能做有意义的事情。这章节主要让你理解和使用隐式转换,而不是自己声明。
+ */
-// 参见: scala.collection.immutable.StringOps
+// 可以通过 "implicit" 声明任何值(val, 函数,对象等)为隐式值,
+// 请注意这些例子中,我们用到第5部分的 Dog 类。
+implicit val myImplicitInt = 100
+implicit def myImplicitFunction(breed: String) = new Dog("Golden " + breed)
-println("ABCDEF".length)
-println("ABCDEF".substring(2, 6))
-println("ABCDEF".replace("C", "3"))
+// implicit 关键字本身不改变值的行为,所以上面的值可以照常使用。
+myImplicitInt + 2 // => 102
+myImplicitFunction("Pitbull").breed // => "Golden Pitbull"
-val n = 45
-println(s"We have $n apples")
+// 区别在于,当另一段代码“需要”隐式值时,这些值现在有资格作为隐式值。
+// 一种情况是隐式函数参数。
+def sendGreetings(toWhom: String)(implicit howMany: Int) =
+ s"Hello $toWhom, $howMany blessings to you and yours!"
-val a = Array(11, 9, 6)
-println(s"My second daughter is ${a(2-1)} years old")
+// 如果提供值给 “howMany”,函数正常运行
+sendGreetings("John")(1000) // => "Hello John, 1000 blessings to you and yours!"
-// 一些字符需要被转义,举例来说,字符串中的双引号:
-val a = "They stood outside the \"Rose and Crown\""
+// 如果省略隐式参数,会传一个和参数类型相同的隐式值,
+// 在这个例子中, 是 “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] = ...
-val html = """<form id="daform">
- <p>Press belo', Joe</p>
- | <input type="submit">
- </form>"""
+// 编译器寻找隐式值另一种情况是你调用方法时
+// obj.method(...)
+// 但 "obj" 没有一个名为 "method" 的方法。这样的话,如果有一个参数类型为 A
+// 返回值类型为 B 的隐式转换,obj 的类型是 A,B 有一个方法叫 "method" ,这样
+// 转换就会被应用。所以作用域里有上面的 myImplicitFunction, 我们可以这样做:
+"Retriever".breed // => "Golden Retriever"
+"Sheperd".bark // => "Woof, woof!"
+// 这里字符串先被上面的函数转换为 Dog 对象,然后调用相应的方法。
+// 这是相当强大的特性,但再次提醒,请勿轻率使用。
+// 事实上,当你定义上面的隐式函数时,编译器会作出警告,除非你真的了解
+// 你正在做什么否则不要使用。
-// 应用结果和组织
+/////////////////////////////////////////////////
+// 9. 杂项
+/////////////////////////////////////////////////
-// import
+// 导入类
import scala.collection.immutable.List
-// Import 所有的子包
+// 导入所有子包
import scala.collection.immutable._
-// 在一条语句中 Import 多个类
+// 一条语句导入多个类
import scala.collection.immutable.{List, Map}
-// 使用 '=>' 来重命名一个 import
+// 使用 ‘=>’ 对导入进行重命名
import scala.collection.immutable.{ List => ImmutableList }
-// import 除了一些类的其它所有的类。下面的例子除去了 Map 类和 Set 类:
+// 导入所有类,排除其中一些。下面的语句排除了 Map 和 Set:
import scala.collection.immutable.{Map => _, Set => _, _}
-// 在 scala 源文件中,你的程序入口点使用一个拥有单一方法 main 的对象来定义:
-
+// 在 Scala 文件用 object 和单一的 main 方法定义程序入口:
object Application {
def main(args: Array[String]): Unit = {
// stuff goes here.
}
}
-// 文件可以包含多个类和对象。由 scalac 来编译
+// 文件可以包含多个 class 和 object,用 scalac 编译源文件
// 输入和输出
-// 一行一行读取文件
+// 按行读文件
import scala.io.Source
-for(line <- Source.fromPath("myfile.txt").getLines())
+for(line <- Source.fromFile("myfile.txt").getLines())
println(line)
-// 使用 Java 的 PrintWriter 来写文件
-
+// 用 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()
```
diff --git a/zh-cn/swift-cn.html.markdown b/zh-cn/swift-cn.html.markdown
index c87f9d10..28001e3f 100644
--- a/zh-cn/swift-cn.html.markdown
+++ b/zh-cn/swift-cn.html.markdown
@@ -5,223 +5,587 @@ contributors:
- ["Grant Timmerman", "http://github.com/grant"]
translators:
- ["Xavier Yao", "http://github.com/xavieryao"]
+ - ["Joey Huang", "http://github.com/kamidox"]
lang: zh-cn
---
-Swift 是Apple 开发的用于iOS 和OS X 开发的编程语言。Swift 于2014年Apple WWDC (全球开发者大会)中被引入,用以与Objective-C 共存,同时对错误代码更具弹性。Swift 由Xcode 6 beta 中包含的LLVM编译器编译。
+Swift 是 Apple 开发的用于 iOS 和 OS X 开发的编程语言。Swift 于2014年 Apple WWDC (全球开发者大会)中被引入,用以与 Objective-C 共存,同时对错误代码更具弹性。Swift 由 Xcode 6 beta 中包含的 LLVM 编译器编译。
-参阅:Apple's [getting started guide](https://developer.apple.com/library/prerelease/ios/referencelibrary/GettingStarted/LandingPage/index.html) ——一个完整的Swift 教程
+Swift 的官方语言教程 [Swift Programming Language](https://itunes.apple.com/us/book/swift-programming-language/id881256329) 可以从 iBooks 免费下载.
-```js
-//
-// 基础
+亦可参阅:Apple's [getting started guide](https://developer.apple.com/library/prerelease/ios/referencelibrary/GettingStarted/RoadMapiOS/index.html) ——一个完整的Swift 教程
+
+```swift
+// 导入外部模块
+import UIKit
+
+//
+// MARK: 基础
//
+// XCODE 支持给注释代码作标记,这些标记会列在 XCODE 的跳转栏里,支持的标记为
+// MARK: 普通标记
+// TODO: TODO 标记
+// FIXME: FIXME 标记
+
println("Hello, world")
+
+// 变量 (var) 的值设置后可以随意改变
+// 常量 (let) 的值设置后不能改变
var myVariable = 42
+let øπΩ = "value" // 可以支持 unicode 变量名
+let π = 3.1415926
let myConstant = 3.1415926
-let explicitDouble: Double = 70
-let label = "some text " + String(myVariable) // Casting
-let piText = "Pi = \(myConstant)" // String interpolation
-var optionalString: String? = "optional" // Can be nil
-optionalString = nil
+let explicitDouble: Double = 70 // 明确指定变量类型为 Double ,否则编译器将自动推断变量类型
+let weak = "keyword"; let override = "another keyword" // 语句之间可以用分号隔开,语句未尾不需要分号
+let intValue = 0007 // 7
+let largeIntValue = 77_000 // 77000
+let label = "some text " + String(myVariable) // 类型转换
+let piText = "Pi = \(π), Pi 2 = \(π * 2)" // 格式化字符串
+
+// 条件编译
+// 使用 -D 定义编译开关
+#if false
+ println("Not printed")
+ let buildValue = 3
+#else
+ let buildValue = 7
+#endif
+println("Build value: \(buildValue)") // Build value: 7
+
+/*
+ Optionals 是 Swift 的新特性,它允许你存储两种状态的值给 Optional 变量:有效值或 None
+
+ Swift 要求所有的 Optinal 属性都必须有明确的值,如果为空,则必须明确设定为 nil
+
+ Optional<T> 是个枚举类型
+*/
+var someOptionalString: String? = "optional" // 可以是 nil
+// 下面的语句和上面完全等价,上面的写法更推荐,因为它更简洁,问号 (?) 是 Swift 提供的语法糖
+var someOptionalString2: Optional<String> = "optional"
+
+if someOptionalString != nil {
+ // 变量不为空
+ if someOptionalString!.hasPrefix("opt") {
+ println("has the prefix")
+ }
+
+ let empty = someOptionalString?.isEmpty
+}
+someOptionalString = nil
+
+// 显式解包 optional 变量
+var unwrappedString: String! = "Value is expected."
+// 下面语句和上面完全等价,感叹号 (!) 是个后缀运算符,这也是个语法糖
+var unwrappedString2: ImplicitlyUnwrappedOptional<String> = "Value is expected."
+
+if let someOptionalStringConstant = someOptionalString {
+ // 由于变量 someOptinalString 有值,不为空,所以 if 条件为真
+ if !someOptionalStringConstant.hasPrefix("ok") {
+ // does not have the prefix
+ }
+}
+
+// Swift 支持可保存任何数据类型的变量
+// AnyObject == id
+// 和 Objective-C `id` 不一样, AnyObject 可以保存任何类型的值 (Class, Int, struct, 等)
+var anyObjectVar: AnyObject = 7
+anyObjectVar = "Changed value to a string, not good practice, but possible."
+
+/*
+ 这里是注释
+
+ /*
+ 支持嵌套的注释
+ */
+*/
//
-// 数组与字典(关联数组)
+// Mark: 数组与字典(关联数组)
//
-// 数组
+/*
+ Array 和 Dictionary 是结构体,不是类,他们作为函数参数时,是用值传递而不是指针传递。
+ 可以用 `var` 和 `let` 来定义变量和常量。
+*/
+
+// Array
var shoppingList = ["catfish", "water", "lemons"]
shoppingList[1] = "bottle of water"
-let emptyArray = String[]()
+let emptyArray = [String]() // 使用 let 定义常量,此时 emptyArray 数组不能添加或删除内容
+let emptyArray2 = Array<String>() // 与上一语句等价,上一语句更常用
+var emptyMutableArray = [String]() // 使用 var 定义变量,可以向 emptyMutableArray 添加数组元素
// 字典
var occupations = [
- "Malcolm": "Captain",
- "kaylee": "Mechanic"
+ "Malcolm": "Captain",
+ "kaylee": "Mechanic"
]
-occupations["Jayne"] = "Public Relations"
-let emptyDictionary = Dictionary<String, Float>()
+occupations["Jayne"] = "Public Relations" // 修改字典,如果 key 不存在,自动添加一个字典元素
+let emptyDictionary = [String: Float]() // 使用 let 定义字典常量,字典常量不能修改里面的值
+let emptyDictionary2 = Dictionary<String, Float>() // 与上一语句类型等价,上一语句更常用
+var emptyMutableDictionary = [String: Float]() // 使用 var 定义字典变量
//
-// 控制流
+// MARK: 控制流
//
-// 用于数组的for 循环
+// 数组的 for 循环
let myArray = [1, 1, 2, 3, 5]
for value in myArray {
- if value == 1 {
- println("One!")
- } else {
- println("Not one!")
- }
+ if value == 1 {
+ println("One!")
+ } else {
+ println("Not one!")
+ }
}
-// 用于字典的for 循环
+// 字典的 for 循环
+var dict = ["one": 1, "two": 2]
for (key, value) in dict {
- println("\(key): \(value)")
+ println("\(key): \(value)")
}
-// 用于区间的for 循环
-for i in -1...1 { // [-1, 0, 1]
- println(i)
+// 区间的 loop 循环:其中 `...` 表示闭环区间,即[-1, 3];`..<` 表示半开闭区间,即[-1,3)
+for i in -1...shoppingList.count {
+ println(i)
}
-// 使用 .. 表示的区间不包含最后一个元素 [-1,0,1)
+shoppingList[1...2] = ["steak", "peacons"]
+// 可以使用 `..<` 来去掉最后一个元素
// while 循环
var i = 1
while i < 1000 {
- i *= 2
+ i *= 2
}
// do-while 循环
do {
- println("hello")
+ println("hello")
} while 1 == 2
-// Switch
+// Switch 语句
+// Swift 里的 Switch 语句功能异常强大,结合枚举类型,可以实现非常简洁的代码,可以把 switch 语句想象成 `if` 的语法糖
+// 它支持字符串,类实例或原生数据类型 (Int, Double, etc)
let vegetable = "red pepper"
switch vegetable {
case "celery":
- let vegetableComment = "Add some raisins and make ants on a log."
+ let vegetableComment = "Add some raisins and make ants on a log."
case "cucumber", "watercress":
- let vegetableComment = "That would make a good tea sandwich."
-case let x where x.hasSuffix("pepper"):
- let vegetableComment = "Is it a spicy \(x)?"
-default: // 必须 (为了覆盖所有可能的输入)
- let vegetableComment = "Everything tastes good in soup."
+ let vegetableComment = "That would make a good tea sandwich."
+case let localScopeValue where localScopeValue.hasSuffix("pepper"):
+ let vegetableComment = "Is it a spicy \(localScopeValue)?"
+default: // 在 Swift 里,switch 语句的 case 必须处理所有可能的情况,如果 case 无法全部处理,则必须包含 default语句
+ let vegetableComment = "Everything tastes good in soup."
}
//
-// 函数
+// MARK: 函数
//
-// 函数是一等类型,这意味着可以在函数中构建函数
-// 并且可以被传递
+// 函数是一个 first-class 类型,他们可以嵌套,可以作为函数参数传递
-// 函数
+// 函数文档可使用 reStructedText 格式直接写在函数的头部
+/**
+ A greet operation
+
+ - A bullet in docs
+ - Another bullet in the docs
+
+ :param: name A name
+ :param: day A day
+ :returns: A string containing the name and day value.
+*/
func greet(name: String, day: String) -> String {
- return "Hello \(name), today is \(day)."
+ return "Hello \(name), today is \(day)."
}
greet("Bob", "Tuesday")
-// 使用多元数组返回多返回值的函数
-func getGasPrices() -> (Double, Double, Double) {
- return (3.59, 3.69, 3.79)
+// 函数参数前带 `#` 表示外部参数名和内部参数名使用同一个名称。
+// 第二个参数表示外部参数名使用 `externalParamName` ,内部参数名使用 `localParamName`
+func greet2(#requiredName: String, externalParamName localParamName: String) -> String {
+ return "Hello \(requiredName), the day is \(localParamName)"
}
+greet2(requiredName:"John", externalParamName: "Sunday") // 调用时,使用命名参数来指定参数的值
-// 不定参数
-func setup(numbers: Int...) {}
+// 函数可以通过元组 (tuple) 返回多个值
+func getGasPrices() -> (Double, Double, Double) {
+ return (3.59, 3.69, 3.79)
+}
+let pricesTuple = getGasPrices()
+let price = pricesTuple.2 // 3.79
+// 通过下划线 (_) 来忽略不关心的值
+let (_, price1, _) = pricesTuple // price1 == 3.69
+println(price1 == pricesTuple.1) // true
+println("Gas price: \(price)")
+
+// 可变参数
+func setup(numbers: Int...) {
+ // 可变参数是个数组
+ let number = numbers[0]
+ let argCount = numbers.count
+}
-// 传递、返回函数
+// 函数变量以及函数作为返回值返回
func makeIncrementer() -> (Int -> Int) {
- func addOne(number: Int) -> Int {
- return 1 + number
- }
- return addOne
+ func addOne(number: Int) -> Int {
+ return 1 + number
+ }
+ return addOne
}
var increment = makeIncrementer()
increment(7)
+// 强制进行指针传递 (引用传递),使用 `inout` 关键字修饰函数参数
+func swapTwoInts(inout a: Int, inout b: Int) {
+ let tempA = a
+ a = b
+ b = tempA
+}
+var someIntA = 7
+var someIntB = 3
+swapTwoInts(&someIntA, &someIntB)
+println(someIntB) // 7
+
//
-// 闭包
+// MARK: 闭包
//
+var numbers = [1, 2, 6]
-// 函数是特殊的闭包({})
+// 函数是闭包的一个特例
-// 闭包示例.
-// `->` 分隔参数和返回类型
-// `in` 分隔闭包头和闭包体
+// 闭包实例
+// `->` 分隔了闭包的参数和返回值
+// `in` 分隔了闭包头 (包括参数及返回值) 和闭包体
+// 下面例子中,`map` 的参数是一个函数类型,它的功能是把数组里的元素作为参数,逐个调用 `map` 参数传递进来的函数。
numbers.map({
- (number: Int) -> Int in
- let result = 3 * number
- return result
- })
+ (number: Int) -> Int in
+ let result = 3 * number
+ return result
+})
-// 当类型已知时,可以这样做:
-var numbers = [1, 2, 6]
+// 当闭包的参数类型和返回值都是己知的情况下,且只有一个语句作为其返回值时,我们可以简化闭包的写法
numbers = numbers.map({ number in 3 * number })
+// 我们也可以使用 $0, $1 来指代第 1 个,第 2 个参数,上面的语句最终可简写为如下形式
+// numbers = numbers.map({ $0 * 3 })
+
+print(numbers) // [3, 6, 18]
+
+// 简洁的闭包
+numbers = sorted(numbers) { $0 > $1 }
+// 函数的最后一个参数可以放在括号之外,上面的语句是这个语句的简写形式
+// numbers = sorted(numbers, { $0 > $1 })
+
+print(numbers) // [18, 6, 3]
+
+// 超级简洁的闭包,因为 `<` 是个操作符函数
+numbers = sorted(numbers, < )
+
print(numbers) // [3, 6, 18]
//
-// 类
+// MARK: 结构体
//
-// 类的全部方法和属性都是public 的
-// 如果你在一个数据结构中只需储存数据,
-// 应使用 `struct`
+// 结构体和类非常类似,可以有属性和方法
-// 集成自`Shape` 类的简单的类`Square
-class Rect: Shape {
- var sideLength: Int = 1
-
- // Custom getter and setter property
- var perimeter: Int {
- get {
- return 4 * sideLength
- }
- set {
- sideLength = newValue / 4
+struct NamesTable {
+ let names = [String]()
+
+ // 自定义下标运算符
+ subscript(index: Int) -> String {
+ return names[index]
}
- }
+}
+
+// 结构体有一个自动生成的隐含的命名构造函数
+let namesTable = NamesTable(names: ["Me", "Them"])
+let name = namesTable[1]
+println("Name is \(name)") // Name is Them
+
+//
+// MARK: 类
+//
- init(sideLength: Int) {
- super.init()
- self.sideLength = sideLength
- }
+// 类和结构体的有三个访问控制级别,他们分别是 internal (默认), public, private
+// internal: 模块内部可以访问
+// public: 其他模块可以访问
+// private: 只有定义这个类或结构体的源文件才能访问
- func shrink() {
- if sideLength > 0 {
- --sideLength
+public class Shape {
+ public func getArea() -> Int {
+ return 0;
+ }
+}
+
+// 类的所有方法和属性都是 public 的
+// 如果你只是需要把数据保存在一个结构化的实例里面,应该用结构体
+
+internal class Rect: Shape {
+ // 值属性 (Stored properties)
+ var sideLength: Int = 1
+
+ // 计算属性 (Computed properties)
+ private var perimeter: Int {
+ get {
+ return 4 * sideLength
+ }
+ set {
+ // `newValue` 是个隐含的变量,它表示将要设置进来的新值
+ sideLength = newValue / 4
+ }
+ }
+
+ // 延时加载的属性,只有这个属性第一次被引用时才进行初始化,而不是定义时就初始化
+ // subShape 值为 nil ,直到 subShape 第一次被引用时才初始化为一个 Rect 实例
+ lazy var subShape = Rect(sideLength: 4)
+
+ // 监控属性值的变化。
+ // 当我们需要在属性值改变时做一些事情,可以使用 `willSet` 和 `didSet` 来设置监控函数
+ // `willSet`: 值改变之前被调用
+ // `didSet`: 值改变之后被调用
+ var identifier: String = "defaultID" {
+ // `willSet` 的参数是即将设置的新值,参数名可以指定,如果没有指定,就是 `newValue`
+ willSet(someIdentifier) {
+ println(someIdentifier)
+ }
+ // `didSet` 的参数是已经被覆盖掉的旧的值,参数名也可以指定,如果没有指定,就是 `oldValue`
+ didSet {
+ println(oldValue)
+ }
+ }
+
+ // 命名构造函数 (designated inits),它必须初始化所有的成员变量,
+ // 然后调用父类的命名构造函数继续初始化父类的所有变量。
+ init(sideLength: Int) {
+ self.sideLength = sideLength
+ // 必须显式地在构造函数最后调用父类的构造函数 super.init
+ super.init()
+ }
+
+ func shrink() {
+ if sideLength > 0 {
+ --sideLength
+ }
}
- }
+
+ // 函数重载使用 override 关键字
+ override func getArea() -> Int {
+ return sideLength * sideLength
+ }
+}
- override func getArea() -> Int {
- return sideLength * sideLength
- }
+// 类 `Square` 从 `Rect` 继承
+class Square: Rect {
+ // 便捷构造函数 (convenience inits) 是调用自己的命名构造函数 (designated inits) 的构造函数
+ // Square 自动继承了父类的命名构造函数
+ convenience init() {
+ self.init(sideLength: 5)
+ }
+ // 关于构造函数的继承,有以下几个规则:
+ // 1. 如果你没有实现任何命名构造函数,那么你就继承了父类的所有命名构造函数
+ // 2. 如果你重载了父类的所有命名构造函数,那么你就自动继承了所有的父类快捷构造函数
+ // 3. 如果你没有实现任何构造函数,那么你继承了父类的所有构造函数,包括命名构造函数和便捷构造函数
}
-var mySquare = new Square(sideLength: 5)
-print(mySquare.getArea()) // 25
+
+var mySquare = Square()
+println(mySquare.getArea()) // 25
mySquare.shrink()
-print(mySquare.sideLength) // 4
+println(mySquare.sideLength) // 4
+
+// 类型转换
+let aShape = mySquare as Shape
+
+// 使用三个等号来比较是不是同一个实例
+if mySquare === aShape {
+ println("Yep, it's mySquare")
+}
+
+class Circle: Shape {
+ var radius: Int
+ override func getArea() -> Int {
+ return 3 * radius * radius
+ }
+
+ // optional 构造函数,可能会返回 nil
+ init?(radius: Int) {
+ self.radius = radius
+ super.init()
+
+ if radius <= 0 {
+ return nil
+ }
+ }
+}
-// 如果你不需要自定义getter 和setter,
-// 但仍希望在获取或设置一个属性之前或之后运行
-// 一些代码,你可以使用`willSet` 和 `didSet`
+// 根据 Swift 类型推断,myCircle 是 Optional<Circle> 类型的变量
+var myCircle = Circle(radius: 1)
+println(myCircle?.getArea()) // Optional(3)
+println(myCircle!.getArea()) // 3
+var myEmptyCircle = Circle(radius: -1)
+println(myEmptyCircle?.getArea()) // "nil"
+if let circle = myEmptyCircle {
+ // 此语句不会输出,因为 myEmptyCircle 变量值为 nil
+ println("circle is not nil")
+}
//
-// 枚举类型
+// MARK: 枚举
//
-// 枚举类型可以是某种指定的类型,抑或自成一种类型
-// 像类一样,枚举类型可以包含方法
+// 枚举可以像类一样,拥有方法
enum Suit {
- case Spades, Hearts, Diamonds, Clubs
- func getIcon() -> String {
- switch self {
- case .Spades: return "♤"
- case .Hearts: return "♡"
- case .Diamonds: return "♢"
- case .Clubs: return "♧"
+ case Spades, Hearts, Diamonds, Clubs
+ func getIcon() -> String {
+ switch self {
+ case .Spades: return "♤"
+ case .Hearts: return "♡"
+ case .Diamonds: return "♢"
+ case .Clubs: return "♧"
+ }
}
- }
}
+// 当变量类型明确指定为某个枚举类型时,赋值时可以省略枚举类型
+var suitValue: Suit = .Hearts
+
+// 非整型的枚举类型需要在定义时赋值
+enum BookName: String {
+ case John = "John"
+ case Luke = "Luke"
+}
+println("Name: \(BookName.John.rawValue)")
+
+// 与特定数据类型关联的枚举
+enum Furniture {
+ // 和 Int 型数据关联的枚举记录
+ case Desk(height: Int)
+ // 和 String, Int 关联的枚举记录
+ case Chair(brand: String, height: Int)
+
+ func description() -> String {
+ switch self {
+ case .Desk(let height):
+ return "Desk with \(height) cm"
+ case .Chair(let brand, let height):
+ return "Chair of \(brand) with \(height) cm"
+ }
+ }
+}
+
+var desk: Furniture = .Desk(height: 80)
+println(desk.description()) // "Desk with 80 cm"
+var chair = Furniture.Chair(brand: "Foo", height: 40)
+println(chair.description()) // "Chair of Foo with 40 cm"
+
//
-// 其它
+// MARK: 协议
+// 与 Java 的 interface 类似
//
-// `协议(protocol)`: 与Java 的接口(Interface) 类似.
-// `扩展(extension)`: 为现有类型添加额外特性
-// 泛型: 与Java 相似。使用`where` 关键字指定
-// 泛型的要求.
+// 协议可以让遵循同一协议的类型实例拥有相同的属性,方法,类方法,操作符或下标运算符等
+// 下面代码定义一个协议,这个协议包含一个名为 enabled 的计算属性且包含 buildShape 方法
+protocol ShapeGenerator {
+ var enabled: Bool { get set }
+ func buildShape() -> Shape
+}
+
+// 协议声明时可以添加 @objc 前缀,添加 @objc 前缀后,
+// 可以使用 is, as, as? 等来检查协议兼容性
+// 需要注意,添加 @objc 前缀后,协议就只能被类来实现,
+// 结构体和枚举不能实现加了 @objc 的前缀
+// 只有添加了 @objc 前缀的协议才能声明 optional 方法
+// 一个类实现一个带 optional 方法的协议时,可以实现或不实现这个方法
+// optional 方法可以使用 optional 规则来调用
+@objc protocol TransformShape {
+ optional func reshaped()
+ optional func canReshape() -> Bool
+}
+
+class MyShape: Rect {
+ var delegate: TransformShape?
+
+ func grow() {
+ sideLength += 2
+
+ // 在 optional 属性,方法或下标运算符后面加一个问号,可以优雅地忽略 nil 值,返回 nil。
+ // 这样就不会引起运行时错误 (runtime error)
+ if let allow = self.delegate?.canReshape?() {
+ // 注意语句中的问号
+ self.delegate?.reshaped?()
+ }
+ }
+}
+
+
+//
+// MARK: 其它
+//
+
+// 扩展: 给一个已经存在的数据类型添加功能
+
+// 给 Square 类添加 `Printable` 协议的实现,现在其支持 `Printable` 协议
+extension Square: Printable {
+ var description: String {
+ return "Area: \(self.getArea()) - ID: \(self.identifier)"
+ }
+}
+
+println("Square: \(mySquare)") // Area: 16 - ID: defaultID
+
+// 也可以给系统内置类型添加功能支持
+extension Int {
+ var customProperty: String {
+ return "This is \(self)"
+ }
+
+ func multiplyBy(num: Int) -> Int {
+ return num * self
+ }
+}
+
+println(7.customProperty) // "This is 7"
+println(14.multiplyBy(3)) // 42
+
+// 泛型: 和 Java 及 C# 的泛型类似,使用 `where` 关键字来限制类型。
+// 如果只有一个类型限制,可以省略 `where` 关键字
+func findIndex<T: Equatable>(array: [T], valueToFind: T) -> Int? {
+ for (index, value) in enumerate(array) {
+ if value == valueToFind {
+ return index
+ }
+ }
+ return nil
+}
+let foundAtIndex = findIndex([1, 2, 3, 4], 3)
+println(foundAtIndex == 2) // true
+
+// 自定义运算符:
+// 自定义运算符可以以下面的字符打头:
+// / = - + * % < > ! & | ^ . ~
+// 甚至是 Unicode 的数学运算符等
+prefix operator !!! {}
+
+// 定义一个前缀运算符,使矩形的边长放大三倍
+prefix func !!! (inout shape: Square) -> Square {
+ shape.sideLength *= 3
+ return shape
+}
+
+// 当前值
+println(mySquare.sideLength) // 4
+
+// 使用自定义的 !!! 运算符来把矩形边长放大三倍
+!!!mySquare
+println(mySquare.sideLength) // 12
```
+