diff options
Diffstat (limited to 'zh-cn')
24 files changed, 3832 insertions, 731 deletions
diff --git a/zh-cn/awk-cn.html.markdown b/zh-cn/awk-cn.html.markdown new file mode 100644 index 00000000..8ee2db2c --- /dev/null +++ b/zh-cn/awk-cn.html.markdown @@ -0,0 +1,327 @@ +--- +category: tool +tool: awk +contributors: + - ["Marshall Mason", "http://github.com/marshallmason"] +translators: + - ["Tian Zhipeng", "https://github.com/tianzhipeng-git"] +filename: learnawk-cn.awk +lang: zh-cn +--- + +AWK是POSIX兼容的UNIX系统中的标准工具. 它像简化版的Perl, 非常适用于文本处理任务和其他脚本类需求. +它有着C风格的语法, 但是没有分号, 没有手动内存管理, 没有静态类型. +他擅长于文本处理, 你可以通过shell脚本调用AWK, 也可以用作独立的脚本语言. + +为什么使用AWK而不是Perl, 大概是因为AWK是UNIX的一部分, 你总能依靠它, 而Perl已经前途未卜了. +AWK比Perl更易读. 对于简单的文本处理脚本, 特别是按行读取文件, 按分隔符分隔处理, AWK极可能是正确的工具. + +```awk +#!/usr/bin/awk -f + +# 注释使用井号 + +# AWK程序由一系列 模式(patterns) 和 动作(actions) 组成. +# 最重要的模式叫做 BEGIN. 动作由大括号包围. +BEGIN { + + # BEGIN在程序最开始运行. 在这里放一些在真正处理文件之前的准备和setup的代码. + # 如果没有文本文件要处理, 那就把BEGIN作为程序的主入口吧. + + # 变量是全局的. 直接赋值使用即可, 无需声明. + count = 0 + + # 运算符和C语言系一样 + a = count + 1 + b = count - 1 + c = count * 1 + d = count / 1 # 整数除法 + e = count % 1 # 取余 + f = count ^ 1 # 取幂 + + a += 1 + b -= 1 + c *= 1 + d /= 1 + e %= 1 + f ^= 1 + + # 自增1, 自减1 + a++ + b-- + + # 前置运算, 返回增加之后的值 + ++a + --b + + # 注意, 不需要分号之类的标点来分隔语句 + + # 控制语句 + if (count == 0) + print "Starting with count of 0" + else + print "Huh?" + + # 或者三目运算符 + print (count == 0) ? "Starting with count of 0" : "Huh?" + + # 多行的代码块用大括号包围 + while (a < 10) { + print "String concatenation is done" " with a series" " of" + " space-separated strings" + print a + + a++ + } + + for (i = 0; i < 10; i++) + print "Good ol' for loop" + + # 标准的比较运算符 + a < b # 小于 + a <= b # 小于或等于 + a != b # 不等于 + a == b # 等于 + a > b # 大于 + a >= b # 大于或等于 + + # 也有逻辑运算符 + a && b # 且 + a || b # 或 + + # 并且有超实用的正则表达式匹配 + if ("foo" ~ "^fo+$") + print "Fooey!" + if ("boo" !~ "^fo+$") + print "Boo!" + + # 数组 + arr[0] = "foo" + arr[1] = "bar" + # 不幸的是, 没有其他方式初始化数组. 必须像这样一行一行的赋值. + + # 关联数组, 类似map或dict的用法. + assoc["foo"] = "bar" + assoc["bar"] = "baz" + + # 多维数组. 但是有一些局限性这里不提了. + multidim[0,0] = "foo" + multidim[0,1] = "bar" + multidim[1,0] = "baz" + multidim[1,1] = "boo" + + # 可以检测数组包含关系 + if ("foo" in assoc) + print "Fooey!" + + # 可以使用in遍历数组 + for (key in assoc) + print assoc[key] + + # 命令行参数是一个叫ARGV的数组 + for (argnum in ARGV) + print ARGV[argnum] + + # 可以从数组中移除元素 + # 在 防止awk把文件参数当做数据来处理 时delete功能很有用. + delete ARGV[1] + + # 命令行参数的个数是一个叫ARGC的变量 + print ARGC + + # AWK有很多内置函数, 分为三类, 会在接下来定义的各个函数中介绍. + + return_value = arithmetic_functions(a, b, c) + string_functions() + io_functions() +} + +# 定义函数 +function arithmetic_functions(a, b, c, d) { + + # 或许AWK最让人恼火的地方是没有局部变量, 所有东西都是全局的, + # 对于短的脚本还好, 对于长一些的就会成问题. + + # 这里有一个技巧, 函数参数是对函数局部可见的, 并且AWK允许定义多余的参数, + # 因此可以像上面那样把局部变量插入到函数声明中. + # 为了方便区分普通参数(a,b,c)和局部变量(d), 可以多键入一些空格. + + # 现在介绍数学类函数 + + # 多数AWK实现中包含标准的三角函数 + localvar = sin(a) + localvar = cos(a) + localvar = atan2(a, b) # arc tangent of b / a + + # 对数 + localvar = exp(a) + localvar = log(a) + + # 平方根 + localvar = sqrt(a) + + # 浮点型转为整型 + localvar = int(5.34) # localvar => 5 + + # 随机数 + srand() # 接受随机种子作为参数, 默认使用当天的时间 + localvar = rand() # 0到1之间随机 + + # 函数返回 + return localvar +} + +function string_functions( localvar, arr) { + + # AWK, 作为字符处理语言, 有很多字符串相关函数, 其中大多数都严重依赖正则表达式. + + # 搜索并替换, 第一个出现的 (sub) or 所有的 (gsub) + # 都是返回替换的个数 + localvar = "fooooobar" + sub("fo+", "Meet me at the ", localvar) # localvar => "Meet me at the bar" + gsub("e+", ".", localvar) # localvar => "m..t m. at th. bar" + + # 搜索匹配正则的字符串 + # index() 也是搜索, 不支持正则 + match(localvar, "t") # => 4, 't'在4号位置. + # (译者注: awk是1开始计数的,不是常见的0-base) + + # 按分隔符分隔 + split("foo-bar-baz", arr, "-") # a => ["foo", "bar", "baz"] + + # 其他有用的函数 + sprintf("%s %d %d %d", "Testing", 1, 2, 3) # => "Testing 1 2 3" + substr("foobar", 2, 3) # => "oob" + substr("foobar", 4) # => "bar" + length("foo") # => 3 + tolower("FOO") # => "foo" + toupper("foo") # => "FOO" +} + +function io_functions( localvar) { + + # 你已经见过的print函数 + print "Hello world" + + # 也有printf + printf("%s %d %d %d\n", "Testing", 1, 2, 3) + + # AWK本身没有文件句柄, 当你使用需要文件的东西时会自动打开文件, + # 做文件I/O时, 字符串就是打开的文件句柄. 这看起来像Shell + print "foobar" >"/tmp/foobar.txt" + + # 现在"/tmp/foobar.txt"字符串是一个文件句柄, 你可以关闭它 + close("/tmp/foobar.txt") + + # 在shell里运行一些东西 + system("echo foobar") # => prints foobar + + # 从标准输入中读一行, 并存储在localvar中 + getline localvar + + # 从管道中读一行, 并存储在localvar中 + "echo foobar" | getline localvar # localvar => "foobar" + close("echo foobar") + + # 从文件中读一行, 并存储在localvar中 + getline localvar <"/tmp/foobar.txt" + close("/tmp/foobar.txt") +} + +# 正如开头所说, AWK程序由一系列模式和动作组成. 你已经看见了重要的BEGIN pattern, +# 其他的pattern在你需要处理来自文件或标准输入的的数据行时才用到. +# +# 当你给AWK程序传参数时, 他们会被视为要处理文件的文件名, 按顺序全部会处理. +# 可以把这个过程看做一个隐式的循环, 遍历这些文件中的所有行. +# 然后这些模式和动作就是这个循环里的switch语句一样 + +/^fo+bar$/ { + + # 这个动作会在匹配这个正则(/^fo+bar$/)的每一行上执行. 不匹配的则会跳过. + # 先让我们打印它: + print + + # 哦, 没有参数, 那是因为print有一个默认参数 $0. + # $0 是当前正在处理的行, 自动被创建好了. + + # 你可能猜到有其他的$变量了. + # 每一行在动作执行前会被分隔符分隔. 像shell中一样, 每个字段都可以用$符访问 + + # 这个会打印这行的第2和第4个字段 + print $2, $4 + + # AWK自动定义了许多其他的变量帮助你处理行. 最常用的是NF变量 + # 打印这一行的字段数 + print NF + + # 打印这一行的最后一个字段 + print $NF +} + +# 每一个模式其实是一个true/false判断, 上面那个正则其实也是一个true/false判断, 只不过被部分省略了. +# 没有指定时默认使用当前处理的整行($0)进行匹配. 因此, 完全版本是这样: + +$0 ~ /^fo+bar$/ { + print "Equivalent to the last pattern" +} + +a > 0 { + # 只要a是整数, 这块会在每一行上执行. +} + +# 就是这样, 处理文本文件, 一次读一行, 对行做一些操作. +# 按分隔符分隔, 这在UNIX中很常见, awk都帮你做好了. +# 你所需要做的是基于自己的需求写一些模式和动作. + +# 这里有一个快速的例子, 展示了AWK所擅长做的事. +# 它从标准输入读一个名字, 打印这个first name下所有人的平均年龄. +# 示例数据: +# +# Bob Jones 32 +# Jane Doe 22 +# Steve Stevens 83 +# Bob Smith 29 +# Bob Barker 72 +# +# 示例脚本: + +BEGIN { + + # 首先, 问用户要一个名字 + print "What name would you like the average age for?" + + # 从标准输入获取名字 + getline name <"/dev/stdin" +} + +# 然后, 用给定的名字匹配每一行的第一个字段. +$1 == name { + + # 这里我们要使用几个有用的变量, 已经提前为我们加载好的: + # $0 是整行 + # $3 是第三个字段, 就是我们所感兴趣的年龄 + # NF 字段数, 这里是3 + # NR 至此为止的行数 + # FILENAME 在处理的文件名 + # FS 在使用的字段分隔符, 这里是空格" " + # ...等等, 还有很多, 在帮助文档中列出. + + # 跟踪 总和以及行数 + sum += $3 + nlines++ +} + +# 另一个特殊的模式叫END. 它会在处理完所有行之后运行. 不像BEGIN, 它只会在有输入的时候运行. +# 它在所有文件依据给定的模式和动作处理完后运行, 目的通常是输出一些最终报告, 做一些数据聚合操作. + +END { + if (nlines) + print "The average age for " name " is " sum / nlines +} + +``` +更多: + +* [Awk 教程](http://www.grymoire.com/Unix/Awk.html) +* [Awk 手册](https://linux.die.net/man/1/awk) +* [The GNU Awk 用户指南](https://www.gnu.org/software/gawk/manual/gawk.html) GNU Awk在大多数Linux中预装 diff --git a/zh-cn/c++-cn.html.markdown b/zh-cn/c++-cn.html.markdown index 87951bc3..e0d6b6fe 100644 --- a/zh-cn/c++-cn.html.markdown +++ b/zh-cn/c++-cn.html.markdown @@ -567,6 +567,6 @@ void doSomethingWithAFile(const std::string& filename) ```
扩展阅读:
-<http://cppreference.com/w/cpp> 提供了最新的语法参考。
-
-可以在 <http://cplusplus.com> 找到一些补充资料。
+* [CPP Reference](http://cppreference.com/w/cpp) 提供了最新的语法参考。
+* 可以在 [CPlusPlus](http://cplusplus.com) 找到一些补充资料。
+* 可以在 [TheChernoProject - C ++](https://www.youtube.com/playlist?list=PLlrATfBNZ98dudnM48yfGUldqGD0S4FFb)上找到涵盖语言基础和设置编码环境的教程。
diff --git a/zh-cn/c-cn.html.markdown b/zh-cn/c-cn.html.markdown index 1e10416e..8566e811 100644 --- a/zh-cn/c-cn.html.markdown +++ b/zh-cn/c-cn.html.markdown @@ -41,7 +41,7 @@ enum days {SUN = 1, MON, TUE, WED, THU, FRI, SAT}; void function_1(char c); void function_2(void); -// 如果函数出现在main()之后,那么必须在main()之前 +// 如果函数调用在main()之后,那么必须声明在main()之前 // 先声明一个函数原型 int add_two_ints(int x1, int x2); // 函数原型 diff --git a/zh-cn/clojure-macro-cn.html.markdown b/zh-cn/clojure-macro-cn.html.markdown index 9324841e..23b2f203 100644 --- a/zh-cn/clojure-macro-cn.html.markdown +++ b/zh-cn/clojure-macro-cn.html.markdown @@ -142,11 +142,9 @@ lang: zh-cn ## 扩展阅读 -[Clojure for the Brave and True](http://www.braveclojure.com/)系列的编写宏 -http://www.braveclojure.com/writing-macros/ +[Clojure for the Brave and True](http://www.braveclojure.com/) +[系列的编写宏](http://www.braveclojure.com/writing-macros/) -官方文档 -http://clojure.org/macros +[官方文档](http://clojure.org/macros) -何时使用宏? -http://dunsmor.com/lisp/onlisp/onlisp_12.html +[何时使用宏?](https://lispcast.com/when-to-use-a-macro/) diff --git a/zh-cn/crystal-cn.html.markdown b/zh-cn/crystal-cn.html.markdown new file mode 100644 index 00000000..14805114 --- /dev/null +++ b/zh-cn/crystal-cn.html.markdown @@ -0,0 +1,567 @@ +--- +language: crystal +filename: learncrystal-cn.cr +contributors: + - ["Vitalii Elenhaupt", "http://veelenga.com"] + - ["Arnaud Fernandés", "https://github.com/TechMagister/"] +translators: + - ["Xuty", "https://github.com/xtyxtyx"] +lang: zh-cn +--- + +```crystal + +# 这是一行注释 + +# 一切都是对象(object) +nil.class #=> Nil +100.class #=> Int32 +true.class #=> Bool + +# nil, false 以及空指针是假值(falsey values) +!nil #=> true : Bool +!false #=> true : Bool +!0 #=> false : Bool + +# 整数类型 + +1.class #=> Int32 + +# 四种有符号整数 +1_i8.class #=> Int8 +1_i16.class #=> Int16 +1_i32.class #=> Int32 +1_i64.class #=> Int64 + +# 四种无符号整数 +1_u8.class #=> UInt8 +1_u16.class #=> UInt16 +1_u32.class #=> UInt32 +1_u64.class #=> UInt64 + +2147483648.class #=> Int64 +9223372036854775808.class #=> UInt64 + +# 二进制数 +0b1101 #=> 13 : Int32 + +# 八进制数 +0o123 #=> 83 : Int32 + +# 十六进制数 +0xFE012D #=> 16646445 : Int32 +0xfe012d #=> 16646445 : Int32 + +# 浮点数类型 + +1.0.class #=> Float64 + +# Crystal中有两种浮点数 +1.0_f32.class #=> Float32 +1_f32.class #=> Float32 + +1e10.class #=> Float64 +1.5e10.class #=> Float64 +1.5e-7.class #=> Float64 + +# 字符类型 + +'a'.class #=> Char + +# 八进制字符 +'\101' #=> 'A' : Char + +# Unicode字符 +'\u0041' #=> 'A' : Char + +# 字符串 + +"s".class #=> String + +# 字符串不可变(immutable) +s = "hello, " #=> "hello, " : String +s.object_id #=> 134667712 : UInt64 +s += "Crystal" #=> "hello, Crystal" : String +s.object_id #=> 142528472 : UInt64 + +# 支持字符串插值(interpolation) +"sum = #{1 + 2}" #=> "sum = 3" : String + +# 多行字符串 +"这是一个 + 多行字符串" + +# 书写带有引号的字符串 +%(hello "world") #=> "hello \"world\"" + +# 符号类型 +# 符号是不可变的常量,本质上是Int32类型 +# 符号通常被用来代替字符串,来高效地传递特定的值 + +:symbol.class #=> Symbol + +sentence = :question? # :"question?" : Symbol + +sentence == :question? #=> true : Bool +sentence == :exclamation! #=> false : Bool +sentence == "question?" #=> false : Bool + +# 数组类型(Array) + +[1, 2, 3].class #=> Array(Int32) +[1, "hello", 'x'].class #=> Array(Int32 | String | Char) + +# 必须为空数组指定类型 +[] # Syntax error: for empty arrays use '[] of ElementType' +[] of Int32 #=> [] : Array(Int32) +Array(Int32).new #=> [] : Array(Int32) + +# 数组可以通过下标访问 +array = [1, 2, 3, 4, 5] #=> [1, 2, 3, 4, 5] : Array(Int32) +array[0] #=> 1 : Int32 +array[10] # raises IndexError +array[-6] # raises IndexError +array[10]? #=> nil : (Int32 | Nil) +array[-6]? #=> nil : (Int32 | Nil) + +# 使用负位置编号,从后往前访问数组 +array[-1] #=> 5 + +# With a start index and size +# 使用起始位置编号+大小 +array[2, 3] #=> [3, 4, 5] + +# 使用范围(range)访问数组 +array[1..3] #=> [2, 3, 4] + +# 向尾部添加元素 +array << 6 #=> [1, 2, 3, 4, 5, 6] + +# 删除尾部元素 +array.pop #=> 6 +array #=> [1, 2, 3, 4, 5] + +# 删除首部元素 +array.shift #=> 1 +array #=> [2, 3, 4, 5] + +# 检查元素是否存在与数组之中 +array.includes? 3 #=> true + +# 一种特殊语法,用来创建字符串数组或符号数组 +%w(one two three) #=> ["one", "two", "three"] : Array(String) +%i(one two three) #=> [:one, :two, :three] : Array(Symbol) + +# 对于定义了`new`和`#<<`方法的类,可以用以下语法创建新对象 +set = Set{1, 2, 3} #=> [1, 2, 3] +set.class #=> Set(Int32) + +# 以下代码与上方等同 +set = Set(typeof(1, 2, 3)).new +set << 1 +set << 2 +set << 3 + +# 哈希表类型(Hash) + +{1 => 2, 3 => 4}.class #=> Hash(Int32, Int32) +{1 => 2, 'a' => 3}.class #=> Hash(Int32 | Char, Int32) + +# 必须为空哈希表指定类型 +{} # Syntax error +{} of Int32 => Int32 # {} +Hash(Int32, Int32).new # {} + +# 可以使用键(key)快速查询哈希表 +hash = {"color" => "green", "number" => 5} +hash["color"] #=> "green" +hash["no_such_key"] #=> Missing hash key: "no_such_key" (KeyError) +hash["no_such_key"]? #=> nil + +# 检查某一键哈希表中是否存在 +hash.has_key? "color" #=> true + +# 对于定义了`#[]=`方法的类,可以使用以下语法创建对象 +class MyType + def []=(key, value) + puts "do stuff" + end +end + +MyType{"foo" => "bar"} + +# 以上与下列代码等同 +tmp = MyType.new +tmp["foo"] = "bar" +tmp + +# 范围类型(Range) + +1..10 #=> Range(Int32, Int32) +Range.new(1, 10).class #=> Range(Int32, Int32) + +# 包含或不包含端点 +(3..5).to_a #=> [3, 4, 5] +(3...5).to_a #=> [3, 4] + +# 检查某一值是否在范围内 +(1..8).includes? 2 #=> true + +# 元组类型(Tuple) + +# 元组类型尺寸固定,不可变,储存在栈中 +# 元组可以有不同类型的对象组成 +{1, "hello", 'x'}.class #=> Tuple(Int32, String, Char) + +# 使用下标访问元组 +tuple = {:key1, :key2} +tuple[1] #=> :key2 +tuple[2] #=> syntax error : Index out of bound + +# 将元组中的元素赋值给变量 +a, b, c = {:a, 'b', "c"} +a #=> :a +b #=> 'b' +c #=> "c" + +# 命名元组类型(NamedTuple) + +tuple = {name: "Crystal", year: 2011} # NamedTuple(name: String, year: Int32) +tuple[:name] # => "Crystal" (String) +tuple[:year] # => 2011 (Int32) + +# 命名元组的键可以是字符串常量 +{"this is a key": 1} # => NamedTuple("this is a key": Int32) + +# 过程类型(Proc) +# 过程代表一个函数指针,以及可选的上下文(闭包) +# 过程通常使用字面值创建 +proc = ->(x : Int32) { x.to_s } +proc.class # Proc(Int32, String) + +# 或者使用`new`方法创建 +Proc(Int32, String).new { |x| x.to_s } + +# 使用`call`方法调用过程 +proc.call 10 #=> "10" + +# 控制语句(Control statements) + +if true + "if 语句" +elsif false + "else-if, 可选" +else + "else, 同样可选" +end + +puts "可以将if后置" if true + +# 将if作为表达式 +a = if 2 > 1 + 3 + else + 4 + end + +a #=> 3 + +# 条件表达式 +a = 1 > 2 ? 3 : 4 #=> 4 + +# `case`语句 +cmd = "move" + +action = case cmd + when "create" + "Creating..." + when "copy" + "Copying..." + when "move" + "Moving..." + when "delete" + "Deleting..." +end + +action #=> "Moving..." + +# 循环 +index = 0 +while index <= 3 + puts "Index: #{index}" + index += 1 +end +# Index: 0 +# Index: 1 +# Index: 2 +# Index: 3 + +index = 0 +until index > 3 + puts "Index: #{index}" + index += 1 +end +# Index: 0 +# Index: 1 +# Index: 2 +# Index: 3 + +# 更好的做法是使用`each` +(0..3).each do |index| + puts "Index: #{index}" +end +# Index: 0 +# Index: 1 +# Index: 2 +# Index: 3 + +# 变量的类型取决于控制语句中表达式的类型 +if a < 3 + a = "hello" +else + a = true +end +typeof a #=> (Bool | String) + +if a && b + # 此处`a`与`b`均为Nil +end + +if a.is_a? String + a.class #=> String +end + +# 函数(Functions) + +def double(x) + x * 2 +end + +# 函数(以及所有代码块)均将最末尾表达式的值作为返回值 +double(2) #=> 4 + +# 在没有歧义的情况下,括号可以省略 +double 3 #=> 6 + +double double 3 #=> 12 + +def sum(x, y) + x + y +end + +# 使用逗号分隔参数 +sum 3, 4 #=> 7 + +sum sum(3, 4), 5 #=> 12 + +# yield +# 所有函数都有一个默认生成、可选的代码块(block)参数 +# 在函数中可以使用yield调用此代码块 + +def surround + puts '{' + yield + puts '}' +end + +surround { puts "hello world" } + +# { +# hello world +# } + + +# 可将代码块作为参数传给函数 +# "&" 表示对代码块参数的引用 +def guests(&block) + block.call "some_argument" +end + +# 使用星号"*"将参数转换成元组 +def guests(*array) + array.each { |guest| puts guest } +end + +# 如果函数返回数组,可以将其解构 +def foods + ["pancake", "sandwich", "quesadilla"] +end +breakfast, lunch, dinner = foods +breakfast #=> "pancake" +dinner #=> "quesadilla" + +# 按照约定,所有返回布尔值的方法都以问号结尾 +5.even? # false +5.odd? # true + +# 以感叹号结尾的方法,都有一些破坏性(destructive)行为,比如改变调用接收者(receiver) +# 对于某些方法,带有感叹号的版本将改变调用接收者,而不带有感叹号的版本返回新值 +company_name = "Dunder Mifflin" +company_name.gsub "Dunder", "Donald" #=> "Donald Mifflin" +company_name #=> "Dunder Mifflin" +company_name.gsub! "Dunder", "Donald" +company_name #=> "Donald Mifflin" + + +# 使用`class`关键字来定义类(class) +class Human + + # 类变量,由类的所有实例所共享 + @@species = "H. sapiens" + + # `name`的类型为`String` + @name : String + + # 构造器方法(initializer) + # 其中@name、@age为简写,相当于 + # + # def initialize(name, age = 0) + # @name = name + # @age = age + # end + # + # `age`为可选参数,如果未指定,则使用默认值0 + def initialize(@name, @age = 0) + end + + # @name的setter方法 + def name=(name) + @name = name + end + + # @name的getter方法 + def name + @name + end + + # 上述getter与setter的定义可以用property宏简化 + property :name + + # 也可用getter与setter宏独立创建getter与setter + getter :name + setter :name + + # 此处的`self.`使`say`成为类方法 + def self.say(msg) + puts msg + end + + def species + @@species + end +end + + +# 将类实例化 +jim = Human.new("Jim Halpert") + +dwight = Human.new("Dwight K. Schrute") + +# 调用一些实例方法 +jim.species #=> "H. sapiens" +jim.name #=> "Jim Halpert" +jim.name = "Jim Halpert II" #=> "Jim Halpert II" +jim.name #=> "Jim Halpert II" +dwight.species #=> "H. sapiens" +dwight.name #=> "Dwight K. Schrute" + +# 调用类方法 +Human.say("Hi") #=> 输出 Hi ,返回 nil + +# 带有`@`前缀的变量为实例变量 +class TestClass + @var = "I'm an instance var" +end + +# 带有`@@`前缀的变量为类变量 +class TestClass + @@var = "I'm a class var" +end +# 首字母大写的变量为常量 +Var = "这是一个常量" +Var = "无法再次被赋值" # 常量`Var`已经被初始化 + +# 在crystal中类也是对象(object),因此类也有实例变量(instance variable) +# 类变量的定义由类以及类的派生类所共有,但类变量的值是独立的 + +# 基类 +class Human + @@foo = 0 + + def self.foo + @@foo + end + + def self.foo=(value) + @@foo = value + end +end + +# 派生类 +class Worker < Human +end + +Human.foo #=> 0 +Worker.foo #=> 0 + +Human.foo = 2 #=> 2 +Worker.foo #=> 0 + +Worker.foo = 3 #=> 3 +Human.foo #=> 2 +Worker.foo #=> 3 + +module ModuleExample + def foo + "foo" + end +end + +# include <Module> 将模块(module)中的方法添加为实例方法 +# extend <Module> 将模块中的方法添加为类方法 + +class Person + include ModuleExample +end + +class Book + extend ModuleExample +end + +Person.foo # => undefined method 'foo' for Person:Class +Person.new.foo # => 'foo' +Book.foo # => 'foo' +Book.new.foo # => undefined method 'foo' for Book + + +# 异常处理 + +# 定义新的异常类(exception) +class MyException < Exception +end + +# 再定义一个异常类 +class MyAnotherException < Exception; end + +ex = begin + raise MyException.new +rescue ex1 : IndexError + "ex1" +rescue ex2 : MyException | MyAnotherException + "ex2" +rescue ex3 : Exception + "ex3" +rescue ex4 # 捕捉任何类型的异常 + "ex4" +end + +ex #=> "ex2" + +``` + +## 参考资料 + +- [官方网站](https://crystal-lang.org/) +- [官方文档](https://crystal-lang.org/docs/overview/) +- [在线运行代码](https://play.crystal-lang.org/#/cr) +- [Github仓库](https://github.com/crystal-lang/crystal) diff --git a/zh-cn/dart-cn.html.markdown b/zh-cn/dart-cn.html.markdown index 6a6562bc..79db8e5c 100644 --- a/zh-cn/dart-cn.html.markdown +++ b/zh-cn/dart-cn.html.markdown @@ -176,23 +176,47 @@ example13() { match(s2); } -// 布尔表达式必需被解析为 true 或 false, -// 因为不支持隐式转换。 +// 布尔表达式支持隐式转换以及动态类型 example14() { - var v = true; - if (v) { - print("Example14 value is true"); + var a = true; + if (a) { + print("Example14 true, a is $a"); } - v = null; + a = null; + if (a) { + print("Example14 true, a is $a"); + } else { + print("Example14 false, a is $a"); // 执行到这里 + } + + // 动态类型的null可以转换成bool型 + var b;// b是动态类型 + b = "abc"; try { - if (v) { - // 不会执行 + if (b) { + print("Example14 true, b is $b"); } else { - // 不会执行 + print("Example14 false, b is $b"); } } catch (e) { - print("Example14 null value causes an exception: '${e}'"); + print("Example14 error, b is $b"); // 这段代码可以执行但是会报错 } + b = null; + if (b) { + print("Example14 true, b is $b"); + } else { + print("Example14 false, b is $b"); // 这行到这里 + } + + // 静态类型的null不能转换成bool型 + var c = "abc"; + c = null; + // 编译出错 + // if (c) { + // print("Example14 true, c is $c"); + // } else { + // print("Example14 false, c is $c"); + // } } // try/catch/finally 和 throw 语句用于异常处理。 @@ -492,8 +516,8 @@ main() { Dart 有一个综合性网站。它涵盖了 API 参考、入门向导、文章以及更多, 还包括一个有用的在线试用 Dart 页面。 -http://www.dartlang.org/ -http://try.dartlang.org/ +* [https://www.dartlang.org](https://www.dartlang.org) +* [https://try.dartlang.org](https://try.dartlang.org) diff --git a/zh-cn/fortran95-cn.html.markdown b/zh-cn/fortran95-cn.html.markdown new file mode 100644 index 00000000..e28d309f --- /dev/null +++ b/zh-cn/fortran95-cn.html.markdown @@ -0,0 +1,435 @@ +--- +language: Fortran +filename: learnfortran-cn.f95 +contributors: + - ["Robert Steed", "https://github.com/robochat"] +translators: + - ["Corvusnest", "https://github.com/Corvusnest"] +lang: zh-cn +--- + +Fortran 是最古老的计算机语言之一。它由IBM开发于1950年用于数值运算(Fortran 为 "Formula +Translation" 的缩写)。虽然该语言已年代久远,但目前仍用于高性能计算,如天气预报。 +该语言仍在持续发展,并且基本保持向下兼容。知名的版本为 Fortran 77, Fortran 90, +Fortran 95, Fortran 2003, Fortran 2008 与 Fortran 2015。 + +这篇概要将讨论 Fortran 95 的一些特征。因为它是目前所广泛采用的标准版本,并且与最新版本的内容 +也基本相同(而 Fortran 77 则是一个非常不同的版本)。 + +```fortran + +! 这是一行注释 + + +program example !声明一个叫做 example 的程序 + + ! 代码只能放在程序、函数、子程序或者模块内部 + ! 推荐使用缩进,但不是必须的。 + + ! 声明变量 + ! =================== + + ! 所有的声明必须放在语句与表达式之前 + + implicit none !阻止变量的隐式声明 (推荐!) + ! Implicit none 必须在每一个 函数/程序/模块 中进行声明 + + ! 重要 - Fortran 对大小写不敏感 + real z + REAL Z2 + + real :: v,x ! 警告: 默认值取决于编译器! + real :: a = 3, b=2E12, c = 0.01 + integer :: i, j, k=1, m + real, parameter :: PI = 3.1415926535897931 !声明一个常量 + logical :: y = .TRUE. , n = .FALSE. !布尔值 + complex :: w = (0,1) !sqrt(-1) (译注: 定义复数,此为-1的平方根) + character (len=3) :: month !长度为3的字符串 + + real :: array(6) !声明长度为6的浮点数数组 + real, dimension(4) :: arrayb !声明数组的另一种方法 + integer :: arrayc(-10:10) !有着自定义索引的数组 + real :: array2d(3,2) !多维数组 + + ! 分隔符 '::' 并不总是必要的,但推荐使用 + + ! 还存在很多其他的变量特征: + real, pointer :: p !声明一个指针 + + integer, parameter :: LP = selected_real_kind(20) + real (kind = LP) :: d !长精度变量 + + ! 警告:在声明期间初始化变量将导致在函数内发生问题,因为这将自动具备了 “save” 属性, + ! 因此变量的值在函数的多次调用期间将被存储。一般来说,除了常量,应分开声明与初始化! + + ! 字符串 + ! ======= + + character :: a_char = 'i' + character (len = 6) :: a_str = "qwerty" + character (len = 30) :: str_b + character (len = *), parameter :: a_long_str = "This is a long string." + !可以通过使用 (len=*) 来自动判断长度,但只对常量有效 + + str_b = a_str // " keyboard" !通过 // 操作符来连接字符串 + + + ! 任务与计算 + ! ======================= + + Z = 1 !向之前声明的变量 z 赋值 (大小写不敏感). + j = 10 + 2 - 3 + a = 11.54 / (2.3 * 3.1) + b = 2**3 !幂 + + + ! 控制流程语句 与 操作符 + ! =================================== + + !单行 if 语句 + if (z == a) b = 4 !判别句永远需要放在圆括号内 + + if (z /= a) then !z 不等于 a + ! 其他的比较运算符: < > <= >= == /= + b = 4 + else if (z .GT. a) then !z 大于(Greater) a + ! 文本形式的比较运算符: .LT. .GT. .LE. .GE. .EQ. .NE. + b = 6 + else if (z < a) then !'then' 必须放在该行 + b = 5 !执行部分必须放在新的一行里 + else + b = 10 + end if !结束语句需要 'if' (也可以用 'endif'). + + + if (.NOT. (x < c .AND. v >= a .OR. z == z)) then !布尔操作符 + inner: if (.TRUE.) then !可以为 if 结构命名 + b = 1 + endif inner !接下来必须命名 endif 语句. + endif + + + i = 20 + select case (i) + case (0) !当 i == 0 + j=0 + case (1:10) !当 i 为 1 到 10 之内 ( 1 <= i <= 10 ) + j=1 + case (11:) !当 i>=11 + j=2 + case default + j=3 + end select + + + month = 'jan' + ! 状态值可以为整数、布尔值或者字符类型 + ! Select 结构同样可以被命名 + monthly: select case (month) + case ("jan") + j = 0 + case default + j = -1 + end select monthly + + do i=2,10,2 !从2到10(包含2和10)以2为步进值循环 + innerloop: do j=1,3 !循环同样可以被命名 + exit !跳出循环 + end do innerloop + cycle !重复跳入下一次循环 + enddo + + + ! Goto 语句是存在的,但强烈不建议使用 + goto 10 + stop 1 !立即停止程序 (返回一个设定的状态码). +10 j = 201 !这一行被标注为 10 行 (line 10) + + + ! 数组 + ! ====== + array = (/1,2,3,4,5,6/) + array = [1,2,3,4,5,6] !当使用 Fortran 2003 版本. + arrayb = [10.2,3e3,0.41,4e-5] + array2d = reshape([1.0,2.0,3.0,4.0,5.0,6.0], [3,2]) + + ! Fortran 数组索引起始于 1 + ! (默认下如此,也可以为数组定义不同的索引起始) + v = array(1) !获取数组的第一个元素 + v = array2d(2,2) + + print *, array(3:5) !打印从第3到第五5之内的所有元素 + print *, array2d(1,:) !打印2维数组的第一列 + + array = array*3 + 2 !可为数组设置数学表达式 + array = array*array !数组操作支持元素级(操作) (element-wise) + !array = array*array2d !这两类数组并不是同一个维度的 + + ! 有很多内置的数组操作函数 + c = dot_product(array,array) !点乘 (点积) + ! 用 matmul() 来进行矩阵运算. + c = sum(array) + c = maxval(array) + print *, minloc(array) + c = size(array) + print *, shape(array) + m = count(array > 0) + + ! 遍历一个数组 (一般使用 Product() 函数). + v = 1 + do i = 1, size(array) + v = v*array(i) + end do + + ! 有条件地执行元素级操作 + array = [1,2,3,4,5,6] + where (array > 3) + array = array + 1 + elsewhere (array == 2) + array = 1 + elsewhere + array = 0 + end where + + ! 隐式DO循环可以很方便地创建数组 + array = [ (i, i = 1,6) ] !创建数组 [1,2,3,4,5,6] + array = [ (i, i = 1,12,2) ] !创建数组 [1,3,5,7,9,11] + array = [ (i**2, i = 1,6) ] !创建数组 [1,4,9,16,25,36] + array = [ (4,5, i = 1,3) ] !创建数组 [4,5,4,5,4,5] + + + ! 输入/输出 + ! ============ + + print *, b !向命令行打印变量 'b' + + ! 我们可以格式化输出 + print "(I6)", 320 !打印 ' 320' + print "(I6.4)", 3 !打印 ' 0003' + print "(F6.3)", 4.32 !打印 ' 4.320' + + + ! 该字母与数值规定了给定的数值与字符所用于打印输出的类型与格式 + ! 字母可为 I (整数), F (浮点数), E (工程格式), + ! L (逻辑/布尔值), A (字符) ... + print "(I3)", 3200 !如果数值无法符合格式将打印 '***' + + ! 可以同时设定多种格式 + print "(I5,F6.2,E6.2)", 120, 43.41, 43.41 + print "(3I5)", 10, 20, 30 !连续打印3个整数 (字段宽度 = 5). + print "(2(I5,F6.2))", 120, 43.42, 340, 65.3 !连续分组格式 + + ! 我们也可以从终端读取输入 + read *, v + read "(2F6.2)", v, x !读取2个数值 + + ! 读取文件 + open(unit=11, file="records.txt", status="old") + ! 文件被引用带有一个单位数 'unit', 为一个取值范围在9-99的整数 + ! 'status' 可以为 {'old','replace','new'} 其中之一 + read(unit=11, fmt="(3F10.2)") a, b, c + close(11) + + ! 写入一个文件 + open(unit=12, file="records.txt", status="replace") + write(12, "(F10.2,F10.2,F10.2)") c, b, a + close(12) + ! 在讨论范围之外的还有更多的细节与可用功能,并于老版本的 Fortran 保持兼容 + + + ! 内置函数 + ! ================== + + ! Fortran 拥有大约 200 个内置函数/子程序 + ! 例子 + call cpu_time(v) !以秒为单位设置时间 + k = ior(i,j) !2个整数的位或运算 + v = log10(x) !以10为底的log运算 + i = floor(b) !返回一个最接近的整数小于或等于x (地板数) + v = aimag(w) !复数的虚数部分 + + + ! 函数与子程序 + ! ======================= + + ! 一个子程序会根据输入值运行一些代码并会导致副作用 (side-effects) 或修改输入值 + ! (译者注: 副作用是指对子程序/函数外的环境产生影响,如修改变量) + + call routine(a,c,v) !调用子程序 + + ! 一个函数会根据输入的一系列数值来返回一个单独的值 + ! 但输入值仍然可能被修改以及产生副作用 + + m = func(3,2,k) !调用函数 + + ! 函数可以在表达式内被调用 + Print *, func2(3,2,k) + + ! 一个纯函数不会去修改输入值或产生副作用 + m = func3(3,2,k) + + +contains ! 用于定义程序内部的副程序(sub-programs)的区域 + + ! Fortran 拥有一些不同的方法去定义函数 + + integer function func(a,b,c) !一个返回一个整数的函数 + implicit none !最好也在函数内将含蓄模式关闭 (implicit none) + integer :: a,b,c !输入值类型定义在函数内部 + if (a >= 2) then + func = a + b + c !返回值默认为函数名 + return !可以在函数内任意时间返回当前值 + endif + func = a + c + ! 在函数的结尾不需要返回语句 + end function func + + + function func2(a,b,c) result(f) !将返回值声明为 'f' + implicit none + integer, intent(in) :: a,b !可以声明让变量无法被函数修改 + integer, intent(inout) :: c + integer :: f !函数的返回值类型在函数内声明 + integer :: cnt = 0 !注意 - 隐式的初始化变量将在函数的多次调用间被存储 + f = a + b - c + c = 4 !变动一个输入变量的值 + cnt = cnt + 1 !记录函数的被调用次数 + end function func2 + + + pure function func3(a,b,c) !一个没有副作用的纯函数 + implicit none + integer, intent(in) :: a,b,c + integer :: func3 + func3 = a*b*c + end function func3 + + + subroutine routine(d,e,f) + implicit none + real, intent(inout) :: f + real, intent(in) :: d,e + f = 2*d + 3*e + f + end subroutine routine + + +end program example ! 函数定义完毕 ----------------------- + +! 函数与子程序的外部声明对于生成程序清单来说,需要一个接口声明(即使它们在同一个源文件内)(见下) +! 使用 'contains' 可以很容易地在模块或程序内定义它们 + +elemental real function func4(a) result(res) +! 一个元函数(elemental function) 为一个纯函数使用一个标量输入值 +! 但同时也可以用在一个数组并对其中的元素分别处理,之后返回一个新的数组 + real, intent(in) :: a + res = a**2 + 1.0 +end function func4 + + +! 模块 +! ======= + +! 模块十分适合于存放与复用相关联的一组声明、函数与子程序 + +module fruit + real :: apple + real :: pear + real :: orange +end module fruit + + +module fruity + + ! 声明必须按照顺序: 模块、接口、变量 + ! (同样可在程序内声明模块和接口) + + use fruit, only: apple, pear ! 使用来自于 fruit 模块的 apple 和 pear + implicit none !在模块导入后声明 + + private !使得模块内容为私有(private)(默认为公共 public) + ! 显式声明一些变量/函数为公共 + public :: apple,mycar,create_mycar + ! 声明一些变量/函数为私有(在当前情况下没必要)(译注: 因为前面声明了模块全局 private) + private :: func4 + + ! 接口 + ! ========== + ! 在模块内显式声明一个外部函数/程序 + ! 一般最好将函数/程序放进 'contains' 部分内 + interface + elemental real function func4(a) result(res) + real, intent(in) :: a + end function func4 + end interface + + ! 重载函数可以通过已命名的接口来定义 + interface myabs + ! 可以通过使用 'module procedure' 关键词来包含一个已在模块内定义的函数 + module procedure real_abs, complex_abs + end interface + + ! 派生数据类型 + ! ================== + ! 可创建自定义数据结构 + type car + character (len=100) :: model + real :: weight !(公斤 kg) + real :: dimensions(3) !例: 长宽高(米) + character :: colour + end type car + + type(car) :: mycar !声明一个自定义类型的变量 + ! 用法具体查看 create_mycar() + + ! 注: 模块内没有可执行的语句 + +contains + + subroutine create_mycar(mycar) + ! 展示派生数据类型的使用 + implicit none + type(car),intent(out) :: mycar + + ! 通过 '%' 操作符来访问(派生数据)类型的元素 + mycar%model = "Ford Prefect" + mycar%colour = 'r' + mycar%weight = 1400 + mycar%dimensions(1) = 5.0 !索引默认起始值为 1 ! + mycar%dimensions(2) = 3.0 + mycar%dimensions(3) = 1.5 + + end subroutine + + real function real_abs(x) + real :: x + if (x<0) then + real_abs = -x + else + real_abs = x + end if + end function real_abs + + real function complex_abs(z) + complex :: z + ! 过长的一行代码可通过延续符 '&' 来换行 + complex_abs = sqrt(real(z)**2 + & + aimag(z)**2) + end function complex_abs + + +end module fruity + +``` + +### 更多资源 + +了解更多的 Fortran 信息: + ++ [wikipedia](https://en.wikipedia.org/wiki/Fortran) ++ [Fortran_95_language_features](https://en.wikipedia.org/wiki/Fortran_95_language_features) ++ [fortranwiki.org](http://fortranwiki.org) ++ [www.fortran90.org/](http://www.fortran90.org) ++ [list of Fortran 95 tutorials](http://www.dmoz.org/Computers/Programming/Languages/Fortran/FAQs%2C_Help%2C_and_Tutorials/Fortran_90_and_95/) ++ [Fortran wikibook](https://en.wikibooks.org/wiki/Fortran) ++ [Fortran resources](http://www.fortranplus.co.uk/resources/fortran_resources.pdf) ++ [Mistakes in Fortran 90 Programs That Might Surprise You](http://www.cs.rpi.edu/~szymansk/OOF90/bugs.html) diff --git a/zh-cn/git-cn.html.markdown b/zh-cn/git-cn.html.markdown index 4ef3ffb8..d471ab5d 100644 --- a/zh-cn/git-cn.html.markdown +++ b/zh-cn/git-cn.html.markdown @@ -131,7 +131,7 @@ $ git help init ```bash -# 显示分支,为跟踪文件,更改和其他不同 +# 显示分支,未跟踪文件,更改和其他不同 $ git status # 查看其他的git status的用法 diff --git a/zh-cn/javascript-cn.html.markdown b/zh-cn/javascript-cn.html.markdown index bdef0099..360f7c65 100644 --- a/zh-cn/javascript-cn.html.markdown +++ b/zh-cn/javascript-cn.html.markdown @@ -12,12 +12,9 @@ translators: lang: zh-cn --- -Javascript于1995年由网景公司的Brendan Eich发明。 -最初发明的目的是作为一个简单的网站脚本语言,来作为 -复杂网站应用java的补充。但由于它与网页结合度很高并且由浏览器内置支持, -所以javascript变得比java在前端更为流行了。 +Javascript 于 1995 年由网景公司的 Brendan Eich 发明。最初它作为一种简单的,用于开发网站的脚本语言而被发明出来,是用于开发复杂网站的 Java 的补充。但由于它与网页结合度很高并且在浏览器中得到内置的支持,所以在网页前端领域 Javascript 变得比 Java 更流行了。 -不过 JavaScript 可不仅仅只用于浏览器: Node.js,一个基于Google Chrome V8引擎的独立运行时环境,也越来越流行。 +不过,Javascript 不仅用于网页浏览器,一个名为 Node.js 的项目提供了面向 Google Chrome V8 引擎的独立运行时环境,它正在变得越来越流行。 很欢迎来自您的反馈,您可以通过下列方式联系到我: [@adambrenecki](https://twitter.com/adambrenecki), 或者 diff --git a/zh-cn/julia-cn.html.markdown b/zh-cn/julia-cn.html.markdown index 1f91d52c..b350b6dc 100644 --- a/zh-cn/julia-cn.html.markdown +++ b/zh-cn/julia-cn.html.markdown @@ -2,16 +2,24 @@ language: Julia filename: learn-julia-zh.jl contributors: - - ["Jichao Ouyang", "http://oyanglul.us"] + - ["Leah Hanson", "http://leahhanson.us"] + - ["Pranit Bauva", "https://github.com/pranitbauva1997"] + - ["Daniel YC Lin", "https://github.com/dlintw"] translators: - ["Jichao Ouyang", "http://oyanglul.us"] + - ["woclass", "https://github.com/inkydragon"] lang: zh-cn --- -```ruby -# 单行注释只需要一个井号 +Julia 是一种新的同像函数式编程语言(homoiconic functional language),它专注于科学计算领域。 +虽然拥有同像宏(homoiconic macros)、一级函数(first-class functions)和底层控制等全部功能,但 Julia 依旧和 Python 一样易于学习和使用。 + +示例代码基于 Julia 1.0.0 + +```julia +# 单行注释只需要一个井号「#」 #= 多行注释 - 只需要以 '#=' 开始 '=#' 结束 + 只需要以「#=」开始「=#」结束 还可以嵌套. =# @@ -19,41 +27,41 @@ lang: zh-cn ## 1. 原始类型与操作符 #################################################### -# Julia 中一切皆是表达式。 - -# 这是一些基本数字类型. -3 # => 3 (Int64) -3.2 # => 3.2 (Float64) -2 + 1im # => 2 + 1im (Complex{Int64}) -2//3 # => 2//3 (Rational{Int64}) - -# 支持所有的普通中缀操作符。 -1 + 1 # => 2 -8 - 1 # => 7 -10 * 2 # => 20 -35 / 5 # => 7.0 -5 / 2 # => 2.5 # 用 Int 除 Int 永远返回 Float -div(5, 2) # => 2 # 使用 div 截断小数点 -5 \ 35 # => 7.0 -2 ^ 2 # => 4 # 次方, 不是二进制 xor -12 % 10 # => 2 +# Julia 中一切皆为表达式 + +# 这是一些基本数字类型 +typeof(3) # => Int64 +typeof(3.2) # => Float64 +typeof(2 + 1im) # => Complex{Int64} +typeof(2 // 3) # => Rational{Int64} + +# 支持所有的普通中缀操作符 +1 + 1 # => 2 +8 - 1 # => 7 +10 * 2 # => 20 +35 / 5 # => 7.0 +10 / 2 # => 5.0 # 整数除法总是返回浮点数 +div(5, 2) # => 2 # 使用 div 可以获得整除的结果 +5 \ 35 # => 7.0 +2^2 # => 4 # 幂运算,不是异或 (xor) +12 % 10 # => 2 # 用括号提高优先级 (1 + 3) * 2 # => 8 -# 二进制操作符 -~2 # => -3 # 非 -3 & 5 # => 1 # 与 -2 | 4 # => 6 # 或 -2 $ 4 # => 6 # 异或 -2 >>> 1 # => 1 # 逻辑右移 -2 >> 1 # => 1 # 算术右移 -2 << 1 # => 4 # 逻辑/算术 右移 - -# 可以用函数 bits 查看二进制数。 -bits(12345) +# 位操作符 +~2 # => -3 # 按位非 (not) +3 & 5 # => 1 # 按位与 (and) +2 | 4 # => 6 # 按位或 (or) +xor(2, 4) # => 6 # 按位异或 (xor) +2 >>> 1 # => 1 # 逻辑右移 +2 >> 1 # => 1 # 算术右移 +2 << 1 # => 4 # 逻辑/算术左移 + +# 可以用函数 bitstring 查看二进制数。 +bitstring(12345) # => "0000000000000000000000000000000000000000000000000011000000111001" -bits(12345.0) +bitstring(12345.0) # => "0100000011001000000111001000000000000000000000000000000000000000" # 布尔值是原始类型 @@ -61,40 +69,50 @@ true false # 布尔操作符 -!true # => false -!false # => true -1 == 1 # => true -2 == 1 # => false -1 != 1 # => false -2 != 1 # => true -1 < 10 # => true -1 > 10 # => false -2 <= 2 # => true -2 >= 2 # => true -# 比较可以串联 +!true # => false +!false # => true +1 == 1 # => true +2 == 1 # => false +1 != 1 # => false +2 != 1 # => true +1 < 10 # => true +1 > 10 # => false +2 <= 2 # => true +2 >= 2 # => true + +# 链式比较 1 < 2 < 3 # => true 2 < 3 < 2 # => false -# 字符串可以由 " 创建 +# 字符串可以由「"」创建 "This is a string." -# 字符字面量可用 ' 创建 +# 字符字面量可用「'」创建 'a' +# 字符串使用 UTF-8 编码 # 可以像取数组取值一样用 index 取出对应字符 -"This is a string"[1] # => 'T' # Julia 的 index 从 1 开始 :( -# 但是对 UTF-8 无效, -# 因此建议使用遍历器 (map, for loops, 等). +ascii("This is a string")[1] +# => 'T': ASCII/Unicode U+0054 (category Lu: Letter, uppercase) +# Julia 的 index 从 1 开始 :( +# 但只有在字符串仅由 ASCII 字符构成时,字符串才能够被安全的引索 +# 因此建议使用遍历器 (map, for loops, 等) # $ 可用于字符插值: "2 + 2 = $(2 + 2)" # => "2 + 2 = 4" # 可以将任何 Julia 表达式放入括号。 -# 另一种格式化字符串的方式是 printf 宏. -@printf "%d is less than %f" 4.5 5.3 # 5 is less than 5.300000 +# 另一种输出格式化字符串的方法是使用标准库 Printf 中的 Printf 宏 +using Printf +@printf "%d is less than %f\n" 4.5 5.3 # => 5 is less than 5.300000 # 打印字符串很容易 -println("I'm Julia. Nice to meet you!") +println("I'm Julia. Nice to meet you!") # => I'm Julia. Nice to meet you! + +# 字符串可以按字典序进行比较 +"good" > "bye" # => true +"good" == "good" # => true +"1 + 2 = 3" == "1 + 2 = $(1 + 2)" # => true #################################################### ## 2. 变量与集合 @@ -106,12 +124,12 @@ some_var # => 5 # 访问未声明变量会抛出异常 try - some_other_var # => ERROR: some_other_var not defined + some_other_var # => ERROR: UndefVarError: some_other_var not defined catch e println(e) end -# 变量名需要以字母开头. +# 变量名必须以下划线或字母开头 # 之后任何字母,数字,下划线,叹号都是合法的。 SomeOtherVar123! = 6 # => 6 @@ -122,66 +140,93 @@ SomeOtherVar123! = 6 # => 6 # 注意 Julia 的命名规约: # -# * 变量名为小写,单词之间以下划线连接('\_')。 +# * 名称可以用下划线「_」分割。 +# 不过一般不推荐使用下划线,除非不用变量名就会变得难于理解 # -# * 类型名以大写字母开头,单词以 CamelCase 方式连接。 +# * 类型名以大写字母开头,单词以 CamelCase 方式连接,无下划线。 # # * 函数与宏的名字小写,无下划线。 # -# * 会改变输入的函数名末位为 !。 +# * 会改变输入的函数名末位为「!」。 # 这类函数有时被称为 mutating functions 或 in-place functions. -# 数组存储一列值,index 从 1 开始。 -a = Int64[] # => 0-element Int64 Array +# 数组存储一列值,index 从 1 开始 +a = Int64[] # => 0-element Array{Int64,1} + +# 一维数组可以以逗号分隔值的方式声明 +b = [4, 5, 6] # => 3-element Array{Int64,1}: [4, 5, 6] +b = [4; 5; 6] # => 3-element Array{Int64,1}: [4, 5, 6] +b[1] # => 4 +b[end] # => 6 -# 一维数组可以以逗号分隔值的方式声明。 -b = [4, 5, 6] # => 包含 3 个 Int64 类型元素的数组: [4, 5, 6] -b[1] # => 4 -b[end] # => 6 +# 二维数组以分号分隔维度 +matrix = [1 2; 3 4] # => 2×2 Array{Int64,2}: [1 2; 3 4] -# 二维数组以分号分隔维度。 -matrix = [1 2; 3 4] # => 2x2 Int64 数组: [1 2; 3 4] +# 指定数组的类型 +b = Int8[4, 5, 6] # => 3-element Array{Int8,1}: [4, 5, 6] # 使用 push! 和 append! 往数组末尾添加元素 -push!(a,1) # => [1] -push!(a,2) # => [1,2] -push!(a,4) # => [1,2,4] -push!(a,3) # => [1,2,4,3] -append!(a,b) # => [1,2,4,3,4,5,6] +push!(a, 1) # => [1] +push!(a, 2) # => [1,2] +push!(a, 4) # => [1,2,4] +push!(a, 3) # => [1,2,4,3] +append!(a, b) # => [1,2,4,3,4,5,6] -# 用 pop 弹出末尾元素 -pop!(b) # => 6 and b is now [4,5] +# 用 pop 弹出尾部的元素 +pop!(b) # => 6 +b # => [4,5] -# 可以再放回去 -push!(b,6) # b 又变成了 [4,5,6]. +# 再放回去 +push!(b, 6) # => [4,5,6] +b # => [4,5,6] -a[1] # => 1 # 永远记住 Julia 的 index 从 1 开始! +a[1] # => 1 # 永远记住 Julia 的引索从 1 开始!而不是 0! -# 用 end 可以直接取到最后索引. 可用作任何索引表达式 +# 用 end 可以直接取到最后索引。它可以用在任何索引表达式中 a[end] # => 6 -# 还支持 shift 和 unshift -shift!(a) # => 返回 1,而 a 现在时 [2,4,3,4,5,6] -unshift!(a,7) # => [7,2,4,3,4,5,6] +# 数组还支持 popfirst! 和 pushfirst! +popfirst!(a) # => 1 +a # => [2,4,3,4,5,6] +pushfirst!(a, 7) # => [7,2,4,3,4,5,6] +a # => [7,2,4,3,4,5,6] # 以叹号结尾的函数名表示它会改变参数的值 -arr = [5,4,6] # => 包含三个 Int64 元素的数组: [5,4,6] -sort(arr) # => [4,5,6]; arr 还是 [5,4,6] -sort!(arr) # => [4,5,6]; arr 现在是 [4,5,6] +arr = [5,4,6] # => 3-element Array{Int64,1}: [5,4,6] +sort(arr) # => [4,5,6] +arr # => [5,4,6] +sort!(arr) # => [4,5,6] +arr # => [4,5,6] -# 越界会抛出 BoundsError 异常 +# 数组越界会抛出 BoundsError try - a[0] # => ERROR: BoundsError() in getindex at array.jl:270 - a[end+1] # => ERROR: BoundsError() in getindex at array.jl:270 + a[0] + # => ERROR: BoundsError: attempt to access 7-element Array{Int64,1} at + # index [0] + # => Stacktrace: + # => [1] getindex(::Array{Int64,1}, ::Int64) at .\array.jl:731 + # => [2] top-level scope at none:0 + # => [3] ... + # => in expression starting at ...\LearnJulia.jl:203 + a[end + 1] + # => ERROR: BoundsError: attempt to access 7-element Array{Int64,1} at + # index [8] + # => Stacktrace: + # => [1] getindex(::Array{Int64,1}, ::Int64) at .\array.jl:731 + # => [2] top-level scope at none:0 + # => [3] ... + # => in expression starting at ...\LearnJulia.jl:211 catch e println(e) end -# 错误会指出发生的行号,包括标准库 -# 如果你有 Julia 源代码,你可以找到这些地方 +# 报错时错误会指出出错的文件位置以及行号,标准库也一样 +# 你可以在 Julia 安装目录下的 share/julia 文件夹里找到这些标准库 # 可以用 range 初始化数组 -a = [1:5] # => 5-element Int64 Array: [1,2,3,4,5] +a = [1:5;] # => 5-element Array{Int64,1}: [1,2,3,4,5] +# 注意!分号不可省略 +a2 = [1:5] # => 1-element Array{UnitRange{Int64},1}: [1:5] # 可以切割数组 a[1:3] # => [1, 2, 3] @@ -189,11 +234,13 @@ a[2:end] # => [2, 3, 4, 5] # 用 splice! 切割原数组 arr = [3,4,5] -splice!(arr,2) # => 4 ; arr 变成了 [3,5] +splice!(arr, 2) # => 4 +arr # => [3,5] # 用 append! 连接数组 b = [1,2,3] -append!(a,b) # a 变成了 [1, 2, 3, 4, 5, 1, 2, 3] +append!(a, b) # => [1, 2, 3, 4, 5, 1, 2, 3] +a # => [1, 2, 3, 4, 5, 1, 2, 3] # 检查元素是否在数组中 in(1, a) # => true @@ -201,240 +248,258 @@ in(1, a) # => true # 用 length 获得数组长度 length(a) # => 8 -# Tuples 是 immutable 的 -tup = (1, 2, 3) # => (1,2,3) # an (Int64,Int64,Int64) tuple. +# 元组(Tuples)是不可变的 +tup = (1, 2, 3) # => (1,2,3) +typeof(tup) # => Tuple{Int64,Int64,Int64} tup[1] # => 1 -try: - tup[1] = 3 # => ERROR: no method setindex!((Int64,Int64,Int64),Int64,Int64) +try + tup[1] = 3 + # => ERROR: MethodError: no method matching + # setindex!(::Tuple{Int64,Int64,Int64}, ::Int64, ::Int64) catch e println(e) end -# 大多数组的函数同样支持 tuples +# 大多数组的函数同样支持元组 length(tup) # => 3 -tup[1:2] # => (1,2) -in(2, tup) # => true +tup[1:2] # => (1,2) +in(2, tup) # => true -# 可以将 tuples 元素分别赋给变量 -a, b, c = (1, 2, 3) # => (1,2,3) # a is now 1, b is now 2 and c is now 3 +# 可以将元组的元素解包赋给变量 +a, b, c = (1, 2, 3) # => (1,2,3) +a # => 1 +b # => 2 +c # => 3 # 不用括号也可以 -d, e, f = 4, 5, 6 # => (4,5,6) +d, e, f = 4, 5, 6 # => (4,5,6) +d # => 4 +e # => 5 +f # => 6 # 单元素 tuple 不等于其元素值 (1,) == 1 # => false -(1) == 1 # => true +(1) == 1 # => true # 交换值 -e, d = d, e # => (5,4) # d is now 5 and e is now 4 +e, d = d, e # => (5,4) +d # => 5 +e # => 4 -# 字典Dictionaries store mappings -empty_dict = Dict() # => Dict{Any,Any}() +# 字典用于储存映射(mappings)(键值对) +empty_dict = Dict() # => Dict{Any,Any} with 0 entries # 也可以用字面量创建字典 -filled_dict = ["one"=> 1, "two"=> 2, "three"=> 3] -# => Dict{ASCIIString,Int64} +filled_dict = Dict("one" => 1, "two" => 2, "three" => 3) +# => Dict{String,Int64} with 3 entries: +# => "two" => 2, "one" => 1, "three" => 3 # 用 [] 获得键值 filled_dict["one"] # => 1 # 获得所有键 keys(filled_dict) -# => KeyIterator{Dict{ASCIIString,Int64}}(["three"=>3,"one"=>1,"two"=>2]) +# => Base.KeySet for a Dict{String,Int64} with 3 entries. Keys: +# => "two", "one", "three" # 注意,键的顺序不是插入时的顺序 # 获得所有值 values(filled_dict) -# => ValueIterator{Dict{ASCIIString,Int64}}(["three"=>3,"one"=>1,"two"=>2]) +# => Base.ValueIterator for a Dict{String,Int64} with 3 entries. Values: +# => 2, 1, 3 # 注意,值的顺序也一样 # 用 in 检查键值是否已存在,用 haskey 检查键是否存在 -in(("one", 1), filled_dict) # => true -in(("two", 3), filled_dict) # => false -haskey(filled_dict, "one") # => true -haskey(filled_dict, 1) # => false +in(("one" => 1), filled_dict) # => true +in(("two" => 3), filled_dict) # => false +haskey(filled_dict, "one") # => true +haskey(filled_dict, 1) # => false # 获取不存在的键的值会抛出异常 try - filled_dict["four"] # => ERROR: key not found: four in getindex at dict.jl:489 + filled_dict["four"] # => ERROR: KeyError: key "four" not found catch e println(e) end # 使用 get 可以提供默认值来避免异常 # get(dictionary,key,default_value) -get(filled_dict,"one",4) # => 1 -get(filled_dict,"four",4) # => 4 +get(filled_dict, "one", 4) # => 1 +get(filled_dict, "four", 4) # => 4 -# 用 Sets 表示无序不可重复的值的集合 -empty_set = Set() # => Set{Any}() -# 初始化一个 Set 并定义其值 -filled_set = Set(1,2,2,3,4) # => Set{Int64}(1,2,3,4) +# Set 表示无序不可重复的值的集合 +empty_set = Set() # => Set(Any[]) +# 初始化一个带初值的 Set +filled_set = Set([1, 2, 2, 3, 4]) # => Set([4, 2, 3, 1]) -# 添加值 -push!(filled_set,5) # => Set{Int64}(5,4,2,3,1) +# 新增值 +push!(filled_set, 5) # => Set([4, 2, 3, 5, 1]) -# 检查是否存在某值 -in(2, filled_set) # => true -in(10, filled_set) # => false +# 检查 Set 中是否存在某值 +in(2, filled_set) # => true +in(10, filled_set) # => false # 交集,并集,差集 -other_set = Set(3, 4, 5, 6) # => Set{Int64}(6,4,5,3) -intersect(filled_set, other_set) # => Set{Int64}(3,4,5) -union(filled_set, other_set) # => Set{Int64}(1,2,3,4,5,6) -setdiff(Set(1,2,3,4),Set(2,3,5)) # => Set{Int64}(1,4) - +other_set = Set([3, 4, 5, 6]) # => Set([4, 3, 5, 6]) +intersect(filled_set, other_set) # => Set([4, 3, 5]) +union(filled_set, other_set) # => Set([4, 2, 3, 5, 6, 1]) +setdiff(Set([1,2,3,4]), Set([2,3,5])) # => Set([4, 1]) #################################################### -## 3. 控制流 +## 3. 控制语句 #################################################### # 声明一个变量 some_var = 5 -# 这是一个 if 语句,缩进不是必要的 +# 这是一个 if 语句块,其中的缩进不是必须的 if some_var > 10 println("some_var is totally bigger than 10.") -elseif some_var < 10 # elseif 是可选的. +elseif some_var < 10 # elseif 是可选的 println("some_var is smaller than 10.") -else # else 也是可选的. +else # else 也是可选的 println("some_var is indeed 10.") end -# => prints "some var is smaller than 10" +# => some_var is smaller than 10. # For 循环遍历 -# Iterable 类型包括 Range, Array, Set, Dict, 以及 String. -for animal=["dog", "cat", "mouse"] +# 可迭代的类型包括:Range, Array, Set, Dict 和 AbstractString +for animal = ["dog", "cat", "mouse"] println("$animal is a mammal") - # 可用 $ 将 variables 或 expression 转换为字符串into strings + # 你可以用 $ 将变量或表达式插入字符串中 end -# prints: -# dog is a mammal -# cat is a mammal -# mouse is a mammal +# => dog is a mammal +# => cat is a mammal +# => mouse is a mammal -# You can use 'in' instead of '='. +# 你也可以不用「=」而使用「in」 for animal in ["dog", "cat", "mouse"] println("$animal is a mammal") end -# prints: -# dog is a mammal -# cat is a mammal -# mouse is a mammal +# => dog is a mammal +# => cat is a mammal +# => mouse is a mammal -for a in ["dog"=>"mammal","cat"=>"mammal","mouse"=>"mammal"] - println("$(a[1]) is a $(a[2])") +for pair in Dict("dog" => "mammal", "cat" => "mammal", "mouse" => "mammal") + from, to = pair + println("$from is a $to") end -# prints: -# dog is a mammal -# cat is a mammal -# mouse is a mammal +# => mouse is a mammal +# => cat is a mammal +# => dog is a mammal +# 注意!这里的输出顺序和上面的不同 -for (k,v) in ["dog"=>"mammal","cat"=>"mammal","mouse"=>"mammal"] +for (k, v) in Dict("dog" => "mammal", "cat" => "mammal", "mouse" => "mammal") println("$k is a $v") end -# prints: -# dog is a mammal -# cat is a mammal -# mouse is a mammal +# => mouse is a mammal +# => cat is a mammal +# => dog is a mammal # While 循环 -x = 0 -while x < 4 - println(x) - x += 1 # x = x + 1 +let x = 0 + while x < 4 + println(x) + x += 1 # x = x + 1 的缩写 + end end -# prints: -# 0 -# 1 -# 2 -# 3 +# => 0 +# => 1 +# => 2 +# => 3 # 用 try/catch 处理异常 try - error("help") + error("help") catch e - println("caught it $e") + println("caught it $e") end # => caught it ErrorException("help") - #################################################### ## 4. 函数 #################################################### -# 用关键字 'function' 可创建一个新函数 -#function name(arglist) -# body... -#end +# 关键字 function 用于定义函数 +# function name(arglist) +# body... +# end function add(x, y) println("x is $x and y is $y") - # 最后一行语句的值为返回 + # 函数会返回最后一行的值 x + y end -add(5, 6) # => 在 "x is 5 and y is 6" 后会打印 11 +add(5, 6) +# => x is 5 and y is 6 +# => 11 + +# 更紧凑的定义函数 +f_add(x, y) = x + y # => f_add (generic function with 1 method) +f_add(3, 4) # => 7 + +# 函数可以将多个值作为元组返回 +fn(x, y) = x + y, x - y # => fn (generic function with 1 method) +fn(3, 4) # => (7, -1) # 还可以定义接收可变长参数的函数 function varargs(args...) return args - # 关键字 return 可在函数内部任何地方返回 + # 使用 return 可以在函数内的任何地方返回 end # => varargs (generic function with 1 method) varargs(1,2,3) # => (1,2,3) -# 省略号 ... 被称为 splat. +# 省略号「...」称为 splat # 刚刚用在了函数定义中 -# 还可以用在函数的调用 -# Array 或者 Tuple 的内容会变成参数列表 -Set([1,2,3]) # => Set{Array{Int64,1}}([1,2,3]) # 获得一个 Array 的 Set -Set([1,2,3]...) # => Set{Int64}(1,2,3) # 相当于 Set(1,2,3) +# 在调用函数时也可以使用它,此时它会把数组或元组解包为参数列表 +add([5,6]...) # 等价于 add(5,6) -x = (1,2,3) # => (1,2,3) -Set(x) # => Set{(Int64,Int64,Int64)}((1,2,3)) # 一个 Tuple 的 Set -Set(x...) # => Set{Int64}(2,3,1) +x = (5, 6) # => (5,6) +add(x...) # 等价于 add(5,6) - -# 可定义可选参数的函数 -function defaults(a,b,x=5,y=6) +# 可定义带可选参数的函数 +function defaults(a, b, x=5, y=6) return "$a $b and $x $y" end +# => defaults (generic function with 3 methods) -defaults('h','g') # => "h g and 5 6" -defaults('h','g','j') # => "h g and j 6" -defaults('h','g','j','k') # => "h g and j k" +defaults('h', 'g') # => "h g and 5 6" +defaults('h', 'g', 'j') # => "h g and j 6" +defaults('h', 'g', 'j', 'k') # => "h g and j k" try - defaults('h') # => ERROR: no method defaults(Char,) - defaults() # => ERROR: no methods defaults() + defaults('h') # => ERROR: MethodError: no method matching defaults(::Char) + defaults() # => ERROR: MethodError: no method matching defaults() catch e println(e) end -# 还可以定义键值对的参数 -function keyword_args(;k1=4,name2="hello") # note the ; - return ["k1"=>k1,"name2"=>name2] +# 还可以定义带关键字参数的函数 +function keyword_args(;k1=4, name2="hello") # 注意分号 ';' + return Dict("k1" => k1, "name2" => name2) end +# => keyword_args (generic function with 1 method) -keyword_args(name2="ness") # => ["name2"=>"ness","k1"=>4] -keyword_args(k1="mine") # => ["k1"=>"mine","name2"=>"hello"] -keyword_args() # => ["name2"=>"hello","k1"=>4] +keyword_args(name2="ness") # => ["name2"=>"ness", "k1"=>4] +keyword_args(k1="mine") # => ["name2"=>"hello", "k1"=>"mine"] +keyword_args() # => ["name2"=>"hello", "k1"=>4] -# 可以组合各种类型的参数在同一个函数的参数列表中 +# 可以在一个函数中组合各种类型的参数 function all_the_args(normal_arg, optional_positional_arg=2; keyword_arg="foo") println("normal arg: $normal_arg") println("optional arg: $optional_positional_arg") println("keyword arg: $keyword_arg") end +# => all_the_args (generic function with 2 methods) all_the_args(1, 3, keyword_arg=4) -# prints: -# normal arg: 1 -# optional arg: 3 -# keyword arg: 4 +# => normal arg: 1 +# => optional arg: 3 +# => keyword arg: 4 # Julia 有一等函数 function create_adder(x) @@ -443,14 +508,16 @@ function create_adder(x) end return adder end +# => create_adder (generic function with 1 method) # 这是用 "stabby lambda syntax" 创建的匿名函数 (x -> x > 2)(3) # => true -# 这个函数和上面的 create_adder 一模一样 +# 这个函数和上面的 create_adder 是等价的 function create_adder(x) y -> x + y end +# => create_adder (generic function with 1 method) # 你也可以给内部函数起个名字 function create_adder(x) @@ -459,18 +526,19 @@ function create_adder(x) end adder end +# => create_adder (generic function with 1 method) -add_10 = create_adder(10) -add_10(3) # => 13 - +add_10 = create_adder(10) # => (::getfield(Main, Symbol("#adder#11")){Int64}) + # (generic function with 1 method) +add_10(3) # => 13 # 内置的高阶函数有 -map(add_10, [1,2,3]) # => [11, 12, 13] -filter(x -> x > 5, [3, 4, 5, 6, 7]) # => [6, 7] +map(add_10, [1,2,3]) # => [11, 12, 13] +filter(x -> x > 5, [3, 4, 5, 6, 7]) # => [6, 7] -# 还可以使用 list comprehensions 替代 map -[add_10(i) for i=[1, 2, 3]] # => [11, 12, 13] -[add_10(i) for i in [1, 2, 3]] # => [11, 12, 13] +# 还可以使用 list comprehensions 让 map 更美观 +[add_10(i) for i = [1, 2, 3]] # => [11, 12, 13] +[add_10(i) for i in [1, 2, 3]] # => [11, 12, 13] #################################################### ## 5. 类型 @@ -482,248 +550,315 @@ filter(x -> x > 5, [3, 4, 5, 6, 7]) # => [6, 7] typeof(5) # => Int64 # 类型是一等值 -typeof(Int64) # => DataType -typeof(DataType) # => DataType +typeof(Int64) # => DataType +typeof(DataType) # => DataType # DataType 是代表类型的类型,也代表他自己的类型 -# 类型可用作文档化,优化,以及调度 -# 并不是静态检查类型 +# 类型可用于文档化代码、执行优化以及多重派分(dispatch) +# Julia 并不只是静态的检查类型 # 用户还可以自定义类型 -# 跟其他语言的 records 或 structs 一样 -# 用 `type` 关键字定义新的类型 +# 就跟其它语言的 record 或 struct 一样 +# 用 `struct` 关键字定义新的类型 -# type Name +# struct Name # field::OptionalType # ... # end -type Tiger - taillength::Float64 - coatcolor # 不附带类型标注的相当于 `::Any` +struct Tiger + taillength::Float64 + coatcolor # 不带类型标注相当于 `::Any` end -# 构造函数参数是类型的属性 -tigger = Tiger(3.5,"orange") # => Tiger(3.5,"orange") +# 默认构造函数的参数是类型的属性,按类型定义中的顺序排列 +tigger = Tiger(3.5, "orange") # => Tiger(3.5, "orange") # 用新类型作为构造函数还会创建一个类型 -sherekhan = typeof(tigger)(5.6,"fire") # => Tiger(5.6,"fire") +sherekhan = typeof(tigger)(5.6, "fire") # => Tiger(5.6, "fire") -# struct 类似的类型被称为具体类型 -# 他们可被实例化但不能有子类型 +# 类似 struct 的类型被称为具体类型 +# 它们可被实例化,但不能有子类型 # 另一种类型是抽象类型 -# abstract Name -abstract Cat # just a name and point in the type hierarchy +# 抽象类型名 +abstract type Cat end # 仅仅是指向类型结构层次的一个名称 -# 抽象类型不能被实例化,但是可以有子类型 +# 抽象类型不能被实例化,但可以有子类型 # 例如,Number 就是抽象类型 -subtypes(Number) # => 6-element Array{Any,1}: - # Complex{Float16} - # Complex{Float32} - # Complex{Float64} - # Complex{T<:Real} - # ImaginaryUnit - # Real -subtypes(Cat) # => 0-element Array{Any,1} - -# 所有的类型都有父类型; 可以用函数 `super` 得到父类型. +subtypes(Number) # => 2-element Array{Any,1}: + # => Complex + # => Real +subtypes(Cat) # => 0-element Array{Any,1} + +# AbstractString,类如其名,也是一个抽象类型 +subtypes(AbstractString) # => 4-element Array{Any,1}: + # => String + # => SubString + # => SubstitutionString + # => Test.GenericString + +# 所有的类型都有父类型。可以用函数 `supertype` 得到父类型 typeof(5) # => Int64 -super(Int64) # => Signed -super(Signed) # => Real -super(Real) # => Number -super(Number) # => Any -super(super(Signed)) # => Number -super(Any) # => Any -# 所有这些类型,除了 Int64, 都是抽象类型. - -# <: 是类型集成操作符 -type Lion <: Cat # Lion 是 Cat 的子类型 - mane_color - roar::String +supertype(Int64) # => Signed +supertype(Signed) # => Integer +supertype(Integer) # => Real +supertype(Real) # => Number +supertype(Number) # => Any +supertype(supertype(Signed)) # => Real +supertype(Any) # => Any +# 除了 Int64 外,其余的类型都是抽象类型 +typeof("fire") # => String +supertype(String) # => AbstractString +supertype(AbstractString) # => Any +supertype(SubString) # => AbstractString + +# <: 是子类型化操作符 +struct Lion <: Cat # Lion 是 Cat 的子类型 + mane_color + roar::AbstractString end # 可以继续为你的类型定义构造函数 -# 只需要定义一个同名的函数 -# 并调用已有的构造函数设置一个固定参数 -Lion(roar::String) = Lion("green",roar) -# 这是一个外部构造函数,因为他再类型定义之外 - -type Panther <: Cat # Panther 也是 Cat 的子类型 - eye_color - Panther() = new("green") - # Panthers 只有这个构造函数,没有默认构造函数 +# 只需要定义一个与类型同名的函数,并调用已有的构造函数得到正确的类型 +Lion(roar::AbstractString) = Lion("green", roar) # => Lion +# 这是一个外部构造函数,因为它在类型定义之外 + +struct Panther <: Cat # Panther 也是 Cat 的子类型 + eye_color + Panther() = new("green") + # Panthers 只有这个构造函数,没有默认构造函数 end -# 使用内置构造函数,如 Panther,可以让你控制 -# 如何构造类型的值 -# 应该尽可能使用外部构造函数而不是内部构造函数 +# 像 Panther 一样使用内置构造函数,让你可以控制如何构建类型的值 +# 应该尽量使用外部构造函数,而不是内部构造函数 #################################################### ## 6. 多分派 #################################################### -# 在Julia中, 所有的具名函数都是类属函数 -# 这意味着他们都是有很大小方法组成的 -# 每个 Lion 的构造函数都是类属函数 Lion 的方法 +# Julia 中所有的函数都是通用函数,或者叫做泛型函数(generic functions) +# 也就是说这些函数都是由许多小方法组合而成的 +# Lion 的每一种构造函数都是通用函数 Lion 的一个方法 # 我们来看一个非构造函数的例子 +# 首先,让我们定义一个函数 meow -# Lion, Panther, Tiger 的 meow 定义为 +# Lion, Panther, Tiger 的 meow 定义分别为 function meow(animal::Lion) - animal.roar # 使用点符号访问属性 + animal.roar # 使用点记号「.」访问属性 end +# => meow (generic function with 1 method) function meow(animal::Panther) - "grrr" + "grrr" end +# => meow (generic function with 2 methods) function meow(animal::Tiger) - "rawwwr" + "rawwwr" end +# => meow (generic function with 3 methods) # 试试 meow 函数 -meow(tigger) # => "rawwr" -meow(Lion("brown","ROAAR")) # => "ROAAR" +meow(tigger) # => "rawwwr" +meow(Lion("brown", "ROAAR")) # => "ROAAR" meow(Panther()) # => "grrr" -# 再看看层次结构 -issubtype(Tiger,Cat) # => false -issubtype(Lion,Cat) # => true -issubtype(Panther,Cat) # => true +# 回顾类型的层次结构 +Tiger <: Cat # => false +Lion <: Cat # => true +Panther <: Cat # => true -# 定义一个接收 Cats 的函数 +# 定义一个接收 Cat 类型的函数 function pet_cat(cat::Cat) - println("The cat says $(meow(cat))") + println("The cat says $(meow(cat))") end +# => pet_cat (generic function with 1 method) -pet_cat(Lion("42")) # => prints "The cat says 42" +pet_cat(Lion("42")) # => The cat says 42 try - pet_cat(tigger) # => ERROR: no method pet_cat(Tiger,) + pet_cat(tigger) # => ERROR: MethodError: no method matching pet_cat(::Tiger) catch e println(e) end # 在面向对象语言中,通常都是单分派 -# 这意味着分派方法是通过第一个参数的类型决定的 -# 在Julia中, 所有参数类型都会被考虑到 +# 这意味着使用的方法取决于第一个参数的类型 +# 而 Julia 中选择方法时会考虑到所有参数的类型 -# 让我们定义有多个参数的函数,好看看区别 -function fight(t::Tiger,c::Cat) - println("The $(t.coatcolor) tiger wins!") +# 让我们定义一个有更多参数的函数,这样我们就能看出区别 +function fight(t::Tiger, c::Cat) + println("The $(t.coatcolor) tiger wins!") end # => fight (generic function with 1 method) -fight(tigger,Panther()) # => prints The orange tiger wins! -fight(tigger,Lion("ROAR")) # => prints The orange tiger wins! +fight(tigger, Panther()) # => The orange tiger wins! +fight(tigger, Lion("ROAR")) # => fight(tigger, Lion("ROAR")) -# 让我们修改一下传入具体为 Lion 类型时的行为 -fight(t::Tiger,l::Lion) = println("The $(l.mane_color)-maned lion wins!") +# 让我们修改一下传入 Lion 类型时的行为 +fight(t::Tiger, l::Lion) = println("The $(l.mane_color)-maned lion wins!") # => fight (generic function with 2 methods) -fight(tigger,Panther()) # => prints The orange tiger wins! -fight(tigger,Lion("ROAR")) # => prints The green-maned lion wins! +fight(tigger, Panther()) # => The orange tiger wins! +fight(tigger, Lion("ROAR")) # => The green-maned lion wins! -# 把 Tiger 去掉 -fight(l::Lion,c::Cat) = println("The victorious cat says $(meow(c))") +# 我们不需要一只老虎参与战斗 +fight(l::Lion, c::Cat) = println("The victorious cat says $(meow(c))") # => fight (generic function with 3 methods) -fight(Lion("balooga!"),Panther()) # => prints The victorious cat says grrr +fight(Lion("balooga!"), Panther()) # => The victorious cat says grrr try - fight(Panther(),Lion("RAWR")) # => ERROR: no method fight(Panther,Lion) -catch + fight(Panther(), Lion("RAWR")) + # => ERROR: MethodError: no method matching fight(::Panther, ::Lion) + # => Closest candidates are: + # => fight(::Tiger, ::Lion) at ... + # => fight(::Tiger, ::Cat) at ... + # => fight(::Lion, ::Cat) at ... + # => ... +catch e + println(e) end -# 在试试让 Cat 在前面 -fight(c::Cat,l::Lion) = println("The cat beats the Lion") -# => Warning: New definition -# fight(Cat,Lion) at none:1 -# is ambiguous with -# fight(Lion,Cat) at none:2. -# Make sure -# fight(Lion,Lion) -# is defined first. -#fight (generic function with 4 methods) - -# 警告说明了无法判断使用哪个 fight 方法 -fight(Lion("RAR"),Lion("brown","rarrr")) # => prints The victorious cat says rarrr -# 结果在老版本 Julia 中可能会不一样 - -fight(l::Lion,l2::Lion) = println("The lions come to a tie") -fight(Lion("RAR"),Lion("brown","rarrr")) # => prints The lions come to a tie - - -# Under the hood -# 你还可以看看 llvm 以及生成的汇编代码 - -square_area(l) = l * l # square_area (generic function with 1 method) - -square_area(5) #25 - -# 给 square_area 一个整形时发生什么 -code_native(square_area, (Int32,)) - # .section __TEXT,__text,regular,pure_instructions - # Filename: none - # Source line: 1 # Prologue - # push RBP - # mov RBP, RSP - # Source line: 1 - # movsxd RAX, EDI # Fetch l from memory? - # imul RAX, RAX # Square l and store the result in RAX - # pop RBP # Restore old base pointer - # ret # Result will still be in RAX - -code_native(square_area, (Float32,)) - # .section __TEXT,__text,regular,pure_instructions - # Filename: none - # Source line: 1 - # push RBP - # mov RBP, RSP - # Source line: 1 - # vmulss XMM0, XMM0, XMM0 # Scalar single precision multiply (AVX) - # pop RBP - # ret - -code_native(square_area, (Float64,)) - # .section __TEXT,__text,regular,pure_instructions - # Filename: none - # Source line: 1 - # push RBP - # mov RBP, RSP - # Source line: 1 - # vmulsd XMM0, XMM0, XMM0 # Scalar double precision multiply (AVX) - # pop RBP - # ret - # -# 注意 只要参数中又浮点类型,Julia 就使用浮点指令 +# 试试把 Cat 放在前面 +fight(c::Cat, l::Lion) = println("The cat beats the Lion") +# => fight (generic function with 4 methods) + +# 由于无法判断该使用哪个 fight 方法,而产生了错误 +try + fight(Lion("RAR"), Lion("brown", "rarrr")) + # => ERROR: MethodError: fight(::Lion, ::Lion) is ambiguous. Candidates: + # => fight(c::Cat, l::Lion) in Main at ... + # => fight(l::Lion, c::Cat) in Main at ... + # => Possible fix, define + # => fight(::Lion, ::Lion) + # => ... +catch e + println(e) +end +# 在不同版本的 Julia 中错误信息可能有所不同 + +fight(l::Lion, l2::Lion) = println("The lions come to a tie") +# => fight (generic function with 5 methods) +fight(Lion("RAR"), Lion("brown", "rarrr")) # => The lions come to a tie + + +# 深入编译器之后 +# 你还可以看看 llvm 以及它生成的汇编代码 + +square_area(l) = l * l # => square_area (generic function with 1 method) +square_area(5) # => 25 + +# 当我们喂给 square_area 一个整数时会发生什么? +code_native(square_area, (Int32,), syntax = :intel) + # .text + # ; Function square_area { + # ; Location: REPL[116]:1 # 函数序言 (Prologue) + # push rbp + # mov rbp, rsp + # ; Function *; { + # ; Location: int.jl:54 + # imul ecx, ecx # 求 l 的平方,并把结果放在 ECX 中 + # ;} + # mov eax, ecx + # pop rbp # 还原旧的基址指针(base pointer) + # ret # 返回值放在 EAX 中 + # nop dword ptr [rax + rax] + # ;} +# 使用 syntax 参数指定输出语法。默认为 AT&T 格式,这里指定为 Intel 格式 + +code_native(square_area, (Float32,), syntax = :intel) + # .text + # ; Function square_area { + # ; Location: REPL[116]:1 + # push rbp + # mov rbp, rsp + # ; Function *; { + # ; Location: float.jl:398 + # vmulss xmm0, xmm0, xmm0 # 标量双精度乘法 (AVX) + # ;} + # pop rbp + # ret + # nop word ptr [rax + rax] + # ;} + +code_native(square_area, (Float64,), syntax = :intel) + # .text + # ; Function square_area { + # ; Location: REPL[116]:1 + # push rbp + # mov rbp, rsp + # ; Function *; { + # ; Location: float.jl:399 + # vmulsd xmm0, xmm0, xmm0 # 标量双精度乘法 (AVX) + # ;} + # pop rbp + # ret + # nop word ptr [rax + rax] + # ;} + +# 注意!只要参数中有浮点数,Julia 就会使用浮点指令 # 让我们计算一下圆的面积 -circle_area(r) = pi * r * r # circle_area (generic function with 1 method) -circle_area(5) # 78.53981633974483 - -code_native(circle_area, (Int32,)) - # .section __TEXT,__text,regular,pure_instructions - # Filename: none - # Source line: 1 - # push RBP - # mov RBP, RSP - # Source line: 1 - # vcvtsi2sd XMM0, XMM0, EDI # Load integer (r) from memory - # movabs RAX, 4593140240 # Load pi - # vmulsd XMM1, XMM0, QWORD PTR [RAX] # pi * r - # vmulsd XMM0, XMM0, XMM1 # (pi * r) * r - # pop RBP - # ret - # - -code_native(circle_area, (Float64,)) - # .section __TEXT,__text,regular,pure_instructions - # Filename: none - # Source line: 1 - # push RBP - # mov RBP, RSP - # movabs RAX, 4593140496 - # Source line: 1 - # vmulsd XMM1, XMM0, QWORD PTR [RAX] - # vmulsd XMM0, XMM1, XMM0 - # pop RBP - # ret - # +circle_area(r) = pi * r * r # => circle_area (generic function with 1 method) +circle_area(5) # => 78.53981633974483 + +code_native(circle_area, (Int32,), syntax = :intel) + # .text + # ; Function circle_area { + # ; Location: REPL[121]:1 + # push rbp + # mov rbp, rsp + # ; Function *; { + # ; Location: operators.jl:502 + # ; Function *; { + # ; Location: promotion.jl:314 + # ; Function promote; { + # ; Location: promotion.jl:284 + # ; Function _promote; { + # ; Location: promotion.jl:261 + # ; Function convert; { + # ; Location: number.jl:7 + # ; Function Type; { + # ; Location: float.jl:60 + # vcvtsi2sd xmm0, xmm0, ecx # 从内存中读取整数 r + # movabs rax, 497710928 # 读取 pi + # ;}}}}} + # ; Function *; { + # ; Location: float.jl:399 + # vmulsd xmm1, xmm0, qword ptr [rax] # pi * r + # vmulsd xmm0, xmm1, xmm0 # (pi * r) * r + # ;}} + # pop rbp + # ret + # nop dword ptr [rax] + # ;} + +code_native(circle_area, (Float64,), syntax = :intel) + # .text + # ; Function circle_area { + # ; Location: REPL[121]:1 + # push rbp + # mov rbp, rsp + # movabs rax, 497711048 + # ; Function *; { + # ; Location: operators.jl:502 + # ; Function *; { + # ; Location: promotion.jl:314 + # ; Function *; { + # ; Location: float.jl:399 + # vmulsd xmm1, xmm0, qword ptr [rax] + # ;}}} + # ; Function *; { + # ; Location: float.jl:399 + # vmulsd xmm0, xmm1, xmm0 + # ;} + # pop rbp + # ret + # nop dword ptr [rax + rax] + # ;} ``` + +## 拓展阅读材料 + +你可以在 [Julia 中文文档](http://docs.juliacn.com/latest/) / [Julia 文档(en)](https://docs.julialang.org/) +中获得关于 Julia 的更多细节。 + +如果有任何问题可以去 [Julia 中文社区](http://discourse.juliacn.com/) / [官方社区(en)](https://discourse.julialang.org/) 提问,大家对待新手都非常的友好。 diff --git a/zh-cn/kotlin-cn.html.markdown b/zh-cn/kotlin-cn.html.markdown index 5d655029..f6dcd847 100644 --- a/zh-cn/kotlin-cn.html.markdown +++ b/zh-cn/kotlin-cn.html.markdown @@ -22,7 +22,7 @@ package com.learnxinyminutes.kotlin /* Kotlin程序的入口点是一个"main"函数 -该函数传递一个包含任何命令行参数的数组。 +该函数传递一个包含所有命令行参数的数组。 */ fun main(args: Array<String>) { /* @@ -67,10 +67,10 @@ fun helloWorld(val name : String) { 模板表达式从一个美元符号($)开始。 */ val fooTemplateString = "$fooString has ${fooString.length} characters" - println(fooTemplateString) + println(fooTemplateString) // => 输出 My String Is Here! has 18 characters /* - 当某个变量的值可以为 null 的时候,我们必须被明确指定它是可为空的。 + 当某个变量的值可以为 null 的时候,我们必须明确指定它是可为空的。 在变量声明处的类型后面加上?来标识它是可为空的。 我们可以用?.操作符来访问可为空的变量。 我们可以用?:操作符来指定一个在变量为空时使用的替代值。 @@ -96,24 +96,24 @@ fun helloWorld(val name : String) { println(hello()) // => Hello, world! /* - 用"vararg"关键字来修饰一个函数的参数来允许可变参数传递给该函数 + 函数的可变参数可使用 "vararg" 关键字来修饰 */ fun varargExample(vararg names: Int) { println("Argument has ${names.size} elements") } - varargExample() // => Argument has 0 elements - varargExample(1) // => Argument has 1 elements - varargExample(1, 2, 3) // => Argument has 3 elements + varargExample() // => 传入 0 个参数 + varargExample(1) // => 传入 1 个参数 + varargExample(1, 2, 3) // => 传入 3 个参数 /* - 当函数只包含一个单独的表达式时,大括号可以被省略。 - 函数体可以被指定在一个=符号后面。 + 当函数只包含一个单独的表达式时,大括号可以省略。 + 函数体可以写在一个=符号后面。 */ fun odd(x: Int): Boolean = x % 2 == 1 println(odd(6)) // => false println(odd(7)) // => true - // 如果返回值类型可以被推断,那么我们不需要指定它。 + // 如果返回值类型可以推断,那么我们不需要指定它。 fun even(x: Int) = x % 2 == 0 println(even(6)) // => true println(even(7)) // => false @@ -122,15 +122,14 @@ fun helloWorld(val name : String) { fun not(f: (Int) -> Boolean) : (Int) -> Boolean { return {n -> !f.invoke(n)} } - // 命名函数可以用::运算符被指定为参数。 + // 普通函数可以用::运算符传入引用作为函数参数。 val notOdd = not(::odd) val notEven = not(::even) - // 匿名函数可以被指定为参数。 + // lambda 表达式可以直接作为参数传递。 val notZero = not {n -> n == 0} /* - 如果一个匿名函数只有一个参数 - 那么它的声明可以被省略(连同->)。 - 这个参数的名字是"it"。 + 如果一个 lambda 表达式只有一个参数 + 那么它的声明可以省略(连同->),内部以 "it" 引用。 */ val notPositive = not {it > 0} for (i in 0..4) { @@ -152,7 +151,7 @@ fun helloWorld(val name : String) { 注意,Kotlin没有"new"关键字。 */ val fooExampleClass = ExampleClass(7) - // 可以使用一个点号来调用成员函数。 + // 可以使用一个点号来调用成员方法。 println(fooExampleClass.memberFunction(4)) // => 11 /* 如果使用"infix"关键字来标记一个函数 @@ -162,7 +161,7 @@ fun helloWorld(val name : String) { /* 数据类是创建只包含数据的类的一个简洁的方法。 - "hashCode"、"equals"和"toString"方法将被自动生成。 + "hashCode"、"equals"和"toString"方法将自动生成。 */ data class DataClassExample (val x: Int, val y: Int, val z: Int) val fooData = DataClassExample(1, 2, 4) diff --git a/zh-cn/lambda-calculus-cn.html.markdown b/zh-cn/lambda-calculus-cn.html.markdown new file mode 100644 index 00000000..7719ee71 --- /dev/null +++ b/zh-cn/lambda-calculus-cn.html.markdown @@ -0,0 +1,223 @@ +--- +category: Algorithms & Data Structures +name: Lambda Calculus +lang: zh-cn +contributors: + - ["Max Sun", "http://github.com/maxsun"] + - ["Yan Hui Hang", "http://github.com/yanhh0"] +translators: + - ["Maoyin Sun", "https://github.com/simonmysun"] +--- + +# Lambda 演算 + +Lambda 演算(lambda calculus, λ-calculus), +最初由[阿隆佐·邱奇][]([Alonzo Church][])提出, +是世界上最小的编程语言. +尽管没有数字, 字符串, 布尔或者任何非函数的数据类型, +lambda 演算仍可以表示任何图灵机. + +[阿隆佐·邱奇]: https://zh.wikipedia.org/wiki/%E9%98%BF%E9%9A%86%E4%BD%90%C2%B7%E9%82%B1%E5%A5%87 +[Alonzo Church]: https://en.wikipedia.org/wiki/Alonzo_Church + +Lambda 演算由三种元素组成: **变量**(variables)、**函数**(functions)和**应用**(applications)。 + +| 名称 | 语法 | 示例 | 解释 | +|------|----------------------|-----------|--------------------------------------------------| +| 变量 | `<变量名>` | `x` | 一个名为"x"的变量 | +| 函数 | `λ<参数>.<函数体>` | `λx.x` | 一个以"x"(前者)为参数、以"x"(后者)为函数体的函数 | +| 应用 | `<函数><变量或函数>` | `(λx.x)a` | 以"a"为参数调用函数"λx.x" | + +最基本的函数为恒等函数: `λx.x`, 它等价于`f(x) = x`. +第一个"x"为函数的参数, 第二个为函数体. + +## 自由变量和约束变量: + +- 在函数`λx.x`中, "x"被称作约束变量因为它同时出现在函数体和函数参数中. +- 在`λx.y`中, "y"被称作自由变量因为它没有被预先声明. + +## 求值: + +求值操作是通过[β-归约][]([β-Reduction][])完成的, +它本质上是词法层面上的替换. + +[β-归约]: https://zh.wikipedia.org/wiki/%CE%9B%E6%BC%94%E7%AE%97#'%22%60UNIQ--postMath-0000006F-QINU%60%22'-%E6%AD%B8%E7%B4%84 +[β-Reduction]: https://en.wikipedia.org/wiki/Lambda_calculus#Beta_reduction + +当对表达式`(λx.x)a`求值时, 我们将函数体中所有出现的"x"替换为"a". + +- `(λx.x)a`计算结果为: `a` +- `(λx.y)a`计算结果为: `y` + +你甚至可以创建高阶函数: + +- `(λx.(λy.x))a`计算结果为: `λy.a` + +尽管 lambda 演算传统上仅支持单个参数的函数, +但我们可以通过一种叫作[柯里化][]([Currying][])的技巧创建多个参数的函数. + +[柯里化]: https://zh.wikipedia.org/wiki/%E6%9F%AF%E9%87%8C%E5%8C%96 +[Currying]: https://en.wikipedia.org/wiki/Currying + +- `(λx.λy.λz.xyz)`等价于`f(x, y, z) = ((x y) z)` + +有时`λxy.<body>`与`λx.λy.<body>`可以互换使用. + +---- + +认识到传统的 **lambda 演算没有数字, 字符或者任何非函数的数据类型**很重要. + +## 布尔逻辑: + +在 lambda 演算中没有"真"或"假". 甚至没有 1 或 0. + +作为替换: + +`T`表示为: `λx.λy.x` + +`F`表示为: `λx.λy.y` + +首先, 我们可以定义一个"if"函数`λbtf`, 它当`b`为真时返回`t`, +`b`为假时返回`f` + +`IF`等价于: `λb.λt.λf.b t f` + +通过`IF`, 我们可以定义基本的布尔逻辑运算符: + +`a AND b`等价于: `λab.IF a b F` + +`a OR b`等价于: `λab.IF a T b` + +`NOT a`等价于: `λa.IF a F T` + +*注意: `IF a b c`本质上指: `IF((a b) c)`* + +## 数字: + +尽管 lambda 演算中没有数字, +我们还可以用[邱奇编码][]([Church numerals][])将数字嵌入到 lambda 演算中. + +[邱奇编码]: https://zh.wikipedia.org/wiki/%E9%82%B1%E5%A5%87%E7%BC%96%E7%A0%81 +[Church numerals]: https://en.wikipedia.org/wiki/Church_encoding + +对于任意数字 n: <code>n = λf.f<sup>n</sup></code> 所以: + +`0 = λf.λx.x` + +`1 = λf.λx.f x` + +`2 = λf.λx.f(f x)` + +`3 = λf.λx.f(f(f x))` + +要增加一个邱奇数, 我们使用后继函数`S(n) = n + 1`: + +`S = λn.λf.λx.f((n f) x)` + +使用后继函数, 我们可以定义加法: + +`ADD = λab.(a S)b` + +**挑战**: 试定义乘法函数! + +## 变得更小: SKI, SK 和 Iota + +### SKI 组合子演算 + +令 S, K, I 为下列函数: + +`I x = x` + +`K x y = x` + +`S x y z = x z (y z)` + +我们可以将 lambda 演算中的表达式转换为 SKI 组合子演算中的表达式: + +1. `λx.x = I` +2. `λx.c = Kc` +3. `λx.(y z) = S (λx.y) (λx.z)` + +以邱奇数 2 为例: + +`2 = λf.λx.f(f x)` + +对于里面的部分 `λx.f(f x)`: + +``` + λx.f(f x) += S (λx.f) (λx.(f x)) (case 3) += S (K f) (S (λx.f) (λx.x)) (case 2, 3) += S (K f) (S (K f) I) (case 2, 1) +``` + +所以: + +``` + 2 += λf.λx.f(f x) += λf.(S (K f) (S (K f) I)) += λf.((S (K f)) (S (K f) I)) += S (λf.(S (K f))) (λf.(S (K f) I)) (case 3) +``` + +对于第一个参数`λf.(S (K f))`有: + +``` + λf.(S (K f)) += S (λf.S) (λf.(K f)) (case 3) += S (K S) (S (λf.K) (λf.f)) (case 2, 3) += S (K S) (S (K K) I) (case 2, 3) +``` + +对于第二个参数`λf.(S (K f) I)`有: + +``` + λf.(S (K f) I) += λf.((S (K f)) I) += S (λf.(S (K f))) (λf.I) (case 3) += S (S (λf.S) (λf.(K f))) (K I) (case 2, 3) += S (S (K S) (S (λf.K) (λf.f))) (K I) (case 1, 3) += S (S (K S) (S (K K) I)) (K I) (case 1, 2) +``` + +综上: + +``` + 2 += S (λf.(S (K f))) (λf.(S (K f) I)) += S (S (K S) (S (K K) I)) (S (S (K S) (S (K K) I)) (K I)) +``` + +如果展开这个表达式, 我们最终又会得到邱奇数 2 的相同的表达式. + +### SK 组合子演算 + +SKI 组合子演算还可以进一步简化. 我们可以通过`I = SKK`移除 I 组合子. +我们可以将所有的 `I` 替换为 `SKK`. + +### ι 组合子 + +SK 组合子仍不是最简的. 定义: + +``` +ι = λf.((f S) K) +``` + +我们有: + +``` +I = ιι +K = ι(ιI) = ι(ι(ιι)) +S = ι(K) = ι(ι(ι(ιι))) +``` + +## 更多阅读: + +1. [A Tutorial Introduction to the Lambda Calculus](http://www.inf.fu-berlin.de/lehre/WS03/alpi/lambda.pdf)(英文) +2. [Cornell CS 312 Recitation 26: The Lambda Calculus](https://courses.cs.cornell.edu/cs312/2008sp/recitations/rec26.html)(英文) +3. [Wikipedia - Lambda Calculus](https://en.wikipedia.org/wiki/Lambda_calculus)(英文) +4. [Wikipedia - SKI combinator calculus](https://en.wikipedia.org/wiki/SKI_combinator_calculus)(英文) +5. [Wikipedia - Iota and Jot](https://en.wikipedia.org/wiki/Iota_and_Jot)(英文) +6. [λ演算 - 维基百科,自由的百科全书](https://zh.wikipedia.org/wiki/SKI%E7%BB%84%E5%90%88%E5%AD%90%E6%BC%94%E7%AE%97) +7. [SKI组合子演算 - 维基百科,自由的百科全书](https://zh.wikipedia.org/wiki/SKI%E7%BB%84%E5%90%88%E5%AD%90%E6%BC%94%E7%AE%97) diff --git a/zh-cn/make-cn.html.markdown b/zh-cn/make-cn.html.markdown new file mode 100644 index 00000000..76dde941 --- /dev/null +++ b/zh-cn/make-cn.html.markdown @@ -0,0 +1,263 @@ +--- +category: tool +tool: make +contributors: +- ["Robert Steed", "https://github.com/robochat"] +- ["Jichao Ouyang", "https://github.com/jcouyang"] +translators: +- ["Jichao Ouyang", "https://github.com/jcouyang"] +filename: Makefile-cn +lang: zh-cn +--- + +Makefile 用于定义如何创建目标文件, 比如如何从源码到可执行文件. 创建这一工具的目标是 +减少不必要的编译或者任务.是传说中的 Stuart Feldman 在 1976 年花了一个周末写出来的, +而今仍然使用广泛, 特别是在 Unix 和 Linux 系统上. + +虽然每个语言可能都有相应的或多或少提供 make 的功能, 比如 ruby 的 rake, node 的 gulp, broccoli +, scala 的 sbt 等等. 但是 make 的简洁与高效, 和只做一件事并做到极致的风格, 使其至今仍是无可替代的, +甚至与其他构建工具一起使用也并无冲突. + +尽管有许多的分支和变体, 这篇文章针对是标准的 GNU make. + +```make +# 这行表示注释 + +# 文件名一定要交 Makefile, 大小写区分, 使用 `make <target>` 生成 target +# 如果想要取别的名字, 可以用 `make -f "filename" <target>`. + +# 重要的事情 - 只认识 TAB, 空格是不认的, 但是在 GNU Make 3.82 之后, 可以通过 +# 设置参数 .RECIPEPREFIX 进行修改 + +#----------------------------------------------------------------------- +# 初级 +#----------------------------------------------------------------------- + +# 创建一个 target 的规则非常简单 +# targets : prerequisites +# recipe +# … +# prerequisites(依赖) 是可选的, recipe(做法) 也可以多个或者不给. + +# 下面这个任务没有给 prerequisites, 只会在目标文件 file0.txt 文件不存在时执行 +file0.txt: + echo "foo" > file0.txt + # 试试 `make file0.txt` + # 或者直接 `make`, 因为第一个任务是默认任务. + # 注意: 即使是这些注释, 如果前面有 TAB, 也会发送给 shell, 注意看 `make file0.txt` 输出 + +# 如果提供 prerequisites, 则只有 prerequisites 比 target 新时会执行 +# 比如下面这个任务只有当 file0.txt 比 file1.txt 新时才会执行. +file1.txt: file0.txt + cat file0.txt > file1.txt + # 这里跟shell里的命令式一模一样. + @cat file0.txt >> file1.txt + # @ 不会把命令打印到 stdout. + -@echo 'hello' + # - 意思是发生错误了也没关系. + # 试试 `make file1.txt` 吧. + +# targets 和 prerequisites 都可以是多个, 以空格分割 +file2.txt file3.txt: file0.txt file1.txt + touch file2.txt + touch file3.txt + +# 如果声明重复的 target, make 会给一个 warning, 后面会覆盖前面的 +# 比如重复定义 file2.txt 会得到这样的 warning +# Makefile:46: warning: overriding commands for target `file2.txt' +# Makefile:40: warning: ignoring old commands for target `file2.txt' +file2.txt: file0.txt + touch file2.txt + +# 但是如果不定义任何 recipe, 就不会冲突, 只是多了依赖关系 +file2.txt: file0.txt file3.txt + +#----------------------------------------------------------------------- +# Phony(假的) Targets +#----------------------------------------------------------------------- + +# phony targets 意思是 tagets 并不是文件, 可以想象成一个任务的名字而已. +# 因为不是文件, 无法比对是否有更新, 所以每次make都会执行. +all: maker process + +# 依赖于 phony target 的 target 也会每次 make 都执行, 即使 target 是文件 +ex0.txt ex1.txt: maker + +# target 的声明顺序并不重要, 比如上面的 all 的依赖 maker 现在才声明 +maker: + touch ex0.txt ex1.txt + +# 如果定义的 phony target 与文件名重名, 可以用 .PHONY 显示的指明哪些 targets 是 phony +.PHONY: all maker process +# This is a special target. There are several others. + +# 常用的 phony target 有: all clean install ... + +#----------------------------------------------------------------------- +# 变量与通配符 +#----------------------------------------------------------------------- + +process: file*.txt | dir/a.foo.b # 可以用通配符匹配多个文件作为prerequisites + @echo $^ # $^ 是 prerequisites + @echo $@ # $@ 代表 target, 如果 target 为多个, $@ 代表当前执行的那个 + @echo $< # $< prerequisite 中的第一个 + @echo $? # $? 需要更新的 prerequisite 文件列表 + @echo $+ # $+ 所有依赖, 包括重复的 + @echo $| # $| 竖线后面的 order-only prerequisites + +a.%.b: + @echo $* # $* match 的target % 那部分, 包括路径, 比如 `make dir/a.foo.b` 会打出 `dir/foo` + +# 即便分开定义依赖, $^ 依然能拿到 +process: ex1.txt file0.txt +# 非常智能的, ex1.txt 会被找到, file0.txt 会被去重. + +#----------------------------------------------------------------------- +# 模式匹配 +#----------------------------------------------------------------------- + +# 可以让 make 知道如何转换某些文件到别格式 +# 比如 从 svg 到 png +%.png: %.svg + inkscape --export-png $^ + +# 一旦有需要 foo.png 这个任务就会运行 + +# 路径会被忽略, 所以上面的 target 能匹配所有 png +# 但是如果加了路径, make 会找到最接近的匹配, 如果 +# make small/foo.png (在这之前要先有 small/foo.svg 这个文件) +# 则会匹配下面这个规则 +small/%.png: %.svg + inkscape --export-png --export-dpi 30 $^ + +%.png: %.svg + @echo 重复定义会覆盖前面的, 现在 inkscape 没用了 + +# make 已经有一些内置的规则, 比如从 *.c 到 *.o + +#----------------------------------------------------------------------- +# 变量 +#----------------------------------------------------------------------- +# 其实是宏 macro + +# 变量都是字符串类型, 下面这俩是一样一样的 + +name = Ted +name2="Sarah" + +echo: + @echo $(name) + @echo ${name2} + @echo $name # 这个会被蠢蠢的解析成 $(n)ame. + @echo \"$(name3)\" # 为声明的变量或扩展成空字符串. + @echo $(name4) + @echo $(name5) +# 你可以通过4种方式设置变量. +# 按以下顺序由高到低: +# 1: 命令行参数. 比如试试 `make echo name3=JICHAO` +# 2: Makefile 里面的 +# 3: shell 中的环境变量 +# 4: make 预设的一些变量 + +name4 ?= Jean +# 问号意思是如果 name4 被设置过了, 就不设置了. + +override name5 = David +# 用 override 可以防止命令行参数设置的覆盖 + +name4 +=grey +# 用加号可以连接 (中间用空格分割). + +# 在依赖的地方设置变量 +echo: name2 = Sara2 + +# 还有一些内置的变量 +echo_inbuilt: + echo $(CC) + echo ${CXX)} + echo $(FC) + echo ${CFLAGS)} + echo $(CPPFLAGS) + echo ${CXXFLAGS} + echo $(LDFLAGS) + echo ${LDLIBS} + +#----------------------------------------------------------------------- +# 变量 2 +#----------------------------------------------------------------------- + +# 加个冒号可以声明 Simply expanded variables 即时扩展变量, 即只在声明时扩展一次 +# 之前的等号声明时 recursively expanded 递归扩展 + +var := hello +var2 := $(var) hello + +# 这些变量会在其引用的顺序求值 +# 比如 var3 声明时找不到 var4, var3 会扩展成 `and good luck` +var3 := $(var4) and good luck +# 但是一般的变量会在调用时递归扩展, 先扩展 var5, 再扩展 var4, 所以是正常的 +var5 = $(var4) and good luck +var4 := good night + +echoSEV: + @echo $(var) + @echo $(var2) + @echo $(var3) + @echo $(var4) + @echo $(var5) + +#----------------------------------------------------------------------- +# 函数 +#----------------------------------------------------------------------- + +# make 自带了一些函数. +# wildcard 会将后面的通配符变成一串文件路径 +all_markdown: + @echo $(wildcard *.markdown) +# patsubst 可以做替换, 比如下面会把所有 markdown +# 后缀的文件重命名为 md 后缀 +substitue: * + @echo $(patsubst %.markdown,%.md,$* $^) + +# 函数调用格式是 $(func arg0,arg1,arg2...) + +# 试试 +ls: * + @echo $(filter %.txt, $^) + @echo $(notdir $^) + @echo $(join $(dir $^),$(notdir $^)) + +#----------------------------------------------------------------------- +# Directives +#----------------------------------------------------------------------- + +# 可以用 include 引入别的 Makefile 文件 +# include foo.mk + +sport = tennis +# 流程控制语句 (如if else 等等) 顶格写 +report: +ifeq ($(sport),tennis) + @echo 'game, set, match' +else + @echo "They think it's all over; it is now" +endif + +# 还有 ifneq, ifdef, ifndef + +foo = true + +# 不只是 recipe, 还可以写在外面哟 +ifdef $(foo) +bar = 'bar' +endif + +hellobar: + @echo bar +``` + +### 资源 + ++ GNU Make 官方文档 [HTML](https://www.gnu.org/software/make/manual/) [PDF](https://www.gnu.org/software/make/manual/make.pdf) ++ [software carpentry tutorial](http://swcarpentry.github.io/make-novice/) ++ learn C the hard way [ex2](http://c.learncodethehardway.org/book/ex2.html) [ex28](http://c.learncodethehardway.org/book/ex28.html) diff --git a/zh-cn/markdown-cn.html.markdown b/zh-cn/markdown-cn.html.markdown index 87ed46ad..9b3d96ab 100644 --- a/zh-cn/markdown-cn.html.markdown +++ b/zh-cn/markdown-cn.html.markdown @@ -4,45 +4,74 @@ contributors: - ["Dan Turkel", "http://danturkel.com/"] translators: - ["Fangzhou Chen","https://github.com/FZSS"] + - ["Luffy Zhong", "https://github.com/mengzhongshi"] + - ["Yuchen Liu", "https://github.com/smallg0at"] filename: learnmarkdown-cn.md lang: zh-cn --- Markdown 由 John Gruber 于 2004年创立. 它旨在成为一门容易读写的语法结构,并可以便利地转换成 HTML(以及其他很多)格式。 -欢迎您多多反馈以及分支和请求合并。 - - -```markdown -<!-- Markdown 是 HTML 的父集,所以任何 HTML 文件都是有效的 Markdown。 -这意味着我们可以在 Markdown 里使用任何 HTML 元素,比如注释元素, +在不同的解析器中,Markdown 的实现方法有所不同。 +此教程会指出哪些特征是通用,哪一些只对某一解析器有效。 + +- [HTML标签](#HTML标签) +- [标题](#标题) +- [文本样式](#文本样式) +- [段落](#段落) +- [列表](#列表) +- [代码块](#代码块) +- [水平线分隔](#水平线分隔) +- [链接](#链接) +- [图片](#图片) +- [杂项](#杂项) + +## HTML标签 +Markdown 是 HTML 的父集,所以任何 HTML 文件都是有效的 Markdown。 + +```md +<!--这意味着我们可以在 Markdown 里使用任何 HTML 元素,比如注释元素, 且不会被 Markdown 解析器所影响。不过如果你在 Markdown 文件内创建了 HTML 元素, 你将无法在 HTML 元素的内容中使用 Markdown 语法。--> +``` -<!-- 在不同的解析器中,Markdown 的实现方法有所不同。 -此教程会指出当某功能是否通用及是否只对某一解析器有效。 --> +## 标题 -<!-- 标头 --> -<!-- 通过在文本前加上不同数量的hash(#), 你可以创建相对应的 <h1> -到 <h6> HTML元素。--> +通过在文本前加上不同数量的hash(#), 你可以创建相对应的 `<h1>` 到 `<h6>` HTML元素。 +```md # 这是一个 <h1> ## 这是一个 <h2> ### 这是一个 <h3> #### 这是一个 <h4> ##### 这是一个 <h5> ###### 这是一个 <h6> +``` -<!-- 对于 <h1> 和 <h2> 元素,Markdown 额外提供了两种添加方式。 --> +实际效果(最终显示时会因设置而看起来不同): +# 这是一个 +## 这也是一个 +### 这还是一个 +#### 这依旧是一个 +##### 这真的是一个 +###### 这...是一个 + + +对于 `<h1>` 和 `<h2>` 元素,Markdown 额外提供了两种添加方式。 + +```md 这是一个 h1 ============= 这是一个 h2 ------------- +``` -<!-- 简易文本样式 --> -<!-- 文本的斜体,粗体,和删除线在 Markdown 中可以轻易地被实现。--> +## 文本样式 +文本的*斜体*,**粗体**在 Markdown 中可以轻易实现。 + +```md *此文本为斜体。* _此文本也是。_ @@ -52,40 +81,57 @@ __此文本也是__ ***此文本是斜体加粗体。*** **_或者这样。_** *__这个也是!__* +``` -<!-- 在 GitHub 采用的 Markdown 中 --> +GitHub 也支持 Markdown,在 GitHub 的 Markdown 解析器中,我们可以使用~~删除线~~: +```md ~~此文本为删除线效果。~~ +``` +## 段落 -<!-- 单个段落由一句或多句邻近的句子组成,这些句子由一个或多个空格分隔。--> +段落由一个句子或是多个中间没有空行的句子组成,每个段落由一个或是多个空行分隔开来。 +(注:部分解析器有无需空行就能换行的设置,这个主要看个人喜好) +```md 这是第一段落. 这句话在同一个段落里,好玩么? 现在我是第二段落。 这句话也在第二段落! + 这句话在第三段落! +``` + +如果你想插入一个 `<br />` 标签,你可以在段末加入两个以上的空格,然后另起一 +段。 -<!-- 如果你插入一个 HTML中的<br />标签,你可以在段末加入两个以上的空格, -然后另起一段。--> +(译者注:试了一下,很多解析器,并不需要空两个空格,直接换行就会添加一个`<br />`) +```md 此段落结尾有两个空格(选中以显示)。 上文有一个 <br /> ! +``` + +段落引用可由 `>` 字符轻松实现。 -<!-- 段落引用可由 > 字符轻松实现。--> +> 对的很轻松 -> 这是一个段落引用. 你可以 -> 手动断开你的句子,然后在每句句子前面添加 “>” 字符。或者让你的句子变得很长,以至于他们自动得断开。 -> 只要你的文字以“>” 字符开头,两种方式无异。 +```md +> 这是一个段落引用。 你可以 +> 手动断开你的句子,然后在每句句子前面添加 `>` 字符。或者让你的句子变得很长,以至于他们自动得换行。 +> 只要你的文字以 `>` 字符开头,两种方式无异。 -> 你也对文本进行 +> 你也可以对文本进行 >> 多层引用 > 这多机智啊! +``` -<!-- 序列 --> -<!-- 无序序列可由星号,加号或者减号来建立 --> +## 列表 +- 无序列表可由星号,加号或者减号来创建 +```md * 项目 * 项目 * 另一个项目 @@ -102,139 +148,211 @@ __此文本也是__ - 项目 - 最后一个项目 -<!-- 有序序列可由数字加点来实现 --> +``` + +有序序列可由数字加上点 `.` 来实现 +```md 1. 项目一 2. 项目二 3. 项目三 +``` -<!-- 即使你的标签数字有误,Markdown 依旧会呈现出正确的序号, -不过这并不是一个好主意--> +即使你的数字标签有误,Markdown 依旧会呈现出正确的序号, +不过这并不是一个好主意 +```md 1. 项目一 1. 项目二 1. 项目三 -<!-- (此段与前例一模一样) --> +``` +(此段与上面效果一模一样) -<!-- 你也可以使用子序列 --> +你也可以使用子列表 +```md 1. 项目一 2. 项目二 3. 项目三 * 子项目 * 子项目 4. 项目四 +``` -<!-- 代码段落 --> -<!-- 代码段落(HTML中 <code>标签)可以由缩进四格(spaces) -或者一个制表符(tab)实现--> +你甚至可以使用任务列表,它将会生成 HTML 的选择框(checkboxes)标签。 + +```md +下面方框里包含 'x' 的列表,将会生成选中效果的选择框。 +- [ ] 任务一需要完成 +- [ ] 任务二需要完成 +下面这个选择框将会是选中状态 +- [x] 这个任务已经完成 +``` +- [ ] 你看完了这个任务(注:此选择框是无法直接更改的,即禁用状态。) +## 代码块 + +代码块(HTML中 `<code>` 标签)可以由缩进四格(spaces) +或者一个制表符(tab)实现 + +```md This is code - So is this + So is this +``` -<!-- 在你的代码中,你仍然使用tab可以进行缩进操作 --> +在你的代码中,你仍然使用tab(或者四个空格)可以进行缩进操作 +```md my_array.each do |item| puts item end +``` -<!-- 内联代码可由反引号 ` 实现 --> +内联代码可由反引号 ` 实现 +```md John 甚至不知道 `go_to()` 方程是干嘛的! +``` -<!-- 在GitHub的 Markdown中,对于代码你可以使用特殊的语法 --> +在GitHub的 Markdown(GitHub Flavored Markdown)解析器中,你可以使用特殊的语法表示代码块 -\`\`\`ruby <!-- 插入时记得移除反斜线, 仅留```ruby ! --> +<pre> +<code class="highlight">```ruby def foobar puts "Hello world!" end -\`\`\` <!-- 这里也是,移除反斜线,仅留 ``` --> +```</code></pre> + +以上代码不需要缩进,而且 GitHub 会根据\`\`\`后指定的语言来进行语法高亮显示 -<!-- 以上代码不需要缩进,而且 GitHub 会根据```后表明的语言来进行语法高亮 --> +## 水平线分隔 -<!-- 水平线 (<hr />) --> -<!-- 水平线可由三个或以上的星号或者减号创建,可带可不带空格。 --> +水平线(`<hr/>`)可由三个或以上的星号或是减号创建,它们之间可以带或不带空格 +```md *** --- - - - **************** -<!-- 链接 --> -<!-- Markdown 最棒的地方就是简易的链接制作。链接文字放在中括号[]内, -在随后的括弧()内加入url。--> +下面这个就是示例 +``` +--- + +## 链接 +Markdown 最棒的地方就是便捷的书写链接。把链接文字放在中括号[]内, +在随后的括弧()内加入url就可以了。 + +```md [点我点我!](http://test.com/) -<!-- 你也可以为链接加入一个标题:在括弧内使用引号 --> +``` + +你也可以在小括号内使用引号,为链接加上一个标题(title) +```md [点我点我!](http://test.com/ "连接到Test.com") +``` +相对路径也可以有 -<!-- 相对路径也可以有 --> - +```md [去 music](/music/). +``` -<!-- Markdown同样支持引用样式的链接 --> - -[点此链接][link1]以获取更多信息! -[看一看这个链接][foobar] 如果你愿意的话. +Markdown同样支持引用形式的链接 -[link1]: http://test.com/ "Cool!" -[foobar]: http://foobar.biz/ "Alright!" +```md +[点此链接][link1] 以获取更多信息! +[看一看这个链接][foobar] 如果你愿意的话。 +[link1]: http://test.com/ +[foobar]: http://foobar.biz/ +``` -<!-- 链接的标题可以处于单引号中,括弧中或是被忽略。引用名可以在文档的任意何处, -并且可以随意命名,只要名称不重复。--> +对于引用形式,链接的标题可以处于单引号中,括弧中或是忽略。引用名可以在文档的任何地方,并且可以随意命名,只要名称不重复。 -<!-- “隐含式命名” 的功能可以让链接文字作为引用名 --> +“隐含式命名” 的功能可以让链接文字作为引用名 +```md [This][] is a link. +[This]: http://thisisalink.com/ +``` -[this]: http://thisisalink.com/ - -<!-- 但这并不常用 --> - -<!-- 图像 --> -<!-- 图像与链接相似,只需在前添加一个感叹号 --> +但这并不常用 -![这是我图像的悬停文本(alt text)](http://imgur.com/myimage.jpg "可选命名") +## 图片 +图片与链接相似,只需在前添加一个感叹号 -<!-- 引用样式也同样起作用 --> +```md +![这是alt,请把鼠标放在图片上](http://imgur.com/myimage.jpg "这是title") +``` -![这是我的悬停文本.][myimage] +引用形式也同样起作用 -[myimage]: relative/urls/cool/image.jpg "在此输入标题" +```md +![这是alt][myimage] +[myimage]: relative/urls/cool/image.jpg +``` -<!-- 杂项 --> -<!-- 自动链接 --> +## 杂项 +### 自动链接 +```md <http://testwebsite.com/> 与 [http://testwebsite.com/](http://testwebsite.com/) 等同 +``` -<!-- 电子邮件的自动链接 --> +### 电子邮件的自动链接 +```md <foo@bar.com> +``` -<!-- 转义字符 --> +### 转义字符 +```md 我希望 *将这段文字置于星号之间* 但是我不希望它被 -斜体化, 所以我就: \*这段置文字于星号之间\*。 +斜体化, 这么做: \*这段置文字于星号之间\*。 +``` +对比一下:*将这段文字置于星号之间* 和 \*将这段文字置于星号之间\* -<!-- 表格 --> -<!-- 表格只被 GitHub 的 Markdown 支持,并且有一点笨重,但如果你真的要用的话: --> +### 键盘上的功能键 -| 第一列 | 第二列 | 第三列 | -| :---------- | :------: | ----------: | -| 左对齐 | 居个中 | 右对齐 | -| 某某某 | 某某某 | 某某某 | +在 GitHub 的 Markdown 中,你可以使用 `<kbd>` 标签来表示功能键。 -<!-- 或者, 同样的 --> +```md +你的电脑死机了?试试 +<kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>Del</kbd> +``` +<kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>Del</kbd> -第一列 | 第二列 | 第三列 -:-- | :-: | --: -这太丑了 | 药不能 | 停 +(译注:可能由于网站本身样式问题,效果不明显) -<!-- 结束! --> +### 表格 +下面示例的表格长这样: + +| 第一列 | 第二列 | 第三列 | +| :----------- | :-------: | ----------: | +| 我是左对齐 | 居个中 | 右对齐 | +| 注意 | 冒 | 号 | + +工整一点的写法是这样的: + +```md +| 第一列 | 第二列 | 第三列 | +| :----------- | :-------: | ----------: | +| 我是左对齐 | 居个中 | 右对齐 | +| 注意 | 冒 | 号 | ``` +好吧,强行对齐字符是很难的。但是,至少比下面这种写法好一点—— + +```md +我是超级超级长的第一列 | 第二列 | 第三列 +:-- | :-: | --: +这真的太丑了 | 药不能 | 停!!!! +``` +真的是*看着令人头晕* + 更多信息, 请于[此处](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 index 2fbccfc4..d215755c 100644 --- a/zh-cn/matlab-cn.html.markdown +++ b/zh-cn/matlab-cn.html.markdown @@ -10,9 +10,12 @@ lang: zh-cn ---
-MATLAB 是 MATrix LABoratory (矩阵实验室)的缩写,它是一种功能强大的数值计算语言,在工程和数学领域中应用广泛。
+MATLAB 是 MATrix LABoratory(矩阵实验室)的缩写。
+它是一种功能强大的数值计算语言,在工程和数学领域中应用广泛。
-如果您有任何需要反馈或交流的内容,请联系本教程作者[@the_ozzinator](https://twitter.com/the_ozzinator)、[osvaldo.t.mendoza@gmail.com](mailto:osvaldo.t.mendoza@gmail.com)。
+如果您有任何需要反馈或交流的内容,请联系本教程作者:
+[@the_ozzinator](https://twitter.com/the_ozzinator)
+或 [osvaldo.t.mendoza@gmail.com](mailto:osvaldo.t.mendoza@gmail.com)。
```matlab
% 以百分号作为注释符
@@ -45,7 +48,7 @@ edit('myfunction.m') % 在编辑器中打开指定函数或脚本 type('myfunction.m') % 在命令窗口中打印指定函数或脚本的源码
profile on % 打开 profile 代码分析工具
-profile of % 关闭 profile 代码分析工具
+profile off % 关闭 profile 代码分析工具
profile viewer % 查看 profile 代码分析工具的分析结果
help command % 在命令窗口中显示指定命令的帮助文档
@@ -113,7 +116,7 @@ b(2) % ans = 符 % 元组(cell 数组)
a = {'one', 'two', 'three'}
a(1) % ans = 'one' - 返回一个元组
-char(a(1)) % ans = one - 返回一个字符串
+a{1} % ans = one - 返回一个字符串
% 结构体
@@ -210,8 +213,8 @@ size(A) % 返回矩阵的行数和列数,ans = 3 3 A(1, :) =[] % 删除矩阵的第 1 行
A(:, 1) =[] % 删除矩阵的第 1 列
-transpose(A) % 矩阵转置,等价于 A'
-ctranspose(A) % 矩阵的共轭转置(对矩阵中的每个元素取共轭复数)
+transpose(A) % 矩阵(非共轭)转置,等价于 A.' (注意!有个点)
+ctranspose(A) % 矩阵的共轭转置(对矩阵中的每个元素取共轭复数),等价于 A'
% 元素运算 vs. 矩阵运算
@@ -219,18 +222,20 @@ ctranspose(A) % 矩阵的共轭转置(对矩阵中的每个元素取共轭复 % 在运算符加上英文句点就是对矩阵中的元素进行元素计算
% 示例如下:
A * B % 矩阵乘法,要求 A 的列数等于 B 的行数
-A .* B % 元素乘法,要求 A 和 B 形状一致(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 矩阵)
+sqrtm(A) % 对矩阵整体做开方运算(即试图求出一个矩阵,该矩阵与自身的乘积等于 A 矩阵)
% 绘图
-x = 0:.10:2*pi; % 生成一向量,其元素从 0 开始,以 0.1 的间隔一直递增到 2*pi(pi 就是圆周率)
+x = 0:0.1:2*pi; % 生成一向量,其元素从 0 开始,以 0.1 的间隔一直递增到 2*pi
+ % 其中 pi 为圆周率
y = sin(x);
plot(x,y)
xlabel('x axis')
@@ -288,7 +293,10 @@ clf clear % 清除图形窗口中的图像,并重置图像属性 % 也可以用 gcf 函数返回当前图像的句柄
h = plot(x, y); % 在创建图像时显式地保存图像句柄
set(h, 'Color', 'r')
-% 颜色代码:'y' 黄色,'m' 洋红色,'c' 青色,'r' 红色,'g' 绿色,'b' 蓝色,'w' 白色,'k' 黑色
+% 颜色代码:
+% 'y' 黄色,'m' 洋红,'c' 青色
+% 'r' 红色,'g' 绿色,'b' 蓝色
+% 'w' 白色,'k' 黑色
set(h, 'Color', [0.5, 0.5, 0.4])
% 也可以使用 RGB 值指定颜色
set(h, 'LineStyle', '--')
@@ -328,7 +336,8 @@ load('myFileName.mat') % 将指定文件中的变量载入到当前工作空间 % 与脚本文件类似,同样以 .m 作为后缀名
% 但函数文件可以接受用户输入的参数并返回运算结果
% 并且函数拥有自己的工作空间(变量域),不必担心变量名称冲突
-% 函数文件的名称应当与其所定义的函数的名称一致(比如下面例子中函数文件就应命名为 double_input.m)
+% 函数文件的名称应当与其所定义的函数的名称一致
+% 比如下面例子中函数文件就应命名为 double_input.m
% 使用 'help double_input.m' 可返回函数定义中第一行注释信息
function output = double_input(x)
% double_input(x) 返回 x 的 2 倍
@@ -463,14 +472,16 @@ triu(x) % 返回 x 的上三角这部分 tril(x) % 返回 x 的下三角这部分
cross(A, B) % 返回 A 和 B 的叉积(矢量积、外积)
dot(A, B) % 返回 A 和 B 的点积(数量积、内积),要求 A 和 B 必须等长
-transpose(A) % A 的转置,等价于 A'
+transpose(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 是由奇异值构成的半正定实数对角阵
+[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 % 最大值
@@ -489,5 +500,5 @@ 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/)
+* 官方网页:[MATLAB - 技术计算语言 - MATLAB & Simulink](https://ww2.mathworks.cn/products/matlab.html)
+* 官方论坛:[MATLAB Answers - MATLAB Central](https://ww2.mathworks.cn/matlabcentral/answers/)
diff --git a/zh-cn/python-cn.html.markdown b/zh-cn/python-cn.html.markdown index deb94cdc..65f125d1 100644 --- a/zh-cn/python-cn.html.markdown +++ b/zh-cn/python-cn.html.markdown @@ -439,7 +439,7 @@ Human.grunt() # => "*grunt*" # 我们可以导入其他模块 import math -print math.sqrt(16) # => 4 +print math.sqrt(16) # => 4.0 # 我们也可以从一个模块中导入特定的函数 from math import ceil, floor diff --git a/zh-cn/python3-cn.html.markdown b/zh-cn/python3-cn.html.markdown index 76455a46..fd962305 100644 --- a/zh-cn/python3-cn.html.markdown +++ b/zh-cn/python3-cn.html.markdown @@ -10,13 +10,13 @@ filename: learnpython3-cn.py lang: zh-cn --- -Python是由吉多·范罗苏姆(Guido Van Rossum)在90年代早期设计。它是如今最常用的编程 -语言之一。它的语法简洁且优美,几乎就是可执行的伪代码。 +Python 是由吉多·范罗苏姆(Guido Van Rossum)在 90 年代早期设计。 +它是如今最常用的编程语言之一。它的语法简洁且优美,几乎就是可执行的伪代码。 -欢迎大家斧正。英文版原作Louie Dinh [@louiedinh](http://twitter.com/louiedinh) -或着Email louiedinh [at] [谷歌的信箱服务]。中文翻译Geoff Liu。 +欢迎大家斧正。英文版原作 Louie Dinh [@louiedinh](http://twitter.com/louiedinh) +邮箱 louiedinh [at] [谷歌的信箱服务]。中文翻译 Geoff Liu。 -注意:这篇教程是特别为Python3写的。如果你想学旧版Python2,我们特别有另一篇教程。 +注意:这篇教程是基于 Python 3 写的。如果你想学旧版 Python 2,我们特别有[另一篇教程](http://learnxinyminutes.com/docs/python/)。 ```python @@ -70,15 +70,15 @@ not True # => False not False # => True # 逻辑运算符,注意and和or都是小写 -True and False #=> False -False or True #=> True +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 +0 and 2 # => 0 +-5 or 0 # => -5 +0 == False # => True +2 == True # => False +1 == True # => True # 用==判断相等 1 == 1 # => True @@ -113,10 +113,11 @@ False or True #=> True # 可以重复参数以节省时间 "{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" +# => "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" +"{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") @@ -132,8 +133,8 @@ None is None # => True # 所有其他值都是True bool(0) # => False bool("") # => False -bool([]) #=> False -bool({}) #=> False +bool([]) # => False +bool({}) # => False #################################################### @@ -233,7 +234,8 @@ filled_dict = {"one": 1, "two": 2, "three": 3} filled_dict["one"] # => 1 -# 用keys获得所有的键。因为keys返回一个可迭代对象,所以在这里把结果包在list里。我们下面会详细介绍可迭代。 +# 用 keys 获得所有的键。 +# 因为 keys 返回一个可迭代对象,所以在这里把结果包在 list 里。我们下面会详细介绍可迭代。 # 注意:字典键的顺序是不定的,你得到的结果可能和以下不同。 list(filled_dict.keys()) # => ["three", "two", "one"] @@ -261,7 +263,7 @@ 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.update({"four":4}) # => {"one": 1, "two": 2, "three": 3, "four": 4} filled_dict["four"] = 4 # 另一种赋值方法 # 用del删除 @@ -362,7 +364,7 @@ else: # else语句是可选的,必须在所有的except之后 filled_dict = {"one": 1, "two": 2, "three": 3} our_iterable = filled_dict.keys() -print(our_iterable) # => range(1,10) 是一个实现可迭代接口的对象 +print(our_iterable) # => dict_keys(['one', 'two', 'three']),是一个实现可迭代接口的对象 # 可迭代对象可以遍历 for i in our_iterable: @@ -376,17 +378,17 @@ our_iterator = iter(our_iterable) # 迭代器是一个可以记住遍历的位置的对象 # 用__next__可以取得下一个元素 -our_iterator.__next__() #=> "one" +our_iterator.__next__() # => "one" # 再一次调取__next__时会记得位置 -our_iterator.__next__() #=> "two" -our_iterator.__next__() #=> "three" +our_iterator.__next__() # => "two" +our_iterator.__next__() # => "three" # 当迭代器所有元素都取出后,会抛出StopIteration our_iterator.__next__() # 抛出StopIteration # 可以用list一次取出迭代器所有的元素 -list(filled_dict.keys()) #=> Returns ["one", "two", "three"] +list(filled_dict.keys()) # => Returns ["one", "two", "three"] @@ -568,13 +570,14 @@ def double_numbers(iterable): yield i + i # 生成器只有在需要时才计算下一个值。它们每一次循环只生成一个值,而不是把所有的 -# 值全部算好。这意味着double_numbers不会生成大于15的数字。 +# 值全部算好。 # # range的返回值也是一个生成器,不然一个1到900000000的列表会花很多时间和内存。 # # 如果你想用一个Python的关键字当作变量名,可以加一个下划线来区分。 range_ = range(1, 900000000) # 当找到一个 >=30 的结果就会停 +# 这意味着 `double_numbers` 不会生成大于30的数。 for i in double_numbers(range_): print(i) if i >= 30: diff --git a/zh-cn/ruby-cn.html.markdown b/zh-cn/ruby-cn.html.markdown index 657a913d..9918c022 100644 --- a/zh-cn/ruby-cn.html.markdown +++ b/zh-cn/ruby-cn.html.markdown @@ -6,11 +6,25 @@ contributors: - ["David Underwood", "http://theflyingdeveloper.com"] - ["Joel Walden", "http://joelwalden.net"] - ["Luke Holder", "http://twitter.com/lukeholder"] + - ["Tristan Hume", "http://thume.ca/"] + - ["Nick LaMuro", "https://github.com/NickLaMuro"] + - ["Marcos Brizeno", "http://www.about.me/marcosbrizeno"] + - ["Ariel Krakowski", "http://www.learneroo.com"] + - ["Dzianis Dashkevich", "https://github.com/dskecse"] + - ["Levi Bostian", "https://github.com/levibostian"] + - ["Rahil Momin", "https://github.com/iamrahil"] + - ["Gabriel Halley", "https://github.com/ghalley"] + - ["Persa Zula", "http://persazula.com"] + - ["Jake Faris", "https://github.com/farisj"] + - ["Corey Ward", "https://github.com/coreyward"] + - ["Jannik Siebert", "https://github.com/janniks"] + - ["Keith Miyake", "https://github.com/kaymmm"] - ["lidashuang", "https://github.com/lidashuang"] - ["ftwbzhao", "https://github.com/ftwbzhao"] translators: - ["Lin Xiangyu", "https://github.com/oa414"] - ["Jiang Haiyun", "https://github.com/haiiiiiyun"] + - ["woclass", "https://github.com/inkydragon"] --- ```ruby @@ -18,26 +32,27 @@ translators: =begin 这是多行注释 -没人用这个 -你也不该用 =end -# 首先,也是最重要的,所有东西都是对象 +# 在 Ruby 中,(几乎)所有东西都是对象 # 数字是对象 - -3.class #=> Fixnum - +3.class #=> Integer 3.to_s #=> "3" +# 字符串是对象 +"Hello".class #=> String + +# 甚至方法也是对象 +"Hello".method(:class).class #=> Method -# 一些基本的算术符号 -1 + 1 #=> 2 -8 - 1 #=> 7 -10 * 2 #=> 20 -35 / 5 #=> 7 -2**5 #=> 32 -5 % 3 #=> 2 +# 一些基本的算术操作 +1 + 1 #=> 2 +8 - 1 #=> 7 +10 * 2 #=> 20 +35 / 5 #=> 7 +2 ** 5 #=> 32 +5 % 3 #=> 2 # 位运算符 3 & 5 #=> 1 @@ -48,6 +63,7 @@ translators: # 实际上是调用对象的方法 1.+(3) #=> 4 10.* 5 #=> 50 +100.methods.include?(:/) #=> true # 特殊的值也是对象 nil # 相当于其它语言中的 null @@ -66,11 +82,11 @@ false.class #=> FalseClass 1 != 1 #=> false 2 != 1 #=> true -# 除了false自己,nil是唯一的另一个值为false的对象 - -!nil #=> true -!false #=> true -!0 #=> false +# 除了 false 自己,nil 是唯一的另一个值为 false 的对象 +!!nil #=> false +!!false #=> false +!!0 #=> true +!!"" #=> true # 更多比较 1 < 10 #=> true @@ -90,11 +106,11 @@ true || false #=> true !true #=> false # 也有优先级更低的逻辑运算符 -# 它们用于控制流结构中,用来串接语句,直到返回true或false。 +# 它们用于控制流结构中,用来串接语句,直到返回 true 或 false。 -# `do_something_else` 只当 `do_something` 返回true时才会被调用 +# `do_something_else` 只当 `do_something` 返回 true 时才会被调用 do_something() and do_something_else() -# `log_error` 只当 `do_something` 返回false时才会被调用 +# `log_error` 只当 `do_something` 返回 false 时才会被调用 do_something() or log_error() @@ -114,6 +130,7 @@ placeholder = "use string interpolation" 'hello ' + 'world' #=> "hello world" 'hello ' + 3 #=> TypeError: can't convert Fixnum into String 'hello ' + 3.to_s #=> "hello 3" +"hello #{3}" #=> "hello 3" # 合并字符串及其运算符 'hello ' * 3 #=> "hello hello hello " @@ -141,7 +158,7 @@ x = y = 10 #=> 10 x #=> 10 y #=> 10 -# 按照惯例,使用类似snake_case风格的变量名 +# 按照惯例,使用类似 snake_case 风格的变量名 snake_case = true # 使用有意义的变量名 @@ -174,6 +191,7 @@ array = [1, 2, 3, 4, 5] #=> [1, 2, 3, 4, 5] # 数组可以被索引 # 从前面开始 array[0] #=> 1 +array.first #=> 1 array[12] #=> nil # 像运算符一样,[var] 形式的访问 @@ -189,13 +207,13 @@ array.last #=> 5 # 同时指定开始的位置和长度 array[2, 3] #=> [3, 4, 5] +# 或者指定一个区间 +array[1..3] #=> [2, 3, 4] + # 将数组逆序 a=[1,2,3] a.reverse! #=> [3,2,1] -# 或者指定一个区间 -array[1..3] #=> [2, 3, 4] - # 像这样往数组增加一个元素 array << 6 #=> [1, 2, 3, 4, 5, 6] # 或者像这样 @@ -217,14 +235,18 @@ hash['number'] #=> 5 # 查询一个不存在的键将会返回nil hash['nothing here'] #=> nil -# 从Ruby 1.9开始,用符号作为键的时候有特别的记号表示: +# 从 Ruby 1.9 开始,用符号作为键的时候有特别的记号表示: new_hash = { defcon: 3, action: true } new_hash.keys #=> [:defcon, :action] +# 检查键值是否存在 +hash.key?(:defcon) #=> true +hash.value?(3) #=> true + # 小贴士:数组和哈希表都是可枚举的 -# 它们共享一些有用的方法,比如each,map,count等等 +# 它们共享一些有用的方法,比如 each, map, count 等等 # 控制流 @@ -236,6 +258,8 @@ else "else, also optional" end +# 循环 + for counter in 1..5 puts "iteration #{counter}" end @@ -246,14 +270,14 @@ end #=> iteration 5 -# 但是,没有人用for循环。 -# 你应该使用"each"方法,然后再传给它一个块。 -# 所谓块就是可以传给像"each"这样的方法的代码段。 -# 它类似于其它语言中的lambdas, 匿名函数或闭包。 +# 但是,没有人用 for 循环。 +# 你应该使用 "each" 方法,然后再传给它一个块。 +# 所谓块就是可以传给像 "each" 这样的方法的代码段。 +# 它类似于其它语言中的 lambdas, 匿名函数或闭包。 # -# 区间上的"each"方法会对区间中的每个元素运行一次块代码。 -# 我们将counter作为一个参数传给了块。 -# 调用带有块的"each"方法看起来如下: +# 区间上的 "each" 方法会对区间中的每个元素运行一次块代码。 +# 我们将 counter 作为一个参数传给了块。 +# 调用带有块的 "each" 方法看起来如下: (1..5).each do |counter| puts "iteration #{counter}" @@ -275,7 +299,7 @@ hash.each do |key, value| puts "#{key} is #{value}" end -# 如果你还需要索引值,可以使用"each_with_index",并且定义 +# 如果你还需要索引值,可以使用 "each_with_index",并且定义 # 一个索引变量 array.each_with_index do |element, index| puts "#{element} is number #{index} in the array" @@ -293,7 +317,7 @@ end #=> iteration 5 # Ruby 中还有很多有用的循环遍历函数, -# 如"map","reduce","inject"等等。 +# 如 "map", "reduce", "inject" 等等。 # 以map为例,它会遍历数组,并根据你在 # 块中定义的逻辑对它进行处理,然后返回 # 一个全新的数组。 @@ -388,19 +412,26 @@ surround { puts 'hello world' } # { # hello world # } +# => nil # 可以向函数传递一个块 # "&"标记传递的块是一个引用 def guests(&block) - block.call 'some_argument' + block.class #=> Proc + block.call(4) end +guests { |n| "You have #{n} guests." } +# => "You have 4 guests." + # 可以传递多个参数,这些参数会转成一个数组, # 这也是使用星号符 ("*") 的原因: def guests(*array) array.each { |guest| puts guest } end +# 结构 + # 如果函数返回一个数组,在赋值时可以进行拆分: def foods ['pancake', 'sandwich', 'quesadilla'] @@ -409,21 +440,42 @@ breakfast, lunch, dinner = foods breakfast #=> 'pancake' dinner #=> 'quesadilla' -# 按照惯例,所有返回布尔值的方法都以?结尾 +# 有些情况下,你会想使用解构操作符 `*` 来解构数组 +ranked_competitors = ["John", "Sally", "Dingus", "Moe", "Marcy"] + +def best(first, second, third) + puts "Winners are #{first}, #{second}, and #{third}." +end + +best *ranked_competitors.first(3) #=> Winners are John, Sally, and Dingus. + +# 结构操作符也可放在参数里面 +def best(first, second, third, *others) + puts "Winners are #{first}, #{second}, and #{third}." + puts "There were #{others.count} other participants." +end + +best *ranked_competitors +#=> Winners are John, Sally, and Dingus. +#=> There were 2 other participants. + +# 按照惯例,所有返回布尔值的方法都以 ? 结尾 5.even? # false 5.odd? # true -# 如果方法名末尾有!,表示会做一些破坏性的操作,比如修改调用者自身。 -# 很多方法都会有一个!的版本来进行修改,和一个非!的版本 -# 只用来返回更新了的结果 +# 如果方法名末尾有感叹号 !,表示会做一些破坏性的操作,比如修改调用者自身。 +# 很多方法都会有一个 ! 的版本来进行修改, +# 和一个只返回更新结果的非 ! 版本 company_name = "Dunder Mifflin" company_name.upcase #=> "DUNDER MIFFLIN" company_name #=> "Dunder Mifflin" -company_name.upcase! # we're mutating company_name this time! +# 这次我们修改了 company_name +company_name.upcase! #=> "DUNDER MIFFLIN" company_name #=> "DUNDER MIFFLIN" +# 类 -# 用class关键字定义一个类 +# 用 class 关键字定义一个类 class Human # 一个类变量,它被这个类的所有实例变量共享 @@ -431,30 +483,30 @@ class Human # 基本构造函数 def initialize(name, age = 0) - # 将参数值赋给实例变量"name" + # 将参数值赋给实例变量 "name" @name = name - # 如果没有给出age,那么会采用参数列表中的默认值 + # 如果没有给出 age,那么会采用参数列表中的默认值 @age = age end - # 基本的setter方法 + # 基本的 setter 方法 def name=(name) @name = name end - # 基本地getter方法 + # 基本地 getter 方法 def name @name end - # 以上的功能也可以用下面的attr_accessor来封装 + # 以上的功能也可以用下面的 attr_accessor 来封装 attr_accessor :name - # Getter/setter方法也可以像这样单独创建 + # Getter/setter 方法也可以像这样单独创建 attr_reader :name attr_writer :name - # 类方法通过使用self与实例方法区别开来。 + # 类方法通过使用 self 与实例方法区别开来。 # 它只能通过类来调用,不能通过实例调用。 def self.say(msg) puts "#{msg}" @@ -468,7 +520,6 @@ end # 初始化一个类 jim = Human.new("Jim Halpert") - dwight = Human.new("Dwight K. Schrute") # 让我们来调用一些方法 @@ -483,15 +534,15 @@ dwight.name #=> "Dwight K. Schrute" Human.say('Hi') #=> "Hi" # 变量的作用域由它们的名字格式定义 -# 以$开头的变量具有全局域 +# 以 $ 开头的变量具有全局域 $var = "I'm a global var" defined? $var #=> "global-variable" -# 以@开头的变量具有实例作用域 +# 以 @ 开头的变量具有实例作用域 @var = "I'm an instance var" defined? @var #=> "instance-variable" -# 以@@开头的变量具有类作用域 +# 以 @@ 开头的变量具有类作用域 @@var = "I'm a class var" defined? @@var #=> "class variable" @@ -568,7 +619,6 @@ Book.foo # => 'foo' Book.new.foo # => NoMethodError: undefined method `foo' # 当包含或扩展一个模块时,相应的回调代码会被执行。 - module ConcernExample def self.included(base) base.extend(ClassMethods) diff --git a/zh-cn/solidity-cn.html.markdown b/zh-cn/solidity-cn.html.markdown new file mode 100644 index 00000000..ec684997 --- /dev/null +++ b/zh-cn/solidity-cn.html.markdown @@ -0,0 +1,825 @@ +--- +language: Solidity +filename: learnSolidity-cn.sol +lang: zh-cn +contributors: + - ["Nemil Dalal", "https://www.nemil.com"] + - ["Joseph Chow", ""] + - ["Bhoomtawath Plinsut", "https://github.com/varshard"] + - ["Shooter", "https://github.com/liushooter"] +translators: + - ["Bob Jiang", "https://github.com/bobjiang"] +--- + +Solidity 使你在[以太坊](https://www.ethereum.org/)上编程,一个基于区块链的虚拟机, +允许创建和执行智能合约,无需中心化的或可信的一方。 + +Solidity 是一种与 Javascript 和 C 的相似的、静态类型的合约编程语言。与OOP(面向对象)中 +的对象一样,每个合约都包含状态变量、函数和公共数据类型。合约特定功能包括修饰符(guard)子句, +事件通知的侦听器及自定义的全局变量。 + +以太坊合约的例子包括众筹、投票以及盲拍(私密拍卖)。 + +Solidity 代码中存在高风险和高成本的错误,因此你必须非常小心地进行测试并慢慢地发布。**随着 +以太坊的快速变化,本文档不可能是最新的,所以你应该关注最新的的 solidity 聊天室和以太网博客。 +照搬这里的代码,会存在重大错误或弃用代码模式的风险。(说人话--别照抄例子中的代码)** + +与其他代码不同,可能还需要添加如暂停、弃用和限制使用的设计模式,来降低风险。本文档主要讨论语法, +因此排除了许多流行的设计模式。 + +由于 Solidity 和以太坊正在积极开发,通常会标记为实验或 beta 特性,并很可能会更改。因此欢迎 +提交更改请求。 + +```javascript +// 首先,一个简单的银行合约 +// 允许存款、取款、以及检查余额 + +// simple_bank.sol (注意 .sol 后缀) +/* **** 例子开始 **** */ + +// 声明源文件的编译器版本 +pragma solidity ^0.4.19; + +// 开始 Natspec 注释(三个斜杠) +// 用作文档 - 及UI元素、动作的描述性数据 + +/// @title SimpleBank +/// @author nemild + +/* 'contract' 和其他语言的 'class' 类似 (类变量、继承等) */ +contract SimpleBank { // 单词首字母大写 + // 声明函数外的状态变量,合约生命周期内可用 + + // 地址映射到余额的字典,总是要小心数字的溢出攻击 + mapping (address => uint) private balances; + + // "private" 的意思是其他合约不能直接查询余额,但对于区块链上的其他方来说,数据仍然是可见的。 + + address public owner; + // 'public' 使用户或合约可以从外部读取(不可写) + + // Events(事件) - 向外部监听器发布动作 + event LogDepositMade(address accountAddress, uint amount); + + // Constructor(构造函数)(译者注:solidity 从0.4.22开始使用 constructor() 作为构造函数) + function SimpleBank() public { + // msg 提供了发送给合约的消息详情 + // msg.sender 是合约的调用者(这里是合约创建者的地址) + owner = msg.sender; + } + + /// @notice 存款 ether (以太币) + /// @return 存款后用户的余额 + function deposit() public payable returns (uint) { + // 使用 'require' 来检测用户的输入,'assert' 是内部常量 + // 我们要确保不会发生溢出问题(上溢) + require((balances[msg.sender] + msg.value) >= balances[msg.sender]); + + balances[msg.sender] += msg.value; + // 状态变量不需要 "this." 或 "self." + // 默认情况下,所有值都设置为数据类型的初始值 + + LogDepositMade(msg.sender, msg.value); // 触发事件 + + return balances[msg.sender]; + } + + /// @notice 从银行取款以太币 (ether) + /// @dev 不会返回任何多余的以太币(ether) + /// @param withdrawAmount 取款的数量 + /// @return 用户还剩下的余额 + function withdraw(uint withdrawAmount) public returns (uint remainingBal) { + require(withdrawAmount <= balances[msg.sender]); + + // 注意在发送任何交易,即通过 .transfer .send 调用外部函数之前,马上减掉取款数量 + // 这可以允许调用者使用递归请求大于其余额的金额。目标是在调用外部函数之前提交状态, + // 包括.transfer / .send + balances[msg.sender] -= withdrawAmount; + + // 这会自动引发失败,也就是说还原了更新的余额 + msg.sender.transfer(withdrawAmount); + + return balances[msg.sender]; + } + + /// @notice 获取余额 + /// @return 用户的余额 + // 'view' 防止函数编辑状态变量;允许函数本地运行或链下运行 + function balance() view public returns (uint) { + return balances[msg.sender]; + } +} +// ** 例子结束 ** + + +// 下面, solidity 基础 + +// 1. 数据类型与关联的方法 +// uint 类型用作现金数量(没有双浮点型或单浮点型)及日期(用 unix 时间) +uint x; + +// 256字节的 int, 实例化后不能改变 +int constant a = 8; +int256 constant a = 8; // 和上一行一样,这里256字节显性化了 +uint constant VERSION_ID = 0x123A1; // 16进制常量 +// 'constant' 关键字, 编译器在每个出现的地方替换为实际的值 + +// 所有的状态变量(函数之外的那些),默认是 'internal' 的,只能在合约及所有继承的合约内 +// 可以访问。需要显性的设置为 'public' 才能允许外部合约访问。 +int256 public a = 8; + +// 对于 int 和 uint,可以显性的设置位数(从8位到256位,8位跳跃),如int8, int16, int24 +uint8 b; +int64 c; +uint248 e; + +// 当心不要溢出以及免收此类攻击,例如,对于加法最好这么做: +uint256 c = a + b; +assert(c >= a); // assert 测试内部不变的值;require 用来测试用户输入 +// 更多通用算法问题的例子,参考 Zeppelin's SafeMath library +// https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/math/SafeMath.sol + + +// 没有内建的随机函数,使用其他合约获得随机数 + +// 类型转换 +int x = int(b); + +bool b = true; // 或 'var b = true;' 隐含的类型 + +// 地址 - 20个字节或160位以太坊地址(16进制数字),不允许进行运算 +address public owner; + +// 账户类型: +// 合约账户:在创建时设置地址(创建者地址函数,交易发送) +// 外部账户:(个人账户)从公钥创建的地址 + +// 'public' 的含义是自动创建的 getter 方法,而不是 setter 方法可以公开的、外部访问。 + +// 所有地址都可以进行转账 +owner.transfer(SOME_BALANCE); // 失败后还原 + +// 还可以调用较低级别的 .send , 转账失败会返回 false +if (owner.send) {} +// 记住:用 'if' 包着 send 函数,因为合约地址执行这些函数转账时,可能会失败 +// 另外,确保转账前先减掉余额,因为存在递归调用的风险。 + +// 检查余额 +owner.balance; // 所有者的余额(用户或合约) + + +// 字符类型,从1到32位可用 +byte a; // byte 等同于 byte1 +bytes2 b; +bytes32 c; + +// 动态大小的字符 +bytes m; // 特殊的数组,等同于 byte[],比 byte1 到 byte32 更贵 +// 尽可能不用 bytes + +// 等同于 bytes,但不允许长度或索引的访问 +string n = "hello"; // UTF8存储,注意双引号而不是单引号 +// 字符功能未来会增加,推荐使用 bytes32 或 bytes + +// 推断类型 +// var 会根据第一次赋值决定类型,不能用来作为函数的参数 +var a = true; +// 小心使用,推断可能带来错误的类型,例如,int8,而计数器需要的是 int16 + +// 函数可以用 var 类型赋值给变量 +function a(uint x) returns (uint) { + return x * 2; +} +var f = a; +f(22); // 调用 + +// 默认的,所有值实例化后都设为 0 + +// 大多数类型上可以调用删除(不会销毁值,而是设置为0,初始值) +uint x = 5; + + +// 集合 +(x, y) = (2, 7); // 多值的赋值 + + +// 2. 数据结构 +// 数组 +bytes32[5] nicknames; // 静态数组 +bytes32[] names; // 动态数组 +uint newLength = names.push("John"); // 添加返回数组的新长度 +// 长度 +names.length; // 获得数组长度 +names.length = 1; // 可以设定长度(仅针对 storage 中的动态数组) + +// 多维数组 +uint x[][5]; // 5个动态数组元素的数组(和多数语言的顺序相反) + +// 字典类型 (任一类型到其他类型的映射) +mapping (string => uint) public balances; +balances["charles"] = 1; +// balances["ada"]得到 0, 所有没有设定key值的,返回0 +// 'public' 允许跟着(调用)另一份合约 +contractName.balances("charles"); // returns 1 +// 'public' 创建 getter (而不是 setter )如下: +function balances(string _account) returns (uint balance) { + return balances[_account]; +} + +// 内嵌的 mapping +mapping (address => mapping (address => uint)) public custodians; + +// 删除 +delete balances["John"]; +delete balances; // 所有元素设为 0 + +// 不像其他语言,不知道 keys 的话不能列出 mapping 中的所有元素 - 可以在这之上构建数据结构 + +// 结构 +struct Bank { + address owner; + uint balance; +} +Bank b = Bank({ + owner: msg.sender, + balance: 5 +}); +// 或 +Bank c = Bank(msg.sender, 5); + +c.balance = 5; // 设为新值 +delete b; +// 设为初始值,结构内所有变量设为0,除了 mapping + +// 枚举 +enum State { Created, Locked, Inactive }; // 常常作为状态机 +State public state; // 声明枚举变量 +state = State.Created; +// 枚举类型可以显性化的转换为 ints +uint createdState = uint(State.Created); // 0 + +// 数据位置:内存(Memory) vs. 存储(storage) vs. 调用数据(calldata) +// 所有复杂类型(数据、结构)都有一个数据位置,内存数据不持久,而存储的数据是持久的。 +// 本地变量和状态变量默认是存储,函数参数默认是内存。堆栈存放较小的本地变量 + +// 多数类型,可以显性化的设定使用的数据位置 + + +// 3. 简单操作符 +// solidity 提供了比较、位运算及数学运算的功能 +// 指数运算: ** +// 异或运算: ^ +// 按位取反: ~ + + +// 4. 值得注意的全局变量 +// ** this ** +this; // 合约的地址 +// 常常用在合约生命周期结束前,转走剩下的余额 +this.balance; +this.someFunction(); // 通过 call 的方式而不是内部跳转的方式,从外部调用函数 + +// ** msg - 合约收到的当前消息 ** ** +msg.sender; // 发送者的地址 +msg.value; // 该合约内的以太币数量(单位 wei),该函数应该标记为 "payable" +msg.data; // 字符,完整的调用数据 +msg.gas; // 剩余 gas + +// ** tx - 交易信息 ** +tx.origin; // 本次交易的发送者地址 +tx.gasprice; // 本次交易的 gas price + +// ** block - 当前区块信息 ** +now; // 当前时间(大概)block.timestamp的别名 (采用的 Unix 时间) +// 注意这个可能被矿工操纵,因此请小心使用 + +block.number; // 当前区块号 +block.difficulty; // 当前区块难度 +block.blockhash(1); // 返回 bytes32,只对最近 256 个区块有效 +block.gasLimit(); + +// ** 存储 - 持久化存储哈希 ** +storage['abc'] = 'def'; // 256 位单词 到 256 位单词的映射 + + +// 4. 函数及更多 +// A. 函数 +// 简单函数 +function increment(uint x) returns (uint) { + x += 1; + return x; +} + +// 函数可以通过指定返回的参数名,来返回多个参数 +function increment(uint x, uint y) returns (uint x, uint y) { + x += 1; + y += 1; +} +// 调用前一个函数 +uint (a,b) = increment(1,1); + +// 'view' ('constant'的别名) +// 表明函数不会改变持久化的变量,View函数会本地执行,而不是链上运行。 +// 注意:constant 关键字很快会废弃。 +uint y = 1; + +function increment(uint x) view returns (uint x) { + x += 1; + y += 1; // 这一行会失败 + // y 是一个状态变量,不能在 view 的函数里改变 y +} + +// 'pure' 比 'view' 或 'constant' 更加严格,甚至不允许读取状态变量 +// 具体的规则很复杂,请参考 +// view/pure: +// http://solidity.readthedocs.io/en/develop/contracts.html#view-functions + +// '函数可见性指示器' +// 'view'可以有以下修饰符,包括: +// public - 内部及外部可见(函数的默认值) +// external - 仅外部可见(包括 this 发起的调用) +// private - 仅当前合约可见 +// internal - 仅当前合约及继承的合约可见 + +// 通常,显性的标记每个函数是个好主意 + +// 函数的挂起 - 可以将函数赋值给变量 +function a() { + var z = b; + b(); +} + +function b() { + +} + +// 所有接收 ether 的函数必须标记为 'payable' +function depositEther() public payable { + balances[msg.sender] += msg.value; +} + + +// 首选循环来递归(最大的调用堆栈深度是 1024),另外不要设置没有限制的循环, +// 因为这可能会达到 gas limit + +// B. 事件 +// 事件通知外部各方; 易于搜索和访问来自外部区块链(使用轻客户端)的事件 +// 通常在合约参数之后声明 + +// 通常,首字母大写并在前面加上 Log ,防止与函数混淆 + +// 声明 +event LogSent(address indexed from, address indexed to, uint amount); // 注意 capital first letter + +// 调用 +LogSent(from, to, amount); + +/* + // 对于外部方(合约或外部实体),使用 Web3 Javascript 库来监听 + // 以下是javascript代码,不是solidity代码 + Coin.LogSent().watch({}, '', function(error, result) { + if (!error) { + console.log("Coin transfer: " + result.args.amount + + " coins were sent from " + result.args.from + + " to " + result.args.to + "."); + console.log("Balances now:\n" + + "Sender: " + Coin.balances.call(result.args.from) + + "Receiver: " + Coin.balances.call(result.args.to)); + } + } + +*/ + +// 一个合约依赖另一个合约的共同范例(例如,合约取决于另一个合约提供的当前汇率) + +// C. 修饰器 +// 修饰器验证函数的输入,例如最小余额或用户身份验证; 类似于其他语言的保护子句 + +// '_' (下划线)经常用在代码的最后一行,表明被调用的函数放在那里 +modifier onlyAfter(uint _time) { require (now >= _time); _; } +modifier onlyOwner { require(msg.sender == owner) _; } +// 常用于状态机 +modifier onlyIfStateA (State currState) { require(currState == State.A) _; } + +// 修饰器紧跟在函数声明之后 +function changeOwner(newOwner) +onlyAfter(someTime) +onlyOwner() +onlyIfState(State.A) +{ + owner = newOwner; +} + +// 下划线可以包含在代码结束之前,但明显地返回将跳过后面的代码,因此谨慎使用 +modifier checkValue(uint amount) { + _; + if (msg.value > amount) { + uint amountToRefund = amount - msg.value; + msg.sender.transfer(amountToRefund); + } +} + + +// 6. 判断和循环 + +// 所有基本的逻辑判断都有效 - 包括 if else, for, while, break, continue +// return - 但不跳转 + +// 语法同 javascript, 但没有从非布尔值到布尔值的类型转换 +// (必须使用比较操作符获得布尔变量值) + +// 请注意由用户行为决定的循环 - 因为合约对于代码块具有最大量的 gas 限制 - +// 如果超过限制该代码则将失败 +// 例如: +for(uint x = 0; x < refundAddressList.length; x++) { + refundAddressList[x].transfer(SOME_AMOUNT); +} + +// 上述两个错误: +// 1. 转账失败会阻塞循环完成,钱被占用 +// 2. 该循环可能会很长(根据需要赔偿的用户数量而定),并且也可能由于超过一个区块最大 gas 限制 +// 而总是失败。你应该让人们自己从他们的子账户取款并标记取款完成 +// 例如,首选拉动式的付款,而不是推动式的付款 + + +// 7. 对象与合约 + +// A. 调用外部合约 +contract InfoFeed { + function info() returns (uint ret) { return 42; } +} + +contract Consumer { + InfoFeed feed; // 指向区块链上的一个合约 + + // 设置 feed 为已存在的合约实例 + function setFeed(address addr) { + // 当心类型自动转换;不会调用构造函数 + feed = InfoFeed(addr); + } + + // 设置 feed 为一个合约的新实例 + function createNewFeed() { + feed = new InfoFeed(); // 创建新实例,调用构造函数 + } + + function callFeed() { + // 最后的括号调用合约,可选择的增加自定义的 ether 或 gas 价格 + feed.info.value(10).gas(800)(); + } +} + +// B. 继承 + +// 和顺序有关,最后继承的合约(如 'def')可以覆盖之前已继承合约的部分 +contract MyContract is abc, def("a custom argument to def") { + +// 覆盖函数 + function z() { + if (msg.sender == owner) { + def.z(); // 调用覆盖的函数 + super.z(); // 调用继承的上层合约的函数 + } + } +} + +// 抽象函数 +function someAbstractFunction(uint x); +// 不可以编译,因此用在基础或抽象合约中,等待实现 + +// C. 导入 + +import "filename"; +import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol"; + + +// 8. 其他关键字 + +// A. 自毁 +// 自毁当前的合约,转账资金到一个地址(常常是创建者的地址) +selfdestruct(SOME_ADDRESS); + +// 从当前或以后的区块中移除存储或代码,会帮助客户端瘦身,但之前的数据会永久在区块链中 + +// 常见模式,让所有者结束合约并收回剩余的资金 +function remove() { + if(msg.sender == creator) { // 只有合约的创建者可以这么做 + selfdestruct(creator); // 自毁合约,返还资金 + } +} + +// 可能希望手动停用合约,而不是自毁 +// (发送到自毁合约的 ether 会丢失掉) + + +// 9. 注意合约的设计 + +// A. 困惑 +// 区块链上所有变量都是公开可见的,因此任何私有的需求变得很困惑。(好比哈希的秘密) + +// 步骤: 1. 承诺某事, 2. 揭示承诺 +keccak256("some_bid_amount", "some secret"); // commit + +// 以后调用合约的 reveal 函数,展示出用 SHA3 哈希的 bid 加 secret +reveal(100, "mySecret"); + +// B. 存储优化 +// 写入区块链可能很昂贵,因为数据是永久存储的;鼓励用巧妙的方法使用内存 +//(最终,编译会更好,但现在有利于规划数据结构 - 并将最小数量存储在区块链中) + +// 多维数组这样的变量可能会成本很高 +// (成本用于存储数据 - 而不是声明未填充的变量) + +// C. 区块链中的数据访问 +// 不能限制人或计算机读取交易或交易状态的内容 + +// 然而 'private' 可以防止其他*合约*直接读取数据 - 任意其他方仍然可以从区块链读取数据 + +// 从开始的所有数据都存在区块链上,因此任何人都可以查看之前的所有数据和变化 + +// D. 定时任务 +// 必须手动调用合约来处理时间相关的调度;也可以创建外部代码来定期的ping, +// 或为其他人提供激励(以太) + +// E. 观察者模式 +//观察者模式允许您注册为订阅者,然后注册一个由oracle调用的函数 +//(注意,oracle 需要付费来运行此操作)。与 Pub / sub 中的订阅有些相似之处 + +// 这是一个抽象合约,包括客户端和服务器端的类的导入,客户端应该要实现 +contract SomeOracleCallback { + function oracleCallback(int _value, uint _time, bytes32 info) external; +} + +contract SomeOracle { + SomeOracleCallback[] callbacks; // 所有订阅者的数组 + + // 注册订阅者 + function addSubscriber(SomeOracleCallback a) { + callbacks.push(a); + } + + function notify(value, time, info) private { + for(uint i = 0;i < callbacks.length; i++) { + // 所有调用的订阅者必须实现 oracleCallback + callbacks[i].oracleCallback(value, time, info); + } + } + + function doSomething() public { + // 实现的代码 + + // 通知所有的订阅者 + notify(_value, _time, _info); + } +} + +// 现在你的客户端合约可以通过 importing SomeOracleCallback 和注册某些 Oracle 来 +// addSubscriber 添加订阅者 + +// F. 状态机 +// 参见如下的例子,枚举类型的 State 和 修饰器 inState + + +// *** 例子: 众筹的例子(与 Kickstarter 大致相似)*** +// ** 开始例子 ** + +// CrowdFunder.sol +pragma solidity ^0.4.19; + +/// @title CrowdFunder +/// @author nemild +/// @translator bobjiang +contract CrowdFunder { + // 由创建者创建的变量 + address public creator; + address public fundRecipient; // 创建者可能和收件人不同 + uint public minimumToRaise; // 需要提示,否则每个人都会得到退款 + string campaignUrl; + byte constant version = 1; + + // 数据结构 + enum State { + Fundraising, + ExpiredRefund, + Successful + } + struct Contribution { + uint amount; + address contributor; + } + + // 状态变量State variables + State public state = State.Fundraising; // 创建时实例化 + uint public totalRaised; + uint public raiseBy; + uint public completeAt; + Contribution[] contributions; + + event LogFundingReceived(address addr, uint amount, uint currentTotal); + event LogWinnerPaid(address winnerAddress); + + modifier inState(State _state) { + require(state == _state); + _; + } + + modifier isCreator() { + require(msg.sender == creator); + _; + } + + // 允许合约销毁之前,最终合约状态后要等待24周 + modifier atEndOfLifecycle() { + require(((state == State.ExpiredRefund || state == State.Successful) && + completeAt + 24 weeks < now)); + _; + } + + function CrowdFunder( + uint timeInHoursForFundraising, + string _campaignUrl, + address _fundRecipient, + uint _minimumToRaise) + public + { + creator = msg.sender; + fundRecipient = _fundRecipient; + campaignUrl = _campaignUrl; + minimumToRaise = _minimumToRaise; + raiseBy = now + (timeInHoursForFundraising * 1 hours); + } + + function contribute() + public + payable + inState(State.Fundraising) + returns(uint256 id) + { + contributions.push( + Contribution({ + amount: msg.value, + contributor: msg.sender + }) // 采用数组,因此可以遍历 + ); + totalRaised += msg.value; + + LogFundingReceived(msg.sender, msg.value, totalRaised); + + checkIfFundingCompleteOrExpired(); + return contributions.length - 1; // 返回 id + } + + function checkIfFundingCompleteOrExpired() + public + { + if (totalRaised > minimumToRaise) { + state = State.Successful; + payOut(); + + // 可以激励在这里发起状态改变的人 + } else if ( now > raiseBy ) { + state = State.ExpiredRefund; // 支持者可以通过调用 getRefund(id) 收取退款 + } + completeAt = now; + } + + function payOut() + public + inState(State.Successful) + { + fundRecipient.transfer(this.balance); + LogWinnerPaid(fundRecipient); + } + + function getRefund(uint256 id) + inState(State.ExpiredRefund) + public + returns(bool) + { + require(contributions.length > id && id >= 0 && contributions[id].amount != 0 ); + + uint256 amountToRefund = contributions[id].amount; + contributions[id].amount = 0; + + contributions[id].contributor.transfer(amountToRefund); + + return true; + } + + function removeContract() + public + isCreator() + atEndOfLifecycle() + { + selfdestruct(msg.sender); + // 创建者获得所有未被声明的钱 + } +} +// ** 结束例子 ** + +// 10. 其他原生的函数 + +// 货币单位 +// 货币使用 wei 来定义,以太币的最小单位 = 1 wei; +uint minAmount = 1 wei; +uint a = 1 finney; // 1 ether == 1000 finney +// 其他单位,请参阅: http://ether.fund/tool/converter + +// 时间单位 +1 == 1 second +1 minutes == 60 seconds + +// 可以乘以带时间单位的变量,因为单位不会存储在变量中 +uint x = 5; +(x * 1 days); // 5 天 + +// 小心闰秒闰年与平等声明的时间 +// (相反,首选大于或小于) + +// 加密算法 +// 传递的所有字符串在哈希操作之前需要连接在一起 +sha3("ab", "cd"); +ripemd160("abc"); +sha256("def"); + +// 11. 安全 + +// 以太坊的合约中,错误可能是灾难性的 - 即使在 solidity 中是流行的模式,也可能发现是反模式的 + +// 参见文档底部的安全链接 + +// 12. 较低层次的函数 +// call - 较低层次,不会经常使用,不提供类型安全性 +successBoolean = someContractAddress.call('function_name', 'arg1', 'arg2'); + +// callcode - 在调用合约的*上下文*中执行的目标地址上的代码 +// 提供库功能 +someContractAddress.callcode('function_name'); + + +// 13. 注意风格 +// 基于 Python 的 PEP8 风格指南 +// 全部风格指南: http://solidity.readthedocs.io/en/develop/style-guide.html + +// 快速总结: +// 4个空格缩进 +// 两行隔开合约声明(和其他高级别的声明) +// 避免括号内留出多余的空格 +// 可以省略一行语句的花括号 (if, for, 等) +// else 应该单独一行 + + +// 14. NATSPEC 注释 +// 用于文档、注释和外部UI + +// 合约的 natspec - 总是在合约定义的上面 +/// @title 合约标题 +/// @author 作者名字 + +// 函数的 natspec +/// @notice 函数做什么的相关信息;展示什么时候执行该函数、 +/// @dev 开发者使用的函数文档 + +// 函数参数、返回值的 natspec +/// @param 有关参数用途的描述 +/// @return 返回值的描述 +``` + +## 更多资源 +- [Solidity Docs](https://solidity.readthedocs.org/en/latest/) +- [Smart Contract Best Practices](https://github.com/ConsenSys/smart-contract-best-practices) +- [EthFiddle - The JsFiddle for Solidity](https://ethfiddle.com/) +- [Browser-based Solidity Editor](https://remix.ethereum.org/) +- [Gitter Solidity Chat room](https://gitter.im/ethereum/solidity) +- [Modular design strategies for Ethereum Contracts](https://docs.erisindustries.com/tutorials/solidity/) + +## 重要的库文件 +- [Zeppelin](https://github.com/OpenZeppelin/zeppelin-solidity/): Libraries that provide common contract patterns (crowdfuding, safemath, etc) + +## 示例合约 +- [Dapp Bin](https://github.com/ethereum/dapp-bin) +- [Solidity Baby Step Contracts](https://github.com/fivedogit/solidity-baby-steps/tree/master/contracts) +- [ConsenSys Contracts](https://github.com/ConsenSys/dapp-store-contracts) +- [State of Dapps](http://dapps.ethercasts.com/) + +## 安全 +- [Thinking About Smart Contract Security](https://blog.ethereum.org/2016/06/19/thinking-smart-contract-security/) +- [Smart Contract Security](https://blog.ethereum.org/2016/06/10/smart-contract-security/) +- [Hacking Distributed Blog](http://hackingdistributed.com/) + +## 风格 +- [Solidity Style Guide](http://solidity.readthedocs.io/en/latest/style-guide.html): Ethereum's style guide is heavily derived from Python's [PEP 8](https://www.python.org/dev/peps/pep-0008/) style guide. + +## 编辑器 +- [Emacs Solidity Mode](https://github.com/ethereum/emacs-solidity) +- [Vim Solidity](https://github.com/tomlion/vim-solidity) +- Editor Snippets ([Ultisnips format](https://gist.github.com/nemild/98343ce6b16b747788bc)) + +## Future to Dos +- 新关键字: protected, inheritable +- 常见设计模式列表 (throttling, RNG, version upgrade) +- 常见的安全反模式 + +请随意发送 pull request 或者发邮件给作者 nemild -/at-/ gmail + +或者发邮件给译者 jiangxb -/at-/ gmail.com diff --git a/zh-cn/sql.html.markdown b/zh-cn/sql.html.markdown new file mode 100644 index 00000000..9d430bd1 --- /dev/null +++ b/zh-cn/sql.html.markdown @@ -0,0 +1,105 @@ +--- +language: SQL +filename: learnsql.sql +contributors: + - ["Bob DuCharme", "http://bobdc.com/"] +translators: + - ["Shuxin Shu", "https://github.com/NamelessAshone"] +lang: zh-cn +--- + +结构化查询语言(SQL)是一个ISO标准语言,用于创建和管理数据库, +这种数据库存储一系列表。不同的实现通常会添加特有的语言扩展; +[不同SQL实现的比较(Comparison of different SQL implementat- +ions)](http://troels.arvin.dk/db/rdbms/)是一份很好的产品差 +异参考文档。 + +不同的实现通常会提供一个命令行用于交互式键入命令和显示输出, +同时这些实现也会提供一种执行脚本文件的方法。(如何退出命令行 +就是就是SQL中尚未被标准化部分的一个典型例子,绝大多数SQL实 +现支持关键字QUIT、EXIT或者两者。) + +本文的实例命令假设你已经加载了[github](https://github.com/datacharmer/test_db)上的[MySQL示例员工数据库](https://dev.mysql.com/doc/employee/en/)。 +运行脚本的语法取决于你使用的SQL实现。通常是一个命令行工具。 + +```sql + +-- 注释以两个连字符开始。命令以分号结束。 + +-- SQL关键字大小写不敏感。在下文的示例命令中关键字大写, +-- 因为大写更容易区分数据库、表和列名。 + +-- 创建和删除一个数据库。数据库名和表名是大小写敏感的。 +CREATE DATABASE someDatabase; +DROP DATABASE someDatabase; + +-- 列出可用的数据库。 +SHOW DATABASES; + +-- 使用某个已经存在的数据库 +USE employees; + +-- 从当前的departments表,选择所有的行和列 +-- 解释器的默认行为是将结果打印在屏幕上。 +SELECT * FROM departments; + +-- 检索departments表中所有的行,但只取dept_no和dept_name列。 +-- 一条命令可以跨越多行 +SELECT dept_no, + dept_name FROM departments; + +-- 检索departments表中所有的行,但是只输出5行。 +SELECT * FROM departments LIMIT 5; + +-- 检索departments表中dept_name列包含子串'en'的行。 +SELECT dept_name FROM departments WHERE dept_name LIKE '%en%'; + +-- 检索departmnets表中所有dept_name列值为'S'开头并且'S'后接4个字符的行。 +SELECT * FROM departments WHERE dept_name LIKE 'S____'; + +-- 检索title表中所有行,不显示重复的行。 +SELECT DISTINCT title FROM titles; + +-- 和上面的查询相同,但是以title的值排序(大小写敏感)。 +SELECT DISTINCT title FROM titles ORDER BY title; + +-- 计算departments表的总行数。 +SELECT COUNT(*) FROM departments; + +-- 计算departments表中dept_name列以'en'字段开头的行的数量。 +SELECT COUNT(*) FROM departments WHERE dept_name LIKE '%en%'; + +-- 不同表中信息的JOIN: titles表显示谁有什么工作,员工编号, +-- 入职离职时间。检索这些信息,但是使用员工编号作为employees表 +-- 的交叉引用,而不是直接使用员工编号,来获得每个员工的名和姓。 +-- (同时只取10行) + +SELECT employees.first_name, employees.last_name, + titles.title, titles.from_date, titles.to_date +FROM titles INNER JOIN employees ON + employees.emp_no = titles.emp_no LIMIT 10; + +-- 列出所有数据库中所有的表。不同实现通常提供各自的快捷命令 +-- 来列出当前使用数据库的所有表。 +SELECT * FROM INFORMATION_SCHEMA.TABLES +WHERE TABLE_TYPE='BASE TABLE'; + +-- 在当前使用的数据库中,创建一个名为tablename1的表,包含下 +-- 述两列。许多其它选项可用于定制列,比如列的数据类型。 +CREATE TABLE tablename1 (fname VARCHAR(20), lname VARCHAR(20)); + +-- 向tablename1表插入一行数据。假设该表已经定义并且接受这些值。 +INSERT INTO tablename1 VALUES('Richard','Mutt'); + +-- 更新tablename1表中lname为'Mutt'的行fname的值改为'John'。 +UPDATE tablename1 SET fname='John' WHERE lname='Mutt'; + +-- 删除tablename1表lname列以'M'开头的行。 +DELETE FROM tablename1 WHERE lname like 'M%'; + +-- 删除tablename1表的所有行,留下空表。 +DELETE FROM tablename1; + +-- 删除整个tablename1表。 +DROP TABLE tablename1; +``` diff --git a/zh-cn/swift-cn.html.markdown b/zh-cn/swift-cn.html.markdown index c25b2918..18bc52ed 100644 --- a/zh-cn/swift-cn.html.markdown +++ b/zh-cn/swift-cn.html.markdown @@ -110,7 +110,7 @@ anyObjectVar = "Changed value to a string, not good practice, but possible." // -// Mark: 数组与字典(关联数组) +// MARK: 数组与字典(关联数组) // /* @@ -214,9 +214,9 @@ func greet(name: String, day: String) -> String { } greet("Bob", day: "Tuesday") -// 第一个参数表示外部参数名和内部参数名使用同一个名称。 +// 第一个参数`_`表示不使用外部参数名,忽略`_`表示外部参数名和内部参数名使用同一个名称。 // 第二个参数表示外部参数名使用 `externalParamName` ,内部参数名使用 `localParamName` -func greet2(requiredName requiredName: String, externalParamName localParamName: String) -> String { +func greet2(_ requiredName: String, externalParamName localParamName: String) -> String { return "Hello \(requiredName), the day is \(localParamName)" } greet2(requiredName:"John", externalParamName: "Sunday") // 调用时,使用命名参数来指定参数的值 @@ -250,7 +250,7 @@ var increment = makeIncrementer() increment(7) // 强制进行指针传递 (引用传递),使用 `inout` 关键字修饰函数参数 -func swapTwoInts(inout a: Int, inout b: Int) { +func swapTwoInts(a: inout Int, b: inout Int) { let tempA = a a = b b = tempA @@ -521,7 +521,7 @@ class MyShape: Rect { // 在 optional 属性,方法或下标运算符后面加一个问号,可以优雅地忽略 nil 值,返回 nil。 // 这样就不会引起运行时错误 (runtime error) - if let reshape = self.delegate?.canReshape?() where reshape { + if let reshape = self.delegate?.canReshape?() { // 注意语句中的问号 self.delegate?.reshape?() } @@ -575,10 +575,10 @@ print(foundAtIndex == 2) // true // 自定义运算符可以以下面的字符打头: // / = - + * % < > ! & | ^ . ~ // 甚至是 Unicode 的数学运算符等 -prefix operator !!! {} +prefix operator !!! // 定义一个前缀运算符,使矩形的边长放大三倍 -prefix func !!! (inout shape: Square) -> Square { +prefix func !!! (shape: inout Square) -> Square { shape.sideLength *= 3 return shape } @@ -591,8 +591,8 @@ print(mySquare.sideLength) // 4 print(mySquare.sideLength) // 12 // 运算符也可以是泛型 -infix operator <-> {} -func <-><T: Equatable> (inout a: T, inout b: T) { +infix operator <-> +func <-><T: Equatable> (a: inout T, b: inout T) { let c = a a = b b = c diff --git a/zh-cn/typescript-cn.html.markdown b/zh-cn/typescript-cn.html.markdown index 2651b1cb..5d6153da 100644 --- a/zh-cn/typescript-cn.html.markdown +++ b/zh-cn/typescript-cn.html.markdown @@ -9,97 +9,103 @@ filename: learntypescript-cn.ts lang: zh-cn --- -TypeScript是一门为开发大型JavaScript应用而设计的语言。TypeScript在JavaScript的基础上增加了类、模块、接口、泛型和静态类型(可选)等常见的概念。它是JavaScript的一个超集:所有JavaScript代码都是有效的TypeScript代码,所以任何JavaScript项目都可以无缝引入TypeScript. TypeScript编译器会把TypeScript代码编译成JavaScript代码。 +TypeScript 是一门为开发大型 JavaScript 应用而设计的语言。TypeScript 在 JavaScript 的基础上增加了类、模块、接口、泛型和静态类型(可选)等常见的概念。它是 JavaScript 的超集:所有 JavaScript 代码都是有效的 TypeScript 代码,因此任何 JavaScript 项目都可以无缝引入 TypeScript,TypeScript 编译器最终会把 TypeScript 代码编译成 JavaScript 代码。 -本文只关注TypeScript额外增加的区别于[JavaScript](../javascript-cn/)的语法,. +本文只关注 TypeScript 额外增加的区别于 [JavaScript](../javascript-cn/) 的语法,. -如需测试TypeScript编译器,你可以在[Playground](http://www.typescriptlang.org/Playground)码代码,它会自动编译成JavaScript代码然后直接显示出来。 +如需测试 TypeScript 编译器,你可以到 [Playground](https://www.typescriptlang.org/play/) 编写代码,它会自动将你编写的 TypeScript 代码编译成 JavaScript 代码后,在右侧即时展示出来。 -```js -// TypeScript有三种基本类型 -var isDone: boolean = false; -var lines: number = 42; -var name: string = "Anders"; +```ts +// TypeScript 有三种基本类型,布尔类型、数值类型、字符串类型 +let isDone: boolean = false; +let lines: number = 42; +let name: string = 'Anders'; -// 如果不知道是什么类型,可以使用"any"(任意)类型 -var notSure: any = 4; -notSure = "maybe a string instead"; -notSure = false; // 亦可,定义为布尔型 +// 如果不知道是什么类型,可以使用 "any" (任意)类型 +let notSure: any = 4; +notSure = '可以重新赋值,转换为字符串类型'; +notSure = false; // 亦可,重新定义为布尔类型 -// 对于集合的声明, 有类型化数组和泛型数组 -var list: number[] = [1, 2, 3]; -// 另外一种,使用泛型数组 -var list: Array<number> = [1, 2, 3]; +// 使用 const 关键字将一个字面量修饰为常量 +const numLivesForCat = 9; +numLivesForCat = 1; // 常量不能重新被赋值,所以这里会报错 + +// TypeScript 中的 collection 有两种表示形式, 一种是有类型的数组,另一种是泛型数组 +let list: number[] = [1, 2, 3]; +// 或者,使用泛型数组 +let list: Array<number> = [1, 2, 3]; // 枚举: -enum Color {Red, Green, Blue}; -var c: Color = Color.Green; +enum Color {Red, Green, Blue} +let c: Color = Color.Green; -// 最后,"void"用于函数没有任何返回的特殊情况下 +// 最后是 "void",它用于表明函数没有任何返回值的特殊情况 function bigHorribleAlert(): void { - alert("I'm a little annoying box!"); + alert('我是个烦人的弹出框!'); } -// 函数是"第一等公民"(first class citizens), 支持使用箭头表达式和类型推断 - -// 以下是相等的,TypeScript编译器会把它们编译成相同的JavaScript代码 -var f1 = function(i: number): number { return i * i; } -// 返回推断类型的值 -var f2 = function(i: number) { return i * i; } -var f3 = (i: number): number => { return i * i; } -// 返回推断类型的值 -var f4 = (i: number) => { return i * i; } -// 返回推断类型的值, 单行程式可以不需要return关键字和大括号 -var f5 = (i: number) => i * i; - -// 接口是结构化的,任何具有这些属性的对象都与该接口兼容 +// 函数是"一等公民"(first class citizens), 支持使用 lambda 胖箭头表达式和类型推断 + +// 以下 f1-f5 五个函数是等价的,TypeScript 编译器会把它们编译成相同的 JavaScript 代码(可以到 Playground 验证) +// 一般的函数 +let f1 = function(i: number): number { return i * i; }; +// 根据返回值推断函数返回类型 +let f2 = function(i: number) { return i * i; }; +// 胖箭头表达式 +let f3 = (i: number): number => { return i * i; }; +// 根据返回值推断返回类型的胖箭头表达式 +let f4 = (i: number) => { return i * i; }; +// 根据返回值推断返回类型的胖箭头表达式, 省略花括号的同时,可以同时省去 return 关键字 +let f5 = (i: number) => i * i; + +// 接口是结构化的,任何具备接口中声明的全部属性的对象,都与该接口兼容 interface Person { name: string; - // 可选属性,使用"?"标识 + // 使用 "?" 标识,表明该属性是一个非必需属性 age?: number; // 函数 move(): void; } -// 实现"Person"接口的对象,当它有了"name"和"move"方法之后可被视为一个"Person" -var p: Person = { name: "Bobby", move: () => {} }; -// 带了可选参数的对象 -var validPerson: Person = { name: "Bobby", age: 42, move: () => {} }; -// 因为"age"不是"number"类型所以这不是一个"Person" -var invalidPerson: Person = { name: "Bobby", age: true }; +// 实现 "Person" 接口的对象,当它具备 "name" 属性和 "move" 方法之后可被视为一个 "Person" +let p: Person = { name: 'Bobby', move: () => {} }; +// 带可选属性的对象 +let validPerson: Person = { name: 'Bobby', age: 42, move: () => {} }; +// 由于该对象 "age" 属性的类型不是 "number" ,所以这不是一个 "Person" +let invalidPerson: Person = { name: 'Bobby', age: true }; // 接口同样可以描述一个函数的类型 interface SearchFunc { (source: string, subString: string): boolean; } -// 参数名并不重要,参数类型才是重要的 -var mySearch: SearchFunc; +// 参数名并不重要,参数类型才是最重要的 +let mySearch: SearchFunc; mySearch = function(src: string, sub: string) { - return src.search(sub) != -1; -} + return src.search(sub) !== -1; +}; -// 类 - 成员默认为公共的(public) +// 类 - 成员访问权限默认都是公共的 (public) class Point { - // 属性 + // 成员属性 x: number; - // 构造器 - 这里面的public/private关键字会为属性生成样板代码和初始化值 - // 这个例子中,y会被同x一样定义,不需要额外代码 - // 同样支持默认值 + // 构造器 - 在构造器中使用 public/private 关键字修饰的变量,会被声明为类的成员属性。 + // 下面这个例子中,y 会像 x 一样被声明定义为类成员属性,而不再需要额外代码 + // 声明时,同样支持指定默认值 constructor(x: number, public y: number = 0) { this.x = x; } - // 函数 + // 成员函数 dist() { return Math.sqrt(this.x * this.x + this.y * this.y); } // 静态成员 static origin = new Point(0, 0); } -var p1 = new Point(10 ,20); -var p2 = new Point(25); //y为0 +let p1 = new Point(10 , 20); +let p2 = new Point(25); // y 为构造器中指定的默认值:0 // 继承 class Point3D extends Point { @@ -107,14 +113,14 @@ class Point3D extends Point { super(x, y); // 必须显式调用父类的构造器 } - // 重写 + // 重写父类中的 dist() 函数 dist() { - var d = super.dist(); + let d = super.dist(); return Math.sqrt(d * d + this.z * this.z); } } -// 模块, "."可以作为子模块的分隔符 +// 模块, "." 符号可以作为子模块的分隔符 module Geometry { export class Square { constructor(public sideLength: number = 0) { @@ -125,12 +131,12 @@ module Geometry { } } -var s1 = new Geometry.Square(5); +let s1 = new Geometry.Square(5); -// 引入模块并定义本地别名 +// 为模块创建一个本地别名 import G = Geometry; -var s2 = new G.Square(10); +let s2 = new G.Square(10); // 泛型 // 类 @@ -146,21 +152,21 @@ interface Pair<T> { } // 以及函数 -var pairToTuple = function<T>(p: Pair<T>) { +let pairToTuple = function<T>(p: Pair<T>) { return new Tuple(p.item1, p.item2); }; -var tuple = pairToTuple({ item1:"hello", item2:"world"}); +let tuple = pairToTuple({ item1: 'hello', item2: 'world'}); // 引用定义文件 -// <reference path="jquery.d.ts" /> +/// <reference path="jquery.d.ts" /> // 模板字符串(使用反引号的字符串) // 嵌入变量的模板字符串 -var name = 'Tyrone'; -var greeting = `Hi ${name}, how are you?` +let name = 'Tyrone'; +let greeting = `Hi ${name}, how are you?`; // 有多行内容的模板字符串 -var multiline = `This is an example +let multiline = `This is an example of a multiline string`; ``` diff --git a/zh-cn/visualbasic-cn.html.markdown b/zh-cn/visualbasic-cn.html.markdown index 8bdfabc6..e30041b3 100644 --- a/zh-cn/visualbasic-cn.html.markdown +++ b/zh-cn/visualbasic-cn.html.markdown @@ -5,10 +5,10 @@ contributors: translators: - ["Abner Chou", "http://cn.abnerchou.me"] lang: zh-cn -filename: learnvisualbasic.vb-cn +filename: learnvisualbasic-cn.vb --- -```vb +``` Module Module1 Sub Main() diff --git a/zh-cn/yaml-cn.html.markdown b/zh-cn/yaml-cn.html.markdown index bbda20e9..3ba2afd8 100644 --- a/zh-cn/yaml-cn.html.markdown +++ b/zh-cn/yaml-cn.html.markdown @@ -23,18 +23,23 @@ YAML 根本不容许文字制表符。 ################ # 我们的根对象 (它们在整个文件里延续) 将会是一个映射, -# 它等价于在别的语言里的一个字典,哈西表或对象。 +# 它等价于在别的语言里的一个字典,哈希表或对象。 key: value another_key: Another value goes here. a_number_value: 100 -# 如果你想将数字 1 作为值,你必须要将它括在引号中。 -# 不然 YAML 解析器会假定它是一个布尔值 true。 +# 数字 1 会被解释为数值,而不是一个布尔值。 +# 如果你想要的是一个布尔值,使用 true。 scientific_notation: 1e+12 boolean: true null_value: null key with spaces: value -# 注意到字符串不需要被括在引号中。但是,它们可以被括起来。 -"Keys can be quoted too.": "Useful if you want to put a ':' in your key." +# 注意,字符串不必被括在引号中,但也可以被括起来。 +however: 'A string, enclosed in quotes.' +'Keys can be quoted too.': "Useful if you want to put a ':' in your key." +single quotes: 'have ''one'' escape pattern' +double quotes: "have many: \", \0, \t, \u263A, \x0d\x0a == \r\n, and more." +# UTF-8/16/32 字符需要被转义(encoded) +Superscript two: \u00B2 # 多行字符串既可以写成像一个'文字块'(使用 |), # 或像一个'折叠块'(使用 '>')。 @@ -60,21 +65,21 @@ folded_style: > # 集合类型 # #################### -# 嵌套是通过缩进完成的。 +# 嵌套是通过缩进完成的。推荐使用 2 个空格的缩进(但非必须) a_nested_map: - key: value - another_key: Another Value - another_nested_map: - hello: hello + key: value + another_key: Another Value + another_nested_map: + hello: hello -# 映射的键值不必是字符串。 +# 映射的键不必是字符串。 0.25: a float key -# 键值也可以是复合型的,比如多行对象 +# 键也可以是复合型的,比如多行对象 # 我们用 ? 后跟一个空格来表示一个复合键的开始。 ? | - This is a key - that has multiple lines + This is a key + that has multiple lines : and this is its value # YAML 也允许使用复杂键语法表示序列间的映射关系。 @@ -85,20 +90,24 @@ a_nested_map: : [ 2001-01-01, 2002-02-02 ] # 序列 (等价于列表或数组) 看起来像这样: +# 注意 '-' 算作缩进 a_sequence: - - Item 1 - - Item 2 - - 0.5 # 序列可以包含不同类型。 - - Item 4 - - key: value - another_key: another_value - - - - This is a sequence - - inside another sequence + - Item 1 + - Item 2 + - 0.5 # 序列可以包含不同类型。 + - Item 4 + - key: value + another_key: another_value + - + - This is a sequence + - inside another sequence + - - - Nested sequence indicators + - can be collapsed # 因为 YAML 是 JSON 的超集,你也可以写 JSON 风格的映射和序列: json_map: {"key": "value"} json_seq: [3, 2, 1, "takeoff"] +and quotes are optional: {key: [3, 2, 1, takeoff]} ####################### # 其余的 YAML 特性 # @@ -111,15 +120,18 @@ other_anchor: *anchor_name # 锚也可被用来复制/继承属性 base: &base - name: Everyone has same name + name: Everyone has same name + +# The regexp << is called Merge Key Language-Independent Type. +# 它表明指定映射的所有键值会插入到当前的映射中。 foo: &foo - <<: *base - age: 10 + <<: *base + age: 10 bar: &bar - <<: *base - age: 20 + <<: *base + age: 20 # foo 和 bar 将都含有 name: Everyone has same name @@ -146,22 +158,25 @@ date: 2002-12-14 # 这个 !!binary 标签表明这个字符串实际上 # 是一个用 base64 编码表示的二进制 blob。 gif_file: !!binary | - R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5 - OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+ - +f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC - AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs= + R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5 + OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+ + +f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC + AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs= # YAML 还有一个集合类型,它看起来像这样: set: - ? item1 - ? item2 - ? item3 + ? item1 + ? item2 + ? item3 +or: {item1, item2, item3} -# 像 Python 一样,集合仅是值为 null 的映射;上面的集合等价于: +# 集合只是值为 null 的映射;上面的集合等价于: set2: - item1: null - item2: null - item3: null + item1: null + item2: null + item3: null + +... # document end ``` ### 更多资源 |