diff options
Diffstat (limited to 'zh-cn')
-rw-r--r-- | zh-cn/go-zh.html.markdown | 279 | ||||
-rwxr-xr-x | zh-cn/python-cn.html.markdown | 277 | ||||
-rw-r--r-- | zh-cn/racket-cn.html.markdown | 608 | ||||
-rw-r--r-- | zh-cn/ruby-cn.html.markdown | 5 |
4 files changed, 1029 insertions, 140 deletions
diff --git a/zh-cn/go-zh.html.markdown b/zh-cn/go-zh.html.markdown new file mode 100644 index 00000000..25fd1f03 --- /dev/null +++ b/zh-cn/go-zh.html.markdown @@ -0,0 +1,279 @@ +--- +名字:Go +分类:编程语言 +文件名:learngo.go +贡献者: + - ["Sonia Keys", "https://github.com/soniakeys"] + - ["pantaovay", "https://github.com/pantaovay"] +--- + +发明Go语言是出于更好地完成工作的需要。Go不是计算机科学的最新发展潮流,但它却提供了解决现实问题的最新最快的方法。 + +Go拥有命令式语言的静态类型,编译很快,执行也很快,同时加入了对于目前多核CPU的并发计算支持,也有相应的特性来实现大规模编程。 + +Go语言有非常棒的标准库,还有一个充满热情的社区。 + +```Go +// 单行注释 +/* 多行 + 注释 */ + +// 导入包的子句在每个源文件的开头。 +// Main比较特殊,它用来声明可执行文件,而不是一个库。 +package main + +// Import语句声明了当前文件引用的包。 +import ( + "fmt" // Go语言标准库中的包 + "net/http" // 一个web服务器包 + "strconv" // 字符串转换 +) + +//函数声明:Main是程序执行的入口。不管你喜欢还是不喜欢,反正G就用了花括号来包住函数体。 +func main() { + // 往标准输出打印一行。 + // 用包名fmt限制打印函数。 + fmt.Println("Hello world!") + + // 调用当前包的另一个函数。 + beyondHello() +} + +// 函数可以在括号里加参数。 +// 如果没有参数的话,也需要一个空括号。 +func beyondHello() { + var x int // 变量声明,变量必须在使用之前声明。 + x = 3 // 变量赋值。 + // 可以用:=来偷懒,它自动把变量类型、声明和赋值都搞定了。 + y := 4 + sum, prod := learnMultiple(x, y) // 多个返回变量的函数 + fmt.Println("sum:", sum, "prod:", prod) // 简单输出 + learnTypes() // 少于y分钟,学的更多! +} + +// 多变量和多返回值的函数 +func learnMultiple(x, y int) (sum, prod int) { + return x + y, x * y // 返回两个值 +} + +// 内置变量类型和关键词 +func learnTypes() { + // 短声明给你所想。 + s := "Learn Go!" // String类型 + + s2 := `A "raw" string literal +can include line breaks.` // 同样是String类型 + + // 非ascii字符。Go使用UTF-8编码。 + g := 'Σ' // rune类型,uint32的别名,使用UTF-8编码 + + f := 3.14195 // float64类型,IEEE-754 64位浮点数 + c := 3 + 4i // complex128类型,内部使用两个float64表示 + + // Var变量可以直接初始化。 + var u uint = 7 // unsigned 无符号变量,但是实现依赖int型变量的长度 + var pi float32 = 22. / 7 + + // 字符转换 + n := byte('\n') // byte是uint8的别名 + + // 数组类型编译的时候大小固定。 + var a4 [4] int // 有4个int变量的数组,初始为0 + a3 := [...]int{3, 1, 5} // 有3个int变量的数组,同时进行了初始化 + + // Slice 有动态大小。Array和Slice各有千秋,但是使用slice的地方更多些。 + s3 := []int{4, 5, 9} // 和a3相比,这里没有省略号 + s4 := make([]int, 4) // 分配一个有4个int型变量的slice,全部被初始化为0 + + var d2 [][]float64 // 声明而已,什么都没有分配 + bs := []byte("a slice") // 类型转换的语法 + + p, q := learnMemory() // 声明p,q为int型变量的指针 + fmt.Println(*p, *q) // * 取值 + + // Map是动态可增长关联数组,和其他语言中的hash或者字典相似。 + m := map[string]int{"three": 3, "four": 4} + m["one"] = 1 + + // 在Go语言中未使用的变量在编译的时候会报错,而不是warning。 + // 下划线 _ 可以使你“使用”一个变量,但是丢弃它的值。 + _,_,_,_,_,_,_,_,_ = s2, g, f, u, pi, n, a3, s4, bs + // 输出变量 + fmt.Println(s, c, a4, s3, d2, m) + + learnFlowControl() // 回到流程控制 +} + +// Go全面支持垃圾回收。Go有指针,但是不支持指针运算。 +// 你会因为空指针而犯错,但是不会因为增加指针而犯错。 +func learnMemory() (p, q *int) { + // 返回int型变量指针p和q + p = new(int) // 内置函数new分配内存 + // 自动将分配的int赋值0,p不再是空的了。 + s := make([]int, 20) // 给20个int变量分配一块内存 + s[3] = 7 // 赋值 + r := -2 // 声明另一个局部变量 + return &s[3], &r // & 取址 +} + +func expensiveComputation() int { + return 1e6 +} + +func learnFlowControl() { + // If需要花括号,括号就免了 + if true { + fmt.Println("told ya") + } + // 用go fmt 命令可以帮你格式化代码,所以不用怕被人吐槽代码风格了,也不用容忍被人的代码风格。 + if false { + // pout + } else { + // gloat + } + // 如果太多嵌套的if语句,推荐使用switch + x := 1 + switch x { + case 0: + case 1: + // 隐式调用break语句,匹配上一个即停止 + case 2: + // 不会运行 + } + // 和if一样,for也不用括号 + for x := 0; x < 3; x++ { // ++ 自增 + fmt.Println("iteration", x) + } + // x在这里还是1。为什么? + + // for 是go里唯一的循环关键字,不过它有很多变种 + for { // 无限循环 + break // 骗你的 + continue // 不会运行的 + } + // 和for一样,if中的:=先给y赋值,然后再和x作比较。 + if y := expensiveComputation(); y > x { + x = y + } + // 闭包函数 + xBig := func() bool { + return x > 100 // x是上面声明的变量引用 + } + fmt.Println("xBig:", xBig()) // true (上面把y赋给x了) + x /= 1e5 // x变成10 + fmt.Println("xBig:", xBig()) // 现在是false + + // 当你需要goto的时候,你会爱死它的! + goto love +love: + + learnInterfaces() // 好东西来了! +} + +// 定义Stringer为一个接口类型,有一个方法String +type Stringer interface { + String() string +} + +// 定义pair为一个结构体,有x和y两个int型变量。 +type pair struct { + x, y int +} + +// 定义pair类型的方法,实现Stringer接口。 +func (p pair) String() string { // p被叫做“接收器” + // Sprintf是fmt包中的另一个公有函数。 + // 用 . 调用p中的元素。 + return fmt.Sprintf("(%d, %d)", p.x, p.y) +} + +func learnInterfaces() { + // 花括号用来定义结构体变量,:=在这里将一个结构体变量赋值给p。 + p := pair{3, 4} + fmt.Println(p.String()) // 调用pair类型p的String方法 + var i Stringer // 声明i为Stringer接口类型 + i = p // 有效!因为p实现了Stringer接口(类似java中的塑型) + // 调用i的String方法,输出和上面一样 + fmt.Println(i.String()) + + // fmt包中的Println函数向对象要它们的string输出,实现了String方法就可以这样使用了。(类似java中的序列化) + fmt.Println(p) // 输出和上面一样,自动调用String函数。 + fmt.Println(i) // 输出和上面一样。 + + learnErrorHandling() +} + +func learnErrorHandling() { + // ", ok"用来判断有没有正常工作 + m := map[int]string{3: "three", 4: "four"} + if x, ok := m[1]; !ok { // ok 为false,因为m中没有1 + fmt.Println("no one there") + } else { + fmt.Print(x) // 如果x在map中的话,x就是那个值喽。 + } + // 错误可不只是ok,它还可以给出关于问题的更多细节。 + if _, err := strconv.Atoi("non-int"); err != nil { // _ discards value + // 输出"strconv.ParseInt: parsing "non-int": invalid syntax" + fmt.Println(err) + } + // 待会再说接口吧。同时, + learnConcurrency() +} + +// c是channel类型,一个并发安全的通信对象。 +func inc(i int, c chan int) { + c <- i + 1 // <-把右边的发送到左边的channel。 +} + +// 我们将用inc函数来并发地增加一些数字。 +func learnConcurrency() { + // 用make来声明一个slice,make会分配和初始化slice,map和channel。 + c := make(chan int) + // 用go关键字开始三个并发的goroutine,如果机器支持的话,还可能是并行执行。三个都被发送到同一个channel。 + go inc(0, c) // go is a statement that starts a new goroutine. + go inc(10, c) + go inc(-805, c) + // 从channel中独处结果并打印。 + // 打印出什么东西是不可预知的。 + fmt.Println(<-c, <-c, <-c) // channel在右边的时候,<-是接收操作。 + + cs := make(chan string) // 操作string的channel + cc := make(chan chan string) // 操作channel的channel + go func() { c <- 84 }() // 开始一个goroutine来发送一个新的数字 + go func() { cs <- "wordy" }() // 发送给cs + // Select类似于switch,但是每个case包括一个channel操作。它随机选择一个准备好通讯的case。 + select { + case i := <-c: // 从channel接收的值可以赋给其他变量 + fmt.Println("it's a", i) + case <-cs: // 或者直接丢弃 + fmt.Println("it's a string") + case <-cc: // 空的,还没作好通讯的准备 + fmt.Println("didn't happen.") + } + // 上面c或者cs的值被取到,其中一个goroutine结束,另外一个保持阻塞。 + + learnWebProgramming() // Go很适合web编程,我知道你也想学! +} + +// http包中的一个简单的函数就可以开启web服务器。 +func learnWebProgramming() { + // ListenAndServe第一个参数指定了监听端口,第二个参数是一个接口,特定是http.Handler。 + err := http.ListenAndServe(":8080", pair{}) + fmt.Println(err) // 不要无视错误。 +} + +// 使pair实现http.Handler接口的ServeHTTP方法。 +func (p pair) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // 使用http.ResponseWriter返回数据 + w.Write([]byte("You learned Go in Y minutes!")) +} +``` + +## 更进一步 + +Go的根源在[Go官方网站](http://golang.org/)。 +在那里你可以学习入门教程,通过浏览器交互式地学习,而且可以读到很多东西。 + +强烈推荐阅读语言定义部分,很简单而且很简洁!(as language definitions go these days.) + +学习Go还要阅读Go标准库的源代码,全部文档化了,可读性非常好,可以学到go,go style和go idioms。在文档中点击函数名,源代码就出来了! diff --git a/zh-cn/python-cn.html.markdown b/zh-cn/python-cn.html.markdown index 764eed54..51efaac3 100755 --- a/zh-cn/python-cn.html.markdown +++ b/zh-cn/python-cn.html.markdown @@ -17,6 +17,7 @@ Python 由 Guido Van Rossum 在90年代初创建。 它现在是最流行的语 如果是Python 3,请在网络上寻找其他教程 ```python + # 单行注释 """ 多行字符串可以用 三个引号包裹,不过这也可以被当做 @@ -28,84 +29,84 @@ Python 由 Guido Van Rossum 在90年代初创建。 它现在是最流行的语 #################################################### # 数字类型 -3 #=> 3 +3 # => 3 # 简单的算数 -1 + 1 #=> 2 -8 - 1 #=> 7 -10 * 2 #=> 20 -35 / 5 #=> 7 +1 + 1 # => 2 +8 - 1 # => 7 +10 * 2 # => 20 +35 / 5 # => 7 # 整数的除法会自动取整 -5 / 2 #=> 2 +5 / 2 # => 2 # 要做精确的除法,我们需要引入浮点数 2.0 # 浮点数 -11.0 / 4.0 #=> 2.75 好多了 +11.0 / 4.0 # => 2.75 精确多了 # 括号具有最高优先级 -(1 + 3) * 2 #=> 8 +(1 + 3) * 2 # => 8 -# 布尔值也是原始数据类型 +# 布尔值也是基本的数据类型 True False -# 用not来取非 -not True #=> False -not False #=> True +# 用 not 来取非 +not True # => False +not False # => True # 相等 -1 == 1 #=> True -2 == 1 #=> False +1 == 1 # => True +2 == 1 # => False # 不等 -1 != 1 #=> False -2 != 1 #=> True +1 != 1 # => False +2 != 1 # => True # 更多的比较操作符 -1 < 10 #=> True -1 > 10 #=> False -2 <= 2 #=> True -2 >= 2 #=> True +1 < 10 # => True +1 > 10 # => False +2 <= 2 # => True +2 >= 2 # => True # 比较运算可以连起来写! -1 < 2 < 3 #=> True -2 < 3 < 2 #=> False +1 < 2 < 3 # => True +2 < 3 < 2 # => False -# 字符串通过"或'括起来 +# 字符串通过 " 或 ' 括起来 "This is a string." 'This is also a string.' # 字符串通过加号拼接 -"Hello " + "world!" #=> "Hello world!" +"Hello " + "world!" # => "Hello world!" # 字符串可以被视为字符的列表 -"This is a string"[0] #=> 'T' +"This is a string"[0] # => 'T' # % 可以用来格式化字符串 "%s can be %s" % ("strings", "interpolated") -# 也可以用format方法来格式化字符串 +# 也可以用 format 方法来格式化字符串 # 推荐使用这个方法 "{0} can be {1}".format("strings", "formatted") # 也可以用变量名代替数字 "{name} wants to eat {food}".format(name="Bob", food="lasagna") # None 是对象 -None #=> None +None # => None # 不要用相等 `==` 符号来和None进行比较 -# 要用 `is` -"etc" is None #=> False -None is None #=> True +# 要用 `is` +"etc" is None # => False +None is None # => True # 'is' 可以用来比较对象的相等性 # 这个操作符在比较原始数据时没多少用,但是比较对象时必不可少 -# None, 0, 和空字符串都被算作False -# 其他的均为True -0 == False #=> True -"" == False #=> True +# None, 0, 和空字符串都被算作 False +# 其他的均为 True +0 == False # => True +"" == False # => True #################################################### @@ -116,16 +117,16 @@ None is None #=> True print "I'm Python. Nice to meet you!" -# 给变量赋值前不需要事先生命 -some_var = 5 # 规范用小写字母和下划线来做为变量名 -some_var #=> 5 +# 给变量赋值前不需要事先声明 +some_var = 5 # 一般建议使用小写字母和下划线组合来做为变量名 +some_var # => 5 -# 访问之前为赋值的变量会抛出异常 -# 查看控制流程一节来了解异常处理 -some_other_var # 抛出命名异常 +# 访问未赋值的变量会抛出异常 +# 可以查看控制流程一节来了解如何异常处理 +some_other_var # 抛出 NameError -# if语句可以作为表达式来使用 -"yahoo!" if 3 > 2 else 2 #=> "yahoo!" +# if 语句可以作为表达式来使用 +"yahoo!" if 3 > 2 else 2 # => "yahoo!" # 列表用来保存序列 li = [] @@ -133,64 +134,64 @@ li = [] other_li = [4, 5, 6] # 在列表末尾添加元素 -li.append(1) #li 现在是 [1] -li.append(2) #li 现在是 [1, 2] -li.append(4) #li 现在是 [1, 2, 4] -li.append(3) #li 现在是 [1, 2, 4, 3] +li.append(1) # li 现在是 [1] +li.append(2) # li 现在是 [1, 2] +li.append(4) # li 现在是 [1, 2, 4] +li.append(3) # li 现在是 [1, 2, 4, 3] # 移除列表末尾元素 -li.pop() #=> 3 and li is now [1, 2, 4] -# 放回来 +li.pop() # => 3 li 现在是 [1, 2, 4] +# 重新加进去 li.append(3) # li is now [1, 2, 4, 3] again. # 像其他语言访问数组一样访问列表 -li[0] #=> 1 +li[0] # => 1 # 访问最后一个元素 -li[-1] #=> 3 +li[-1] # => 3 # 越界会抛出异常 -li[4] # 抛出越界异常 +li[4] # 抛出越界异常 # 切片语法需要用到列表的索引访问 # 可以看做数学之中左闭右开区间 -li[1:3] #=> [2, 4] +li[1:3] # => [2, 4] # 省略开头的元素 -li[2:] #=> [4, 3] +li[2:] # => [4, 3] # 省略末尾的元素 -li[:3] #=> [1, 2, 4] +li[:3] # => [1, 2, 4] # 删除特定元素 -del li[2] # li 现在是 [1, 2, 3] +del li[2] # li 现在是 [1, 2, 3] # 合并列表 -li + other_li #=> [1, 2, 3, 4, 5, 6] - 不改变这两个列表 +li + other_li # => [1, 2, 3, 4, 5, 6] - 并不会不改变这两个列表 -# 通过拼接合并列表 -li.extend(other_li) # li 是 [1, 2, 3, 4, 5, 6] +# 通过拼接来合并列表 +li.extend(other_li) # li 是 [1, 2, 3, 4, 5, 6] -# 用in来返回元素是否在列表中 -1 in li #=> True +# 用 in 来返回元素是否在列表中 +1 in li # => True # 返回列表长度 -len(li) #=> 6 +len(li) # => 6 -# 元组类似于列表,但是他是不可改变的 +# 元组类似于列表,但它是不可改变的 tup = (1, 2, 3) -tup[0] #=> 1 +tup[0] # => 1 tup[0] = 3 # 类型错误 # 对于大多数的列表操作,也适用于元组 -len(tup) #=> 3 -tup + (4, 5, 6) #=> (1, 2, 3, 4, 5, 6) -tup[:2] #=> (1, 2) -2 in tup #=> True +len(tup) # => 3 +tup + (4, 5, 6) # => (1, 2, 3, 4, 5, 6) +tup[:2] # => (1, 2) +2 in tup # => True # 你可以将元组解包赋给多个变量 -a, b, c = (1, 2, 3) # a是1,b是2,c是3 -# 如果不加括号,那么会自动视为元组 +a, b, c = (1, 2, 3) # a 是 1,b 是 2,c 是 3 +# 如果不加括号,将会被自动视为元组 d, e, f = 4, 5, 6 # 现在我们可以看看交换两个数字是多么容易的事 -e, d = d, e # d是5,e是4 +e, d = d, e # d 是 5,e 是 4 # 字典用来储存映射关系 @@ -199,59 +200,59 @@ empty_dict = {} filled_dict = {"one": 1, "two": 2, "three": 3} # 字典也用中括号访问元素 -filled_dict["one"] #=> 1 +filled_dict["one"] # => 1 # 把所有的键保存在列表中 -filled_dict.keys() #=> ["three", "two", "one"] +filled_dict.keys() # => ["three", "two", "one"] # 键的顺序并不是唯一的,得到的不一定是这个顺序 # 把所有的值保存在列表中 -filled_dict.values() #=> [3, 2, 1] +filled_dict.values() # => [3, 2, 1] # 和键的顺序相同 # 判断一个键是否存在 -"one" in filled_dict #=> True -1 in filled_dict #=> False +"one" in filled_dict # => True +1 in filled_dict # => False -# 查询一个不存在的键会抛出键异常 -filled_dict["four"] # 键异常 +# 查询一个不存在的键会抛出 KeyError +filled_dict["four"] # KeyError -# 用get方法来避免键异常 -filled_dict.get("one") #=> 1 -filled_dict.get("four") #=> None -# get方法支持在不存在的时候返回一个默认值 -filled_dict.get("one", 4) #=> 1 -filled_dict.get("four", 4) #=> 4 +# 用 get 方法来避免 KeyError +filled_dict.get("one") # => 1 +filled_dict.get("four") # => None +# get 方法支持在不存在的时候返回一个默认值 +filled_dict.get("one", 4) # => 1 +filled_dict.get("four", 4) # => 4 -# Setdefault是一个更安全的添加字典元素的方法 -filled_dict.setdefault("five", 5) #filled_dict["five"] 的值为 5 -filled_dict.setdefault("five", 6) #filled_dict["five"] 的值仍然是 5 +# setdefault 是一个更安全的添加字典元素的方法 +filled_dict.setdefault("five", 5) # filled_dict["five"] 的值为 5 +filled_dict.setdefault("five", 6) # filled_dict["five"] 的值仍然是 5 # 集合储存无顺序的元素 empty_set = set() -# 出事话一个集合 -some_set = set([1,2,2,3,4]) # filled_set 现在是 set([1, 2, 3, 4]) +# 初始化一个集合 +some_set = set([1, 2, 2, 3, 4]) # filled_set 现在是 set([1, 2, 3, 4]) # Python 2.7 之后,大括号可以用来表示集合 -filled_set = {1, 2, 2, 3, 4} # => {1 2 3 4} +filled_set = {1, 2, 2, 3, 4} # => {1 2 3 4} -# 为集合添加元素 -filled_set.add(5) # filled_set 现在是 {1, 2, 3, 4, 5} +# 向集合添加元素 +filled_set.add(5) # filled_set 现在是 {1, 2, 3, 4, 5} -# 用&来实现集合的交 +# 用 & 来计算集合的交 other_set = {3, 4, 5, 6} -filled_set & other_set #=> {3, 4, 5} +filled_set & other_set # => {3, 4, 5} -# 用|来实现集合的并 -filled_set | other_set #=> {1, 2, 3, 4, 5, 6} +# 用 | 来计算集合的并 +filled_set | other_set # => {1, 2, 3, 4, 5, 6} -# 用-来实现集合的差 -{1,2,3,4} - {2,3,5} #=> {1, 4} +# 用 - 来计算集合的差 +{1, 2, 3, 4} - {2, 3, 5} # => {1, 4} -# 用in来判断元素是否存在于集合中 -2 in filled_set #=> True -10 in filled_set #=> False +# 用 in 来判断元素是否存在于集合中 +2 in filled_set # => True +10 in filled_set # => False #################################################### @@ -261,13 +262,13 @@ filled_set | other_set #=> {1, 2, 3, 4, 5, 6} # 新建一个变量 some_var = 5 -# 这是个if语句,在python中缩进是很重要的。 -# 会输出 "some var is smaller than 10" +# 这是个 if 语句,在 python 中缩进是很重要的。 +# 下面的代码片段将会输出 "some var is smaller than 10" if some_var > 10: print "some_var is totally bigger than 10." elif some_var < 10: # 这个 elif 语句是不必须的 print "some_var is smaller than 10." -else: # 也不是必须的 +else: # 这个 else 也不是必须的 print "some_var is indeed 10." @@ -281,7 +282,7 @@ else: # 也不是必须的 for animal in ["dog", "cat", "mouse"]: # 你可以用 % 来格式化字符串 print "%s is a mammal" % animal - + """ `range(number)` 返回从0到给定数字的列表 输出: @@ -294,7 +295,7 @@ for i in range(4): print i """ -While循环 +while 循环 输出: 0 1 @@ -304,29 +305,29 @@ While循环 x = 0 while x < 4: print x - x += 1 # Shorthand for x = x + 1 + x += 1 # x = x + 1 的简写 -# 用 try/except块来处理异常 +# 用 try/except 块来处理异常 # Python 2.6 及以上适用: try: - # 用raise来抛出异常 + # 用 raise 来抛出异常 raise IndexError("This is an index error") except IndexError as e: - pass # Pass就是什么都不做,不过通常这里会做一些恢复工作 + pass # pass 就是什么都不做,不过通常这里会做一些恢复工作 #################################################### ## 4. 函数 #################################################### -# 用def来新建函数 +# 用 def 来新建函数 def add(x, y): print "x is %s and y is %s" % (x, y) - return x + y # Return values with a return statement + return x + y # 通过 return 来返回值 # 调用带参数的函数 -add(5, 6) #=> 输出 "x is 5 and y is 6" 返回 11 +add(5, 6) # => 输出 "x is 5 and y is 6" 返回 11 # 通过关键字赋值来调用函数 add(y=6, x=5) # 顺序是无所谓的 @@ -335,7 +336,7 @@ add(y=6, x=5) # 顺序是无所谓的 def varargs(*args): return args -varargs(1, 2, 3) #=> (1,2,3) +varargs(1, 2, 3) # => (1,2,3) # 我们也可以定义接受多个变量的函数,这些变量是按照关键字排列的 @@ -343,7 +344,7 @@ def keyword_args(**kwargs): return kwargs # 实际效果: -keyword_args(big="foot", loch="ness") #=> {"big": "foot", "loch": "ness"} +keyword_args(big="foot", loch="ness") # => {"big": "foot", "loch": "ness"} # 你也可以同时将一个函数定义成两种形式 def all_the_args(*args, **kwargs): @@ -355,38 +356,38 @@ all_the_args(1, 2, a=3, b=4) prints: {"a": 3, "b": 4} """ -# 当调用函数的时候,我们也可以和之前所做的相反,把元组和字典展开为参数 +# 当调用函数的时候,我们也可以进行相反的操作,把元组和字典展开为参数 args = (1, 2, 3, 4) kwargs = {"a": 3, "b": 4} -all_the_args(*args) # equivalent to foo(1, 2, 3, 4) -all_the_args(**kwargs) # equivalent to foo(a=3, b=4) -all_the_args(*args, **kwargs) # equivalent to foo(1, 2, 3, 4, a=3, b=4) +all_the_args(*args) # 等价于 foo(1, 2, 3, 4) +all_the_args(**kwargs) # 等价于 foo(a=3, b=4) +all_the_args(*args, **kwargs) # 等价于 foo(1, 2, 3, 4, a=3, b=4) -# Python 有一等函数: +# 函数在 python 中是一等公民 def create_adder(x): def adder(y): return x + y return adder add_10 = create_adder(10) -add_10(3) #=> 13 +add_10(3) # => 13 # 匿名函数 -(lambda x: x > 2)(3) #=> True +(lambda x: x > 2)(3) # => True # 内置高阶函数 -map(add_10, [1,2,3]) #=> [11, 12, 13] -filter(lambda x: x > 5, [3, 4, 5, 6, 7]) #=> [6, 7] +map(add_10, [1, 2, 3]) # => [11, 12, 13] +filter(lambda x: x > 5, [3, 4, 5, 6, 7]) # => [6, 7] # 可以用列表方法来对高阶函数进行更巧妙的引用 -[add_10(i) for i in [1, 2, 3]] #=> [11, 12, 13] -[x for x in [3, 4, 5, 6, 7] if x > 5] #=> [6, 7] +[add_10(i) for i in [1, 2, 3]] # => [11, 12, 13] +[x for x in [3, 4, 5, 6, 7] if x > 5] # => [6, 7] #################################################### ## 5. 类 #################################################### -# 我们新建的类是从object类中继承的 +# 我们新建的类是从 object 类中继承的 class Human(object): # 类属性,由所有类的对象共享 @@ -397,9 +398,9 @@ class Human(object): # 将参数赋给对象成员属性 self.name = name - # 成员方法,参数要有self + # 成员方法,参数要有 self def say(self, msg): - return "%s: %s" % (self.name, msg) + return "%s: %s" % (self.name, msg) # 类方法由所有类的对象共享 # 这类方法在调用时,会把类本身传给第一个参数 @@ -421,15 +422,15 @@ j = Human("Joel") print j.say("hello") # 输出 "Joel: hello" # 访问类的方法 -i.get_species() #=> "H. sapiens" +i.get_species() # => "H. sapiens" # 改变共享属性 Human.species = "H. neanderthalensis" -i.get_species() #=> "H. neanderthalensis" -j.get_species() #=> "H. neanderthalensis" +i.get_species() # => "H. neanderthalensis" +j.get_species() # => "H. neanderthalensis" # 访问静态变量 -Human.grunt() #=> "*grunt*" +Human.grunt() # => "*grunt*" #################################################### @@ -438,12 +439,12 @@ Human.grunt() #=> "*grunt*" # 我们可以导入其他模块 import math -print math.sqrt(16) #=> 4 +print math.sqrt(16) # => 4 -# 我们也可以从一个模块中特定的函数 +# 我们也可以从一个模块中导入特定的函数 from math import ceil, floor -print ceil(3.7) #=> 4.0 -print floor(3.7) #=> 3.0 +print ceil(3.7) # => 4.0 +print floor(3.7) # => 3.0 # 从模块中导入所有的函数 # 警告:不推荐使用 @@ -451,13 +452,13 @@ from math import * # 简写模块名 import math as m -math.sqrt(16) == m.sqrt(16) #=> True +math.sqrt(16) == m.sqrt(16) # => True # Python的模块其实只是普通的python文件 # 你也可以创建自己的模块,并且导入它们 # 模块的名字就和文件的名字相同 -# 以可以通过下面的信息找找要成为模块需要什么属性或方法 +# 也可以通过下面的方法查看模块中有什么属性和方法 import math dir(math) diff --git a/zh-cn/racket-cn.html.markdown b/zh-cn/racket-cn.html.markdown new file mode 100644 index 00000000..d43511ea --- /dev/null +++ b/zh-cn/racket-cn.html.markdown @@ -0,0 +1,608 @@ +--- + +language: racket +lang: zh-cn +filename: learnracket.rkt +contributors: + - ["th3rac25", "https://github.com/voila"] + - ["Eli Barzilay", "https://github.com/elibarzilay"] + - ["Gustavo Schmidt", "https://github.com/gustavoschmidt"] +translators: + - ["lyuehh", "https://github.com/lyuehh"] +--- + +Racket是Lisp/Scheme家族中的一个通用的,多范式的编程语言。 +非常期待您的反馈!你可以通过[@th3rac25](http://twitter.com/th3rac25)或以用户名为 th3rac25 的Google邮箱服务和我取得联系 + +```racket +#lang racket ; 声明我们使用的语言 + +;;; 注释 + +;; 单行注释以分号开始 + +#| 块注释 + 可以横跨很多行而且... + #| + 可以嵌套 + |# +|# + +;; S表达式注释忽略剩下的表达式 +;; 在调试的时候会非常有用 +#; (被忽略的表达式) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; 1. 原始数据类型和操作符 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;; 数字 +9999999999999999999999 ; 整数 +#b111 ; 二进制数字 => 7 +#o111 ; 八进制数字 => 73 +#x111 ; 十六进制数字 => 273 +3.14 ; 实数 +6.02e+23 +1/2 ; 有理数 +1+2i ; 复数 + +;; 函数调用写作(f x y z ...) +;; 在这里 f 是一个函数, x, y, z, ... 是参数 +;; 如果你想创建一个列表数据的字面量, 使用 ' 来阻止它们 +;; 被求值 +'(+ 1 2) ; => (+ 1 2) +;; 接下来,是一些数学运算 +(+ 1 1) ; => 2 +(- 8 1) ; => 7 +(* 10 2) ; => 20 +(expt 2 3) ; => 8 +(quotient 5 2) ; => 2 +(remainder 5 2) ; => 1 +(/ 35 5) ; => 7 +(/ 1 3) ; => 1/3 +(exact->inexact 1/3) ; => 0.3333333333333333 +(+ 1+2i 2-3i) ; => 3-1i + +;;; 布尔类型 +#t ; 为真 +#f ; 为假,#f 之外的任何值都是真 +(not #t) ; => #f +(and 0 #f (error "doesn't get here")) ; => #f +(or #f 0 (error "doesn't get here")) ; => 0 + +;;; 字符 +#\A ; => #\A +#\λ ; => #\λ +#\u03BB ; => #\λ + +;;; 字符串是字符组成的定长数组 +"Hello, world!" +"Benjamin \"Bugsy\" Siegel" ; \是转义字符 +"Foo\tbar\41\x21\u0021\a\r\n" ; 包含C语言的转义字符,和Unicode +"λx:(μα.α→α).xx" ; 字符串可以包含Unicode字符 + +;; 字符串可以相加 +(string-append "Hello " "world!") ; => "Hello world!" + +;; 一个字符串可以看做是一个包含字符的列表 +(string-ref "Apple" 0) ; => #\A + +;; format 可以用来格式化字符串 +(format "~a can be ~a" "strings" "formatted") + +;; 打印字符串非常简单 +(printf "I'm Racket. Nice to meet you!\n") + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; 2. 变量 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; 你可以使用 define 定义一个变量 +;; 变量的名字可以使用任何字符除了: ()[]{}",'`;#|\ +(define some-var 5) +some-var ; => 5 + +;; 你也可以使用Unicode字符 +(define ⊆ subset?) +(⊆ (set 3 2) (set 1 2 3)) ; => #t + +;; 访问未赋值的变量会引发一个异常 +; x ; => x: undefined ... + +;; 本地绑定: `me' 被绑定到 "Bob",并且只在 let 中生效 +(let ([me "Bob"]) + "Alice" + me) ; => "Bob" + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; 3. 结构和集合 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; 结构体 +(struct dog (name breed age)) +(define my-pet + (dog "lassie" "collie" 5)) +my-pet ; => #<dog> +(dog? my-pet) ; => #t +(dog-name my-pet) ; => "lassie" + +;;; 对 (不可变的) +;; `cons' 返回对, `car' 和 `cdr' 从对中提取第1个 +;; 和第2个元素 +(cons 1 2) ; => '(1 . 2) +(car (cons 1 2)) ; => 1 +(cdr (cons 1 2)) ; => 2 + +;;; 列表 + +;; 列表由链表构成, 由 `cons' 的结果 +;; 和一个 `null' (或者 '()) 构成,后者标记了这个列表的结束 +(cons 1 (cons 2 (cons 3 null))) ; => '(1 2 3) +;; `list' 给列表提供了一个非常方便的可变参数的生成器 +(list 1 2 3) ; => '(1 2 3) +;; 一个单引号也可以用来表示一个列表字面量 +'(1 2 3) ; => '(1 2 3) + +;; 仍然可以使用 `cons' 在列表的开始处添加一项 +(cons 4 '(1 2 3)) ; => '(4 1 2 3) + +;; `append' 函数可以将两个列表合并 +(append '(1 2) '(3 4)) ; => '(1 2 3 4) + +;; 列表是非常基础的类型,所以有*很多*操作列表的方法 +;; 下面是一些例子: +(map add1 '(1 2 3)) ; => '(2 3 4) +(map + '(1 2 3) '(10 20 30)) ; => '(11 22 33) +(filter even? '(1 2 3 4)) ; => '(2 4) +(count even? '(1 2 3 4)) ; => 2 +(take '(1 2 3 4) 2) ; => '(1 2) +(drop '(1 2 3 4) 2) ; => '(3 4) + +;;; 向量 + +;; 向量是定长的数组 +#(1 2 3) ; => '#(1 2 3) + +;; 使用 `vector-append' 方法将2个向量合并 +(vector-append #(1 2 3) #(4 5 6)) ; => #(1 2 3 4 5 6) + +;;; Set(翻译成集合也不太合适,所以不翻译了..) + +;; 从一个列表创建一个Set +(list->set '(1 2 3 1 2 3 3 2 1 3 2 1)) ; => (set 1 2 3) + +;; 使用 `set-add' 增加一个成员 +;; (函数式特性: 这里会返回一个扩展后的Set,而不是修改输入的值) +(set-add (set 1 2 3) 4) ; => (set 1 2 3 4) + +;; 使用 `set-remove' 移除一个成员 +(set-remove (set 1 2 3) 1) ; => (set 2 3) + +;; 使用 `set-member?' 测试成员是否存在 +(set-member? (set 1 2 3) 1) ; => #t +(set-member? (set 1 2 3) 4) ; => #f + +;;; 散列表 + +;; 创建一个不变的散列表 (可变散列表的例子在下面) +(define m (hash 'a 1 'b 2 'c 3)) + +;; 根据键取得值 +(hash-ref m 'a) ; => 1 + +;; 获取一个不存在的键是一个异常 +; (hash-ref m 'd) => 没有找到元素 + +;; 你可以给不存在的键提供一个默认值 +(hash-ref m 'd 0) ; => 0 + +;; 使用 `hash-set' 来扩展一个不可变的散列表 +;; (返回的是扩展后的散列表而不是修改它) +(define m2 (hash-set m 'd 4)) +m2 ; => '#hash((b . 2) (a . 1) (d . 4) (c . 3)) + +;; 记住,使用 `hash` 创建的散列表是不可变的 +m ; => '#hash((b . 2) (a . 1) (c . 3)) <-- no `d' + +;; 使用 `hash-remove' 移除一个键值对 (函数式特性,m并不变) +(hash-remove m 'a) ; => '#hash((b . 2) (c . 3)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; 3. 函数 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; 使用 `lambda' 创建函数 +;; 函数总是返回它最后一个表达式的值 +(lambda () "Hello World") ; => #<procedure> +;; 也可以使用 Unicode 字符 `λ' +(λ () "Hello World") ; => 同样的函数 + +;; 使用括号调用一个函数,也可以直接调用一个 lambda 表达式 +((lambda () "Hello World")) ; => "Hello World" +((λ () "Hello World")) ; => "Hello World" + +;; 将函数赋值为一个变量 +(define hello-world (lambda () "Hello World")) +(hello-world) ; => "Hello World" + +;; 你可以使用函数定义的语法糖来简化代码 +(define (hello-world2) "Hello World") + +;; `()`是函数的参数列表 +(define hello + (lambda (name) + (string-append "Hello " name))) +(hello "Steve") ; => "Hello Steve" +;; 同样的,可以使用语法糖来定义: +(define (hello2 name) + (string-append "Hello " name)) + +;; 你也可以使用可变参数, `case-lambda' +(define hello3 + (case-lambda + [() "Hello World"] + [(name) (string-append "Hello " name)])) +(hello3 "Jake") ; => "Hello Jake" +(hello3) ; => "Hello World" +;; ... 或者给参数指定一个可选的默认值 +(define (hello4 [name "World"]) + (string-append "Hello " name)) + +;; 函数可以将多余的参数放到一个列表里 +(define (count-args . args) + (format "You passed ~a args: ~a" (length args) args)) +(count-args 1 2 3) ; => "You passed 3 args: (1 2 3)" +;; ... 也可以使用不带语法糖的 `lambda' 形式: +(define count-args2 + (lambda args + (format "You passed ~a args: ~a" (length args) args))) + +;; 你可以混用两种用法 +(define (hello-count name . args) + (format "Hello ~a, you passed ~a extra args" name (length args))) +(hello-count "Finn" 1 2 3) +; => "Hello Finn, you passed 3 extra args" +;; ... 不带语法糖的形式: +(define hello-count2 + (lambda (name . args) + (format "Hello ~a, you passed ~a extra args" name (length args)))) + +;; 使用关键字 +(define (hello-k #:name [name "World"] #:greeting [g "Hello"] . args) + (format "~a ~a, ~a extra args" g name (length args))) +(hello-k) ; => "Hello World, 0 extra args" +(hello-k 1 2 3) ; => "Hello World, 3 extra args" +(hello-k #:greeting "Hi") ; => "Hi World, 0 extra args" +(hello-k #:name "Finn" #:greeting "Hey") ; => "Hey Finn, 0 extra args" +(hello-k 1 2 3 #:greeting "Hi" #:name "Finn" 4 5 6) + ; => "Hi Finn, 6 extra args" + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; 4. 判断是否相等 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; 判断数字使用 `=' +(= 3 3.0) ; => #t +(= 2 1) ; => #f + +;; 判断对象使用 `eq?' +(eq? 3 3) ; => #t +(eq? 3 3.0) ; => #f +(eq? (list 3) (list 3)) ; => #f + +;; 判断集合使用 `equal?' +(equal? (list 'a 'b) (list 'a 'b)) ; => #t +(equal? (list 'a 'b) (list 'b 'a)) ; => #f + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; 5. 控制结构 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;; 条件判断 + +(if #t ; 测试表达式 + "this is true" ; 为真的表达式 + "this is false") ; 为假的表达式 +; => "this is true" + +;; 注意, 除 `#f` 之外的所有值都认为是真 +(member 'Groucho '(Harpo Groucho Zeppo)) ; => '(Groucho Zeppo) +(if (member 'Groucho '(Harpo Groucho Zeppo)) + 'yep + 'nope) +; => 'yep + +;; `cond' 会进行一系列的判断来选择一个结果 +(cond [(> 2 2) (error "wrong!")] + [(< 2 2) (error "wrong again!")] + [else 'ok]) ; => 'ok + +;;; 模式匹配 + +(define (fizzbuzz? n) + (match (list (remainder n 3) (remainder n 5)) + [(list 0 0) 'fizzbuzz] + [(list 0 _) 'fizz] + [(list _ 0) 'buzz] + [_ #f])) + +(fizzbuzz? 15) ; => 'fizzbuzz +(fizzbuzz? 37) ; => #f + +;;; 循环 + +;; 循环可以使用递归(尾递归) +(define (loop i) + (when (< i 10) + (printf "i=~a\n" i) + (loop (add1 i)))) +(loop 5) ; => i=5, i=6, ... + +;; 类似的,可以使用 `let` 定义 +(let loop ((i 0)) + (when (< i 10) + (printf "i=~a\n" i) + (loop (add1 i)))) ; => i=0, i=1, ... + +;; 看上面的例子怎么增加一个新的 `loop' 形式, 但是 Racket 已经有了一个非常 +;; 灵活的 `for' 了: +(for ([i 10]) + (printf "i=~a\n" i)) ; => i=0, i=1, ... +(for ([i (in-range 5 10)]) + (printf "i=~a\n" i)) ; => i=5, i=6, ... + +;;; 其他形式的迭代 +;; `for' 允许在很多数据结构中迭代: +;; 列表, 向量, 字符串, Set, 散列表, 等... + +(for ([i (in-list '(l i s t))]) + (displayln i)) + +(for ([i (in-vector #(v e c t o r))]) + (displayln i)) + +(for ([i (in-string "string")]) + (displayln i)) + +(for ([i (in-set (set 'x 'y 'z))]) + (displayln i)) + +(for ([(k v) (in-hash (hash 'a 1 'b 2 'c 3 ))]) + (printf "key:~a value:~a\n" k v)) + +;;; 更多复杂的迭代 + +;; 并行扫描多个序列 (遇到长度小的就停止) +(for ([i 10] [j '(x y z)]) (printf "~a:~a\n" i j)) +; => 0:x 1:y 2:z + +;; 嵌套循环 +(for* ([i 2] [j '(x y z)]) (printf "~a:~a\n" i j)) +; => 0:x, 0:y, 0:z, 1:x, 1:y, 1:z + +;; 带有条件判断的 `for` +(for ([i 1000] + #:when (> i 5) + #:unless (odd? i) + #:break (> i 10)) + (printf "i=~a\n" i)) +; => i=6, i=8, i=10 + +;;; 更多的例子帮助你加深理解.. +;; 和 `for' 循环非常像 -- 收集结果 + +(for/list ([i '(1 2 3)]) + (add1 i)) ; => '(2 3 4) + +(for/list ([i '(1 2 3)] #:when (even? i)) + i) ; => '(2) + +(for/list ([i 10] [j '(x y z)]) + (list i j)) ; => '((0 x) (1 y) (2 z)) + +(for/list ([i 1000] #:when (> i 5) #:unless (odd? i) #:break (> i 10)) + i) ; => '(6 8 10) + +(for/hash ([i '(1 2 3)]) + (values i (number->string i))) +; => '#hash((1 . "1") (2 . "2") (3 . "3")) + +;; 也有很多其他的内置方法来收集循环中的值: +(for/sum ([i 10]) (* i i)) ; => 285 +(for/product ([i (in-range 1 11)]) (* i i)) ; => 13168189440000 +(for/and ([i 10] [j (in-range 10 20)]) (< i j)) ; => #t +(for/or ([i 10] [j (in-range 0 20 2)]) (= i j)) ; => #t +;; 如果需要合并计算结果, 使用 `for/fold' +(for/fold ([sum 0]) ([i '(1 2 3 4)]) (+ sum i)) ; => 10 +;; (这个函数可以在大部分情况下替代普通的命令式循环) + +;;; 异常 + +;; 要捕获一个异常,使用 `with-handlers' 形式 +(with-handlers ([exn:fail? (lambda (exn) 999)]) + (+ 1 "2")) ; => 999 +(with-handlers ([exn:break? (lambda (exn) "no time")]) + (sleep 3) + "phew") ; => "phew", 如果你打断了它,那么结果 => "no time" + +;; 使用 `raise' 抛出一个异常后者其他任何值 +(with-handlers ([number? ; 捕获抛出的数字类型的值 + identity]) ; 将它们作为普通值 + (+ 1 (raise 2))) ; => 2 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; 6. 可变的值 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; 使用 `set!' 给一个已经存在的变量赋一个新值 +(define n 5) +(set! n (add1 n)) +n ; => 6 + +;; 给那些明确地需要变化的值使用 `boxes` (在其他语言里类似指针 +;; 或者引用) +(define n* (box 5)) +(set-box! n* (add1 (unbox n*))) +(unbox n*) ; => 6 + +;; 很多 Racket 诗句类型是不可变的 (对,列表,等),有一些既是可变的 +;; 又是不可变的 (字符串,向量,散列表 +;; 等...) + +;; 使用 `vector' 或者 `make-vector' 创建一个可变的向量 +(define vec (vector 2 2 3 4)) +(define wall (make-vector 100 'bottle-of-beer)) +;; 使用 `vector-set!` 更新一项 +(vector-set! vec 0 1) +(vector-set! wall 99 'down) +vec ; => #(1 2 3 4) + +;; 创建一个空的可变散列表,然后操作它 +(define m3 (make-hash)) +(hash-set! m3 'a 1) +(hash-set! m3 'b 2) +(hash-set! m3 'c 3) +(hash-ref m3 'a) ; => 1 +(hash-ref m3 'd 0) ; => 0 +(hash-remove! m3 'a) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; 7. 模块 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; 模块让你将你的代码组织为多个文件,成为可重用的模块, +;; 在这里,我们使用嵌套在本文的整个大模块 +;; 里的子模块(从 "#lang" 这一行开始) + +(module cake racket/base ; 基于 racket/base 定义一个 `cake` 模块 + + (provide print-cake) ; 这个模块导出的函数 + + (define (print-cake n) + (show " ~a " n #\.) + (show " .-~a-. " n #\|) + (show " | ~a | " n #\space) + (show "---~a---" n #\-)) + + (define (show fmt n ch) ; 内部函数 + (printf fmt (make-string n ch)) + (newline))) + +;; 使用 `require` 从模块中得到所有 `provide` 的函数 +(require 'cake) ; 这里的 `'`表示是本地的子模块 +(print-cake 3) +; (show "~a" 1 #\A) ; => 报错, `show' 没有被导出,不存在 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; 8. 类和对象 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; 创建一个 fish% 类(%是给类绑定用的) +(define fish% + (class object% + (init size) ; 初始化的参数 + (super-new) ; 父类的初始化 + ;; 域 + (define current-size size) + ;; 公共方法 + (define/public (get-size) + current-size) + (define/public (grow amt) + (set! current-size (+ amt current-size))) + (define/public (eat other-fish) + (grow (send other-fish get-size))))) + +;; 创建一个 fish% 类的示例 +(define charlie + (new fish% [size 10])) + +;; 使用 `send' 调用一个对象的方法 +(send charlie get-size) ; => 10 +(send charlie grow 6) +(send charlie get-size) ; => 16 + +;; `fish%' 是一个普通的值,我们可以用它来混入 +(define (add-color c%) + (class c% + (init color) + (super-new) + (define my-color color) + (define/public (get-color) my-color))) +(define colored-fish% (add-color fish%)) +(define charlie2 (new colored-fish% [size 10] [color 'red])) +(send charlie2 get-color) +;; 或者,不带名字 +(send (new (add-color fish%) [size 10] [color 'red]) get-color) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; 9. 宏 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; 宏让你扩展这门语言的语法 + +;; 让我们定义一个while循环 +(define-syntax-rule (while condition body ...) + (let loop () + (when condition + body ... + (loop)))) + +(let ([i 0]) + (while (< i 10) + (displayln i) + (set! i (add1 i)))) + +;; 宏是安全的,你不能修改现有的变量 +(define-syntax-rule (swap! x y) ; !表示会修改 + (let ([tmp x]) + (set! x y) + (set! y tmp))) + +(define tmp 2) +(define other 3) +(swap! tmp other) +(printf "tmp = ~a; other = ~a\n" tmp other) +;; 变量 `tmp` 被重命名为 `tmp_1` +;; 避免名字冲突 +;; (let ([tmp_1 tmp]) +;; (set! tmp other) +;; (set! other tmp_1)) + +;; 但它们仍然会导致错误代码,比如: +(define-syntax-rule (bad-while condition body ...) + (when condition + body ... + (bad-while condition body ...))) +;; 这个宏会挂掉,它产生了一个无限循环,如果你试图去使用它 +;; 编译器会进入死循环 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; 10. 契约 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; 契约限制变量从模块中导入 + +(module bank-account racket + (provide (contract-out + [deposit (-> positive? any)] ; 数量一直是正值 + [balance (-> positive?)])) + + (define amount 0) + (define (deposit a) (set! amount (+ amount a))) + (define (balance) amount) + ) + +(require 'bank-account) +(deposit 5) + +(balance) ; => 5 + +;; 客户端尝试存储一个负值时会出错 +;; (deposit -5) ; => deposit: contract violation +;; expected: positive? +;; given: -5 +;; more details.... +``` + +## 进一步阅读 + +想知道更多吗? 尝试 [Getting Started with Racket](http://docs.racket-lang.org/getting-started/) diff --git a/zh-cn/ruby-cn.html.markdown b/zh-cn/ruby-cn.html.markdown index 6530b520..619e6e92 100644 --- a/zh-cn/ruby-cn.html.markdown +++ b/zh-cn/ruby-cn.html.markdown @@ -94,7 +94,7 @@ x = y = 10 #=> 10 x #=> 10 y #=> 10 -# 按照惯例,用snake_case 作为变量名 +# 按照惯例,用 snake_case 作为变量名 snake_case = true # 使用具有描述性的运算符 @@ -102,7 +102,8 @@ path_to_project_root = '/good/name/' path = '/bad/name/' # 符号(Symbols,也是对象) -# 符号是不可变的,内部用整数类型表示的可重用的值。通常用它代替字符串来有效地表达有意义的值 +# 符号是不可变的,内部用整数类型表示的可重用的值。 +# 通常用它代替字符串来有效地表示有意义的值。 :pending.class #=> Symbol |