summaryrefslogtreecommitdiffhomepage
path: root/zh-cn
diff options
context:
space:
mode:
Diffstat (limited to 'zh-cn')
-rw-r--r--zh-cn/haskell-cn.html.markdown263
-rw-r--r--zh-cn/scala-cn.html.markdown558
2 files changed, 509 insertions, 312 deletions
diff --git a/zh-cn/haskell-cn.html.markdown b/zh-cn/haskell-cn.html.markdown
index cb7ccdee..8904970f 100644
--- a/zh-cn/haskell-cn.html.markdown
+++ b/zh-cn/haskell-cn.html.markdown
@@ -5,24 +5,25 @@ contributors:
- ["Adit Bhargava", "http://adit.io"]
translators:
- ["Peiyong Lin", ""]
+ - ["chad luo", "http://yuki.rocks"]
lang: zh-cn
---
-Haskell 被设计成一种实用的纯函数式编程语言。它因为 monads 及其类型系统而出名,但是我回归到它本身因为。Haskell 使得编程对于我而言是一种真正的快乐。
+Haskell 是一门实用的函数式编程语言,因其 Monads 与类型系统而闻名。而我使用它则是因为它异常优雅。用 Haskell 编程令我感到非常快乐。
```haskell
--- 单行注释以两个破折号开头
+-- 单行注释以两个减号开头
{- 多行注释像这样
- 被一个闭合的块包围
+ 被一个闭合的块包围
-}
----------------------------------------------------
-- 1. 简单的数据类型和操作符
----------------------------------------------------
--- 你有数字
+-- 数字
3 -- 3
--- 数学计算就像你所期待的那样
+-- 数学计算
1 + 1 -- 2
8 - 1 -- 7
10 * 2 -- 20
@@ -34,7 +35,7 @@ Haskell 被设计成一种实用的纯函数式编程语言。它因为 monads
-- 整除
35 `div` 4 -- 8
--- 布尔值也简单
+-- 布尔值
True
False
@@ -45,21 +46,22 @@ not False -- True
1 /= 1 -- False
1 < 10 -- True
--- 在上述的例子中,`not` 是一个接受一个值的函数。
--- Haskell 不需要括号来调用函数。。。所有的参数
--- 都只是在函数名之后列出来。因此,通常的函数调用模式是:
--- func arg1 arg2 arg3...
--- 查看关于函数的章节以获得如何写你自己的函数的相关信息。
+-- 在上面的例子中,`not` 是一个接受一个参数的函数。
+-- Haskell 不需要括号来调用函数,所有的参数都只是在函数名之后列出来
+-- 因此,通常的函数调用模式是:
+-- func arg1 arg2 arg3...
+-- 你可以查看函数部分了解如何自行编写。
-- 字符串和字符
-"This is a string."
+"This is a string." -- 字符串
'a' -- 字符
'对于字符串你不能使用单引号。' -- 错误!
--- 连结字符串
+-- 连接字符串
"Hello " ++ "world!" -- "Hello world!"
-- 一个字符串是一系列字符
+['H', 'e', 'l', 'l', 'o'] -- "Hello"
"This is a string" !! 0 -- 'T'
@@ -67,162 +69,164 @@ not False -- True
-- 列表和元组
----------------------------------------------------
--- 一个列表中的每一个元素都必须是相同的类型
--- 下面两个列表一样
+-- 一个列表中的每一个元素都必须是相同的类型。
+-- 下面两个列表等价
[1, 2, 3, 4, 5]
[1..5]
--- 在 Haskell 你可以拥有含有无限元素的列表
-[1..] -- 一个含有所有自然数的列表
+-- 区间也可以这样
+['A'..'F'] -- "ABCDEF"
--- 因为 Haskell 有“懒惰计算”,所以无限元素的列表可以正常运作。这意味着
--- Haskell 可以只在它需要的时候计算。所以你可以请求
--- 列表中的第1000个元素,Haskell 会返回给你
+-- 你可以在区间中指定步进
+[0,2..10] -- [0, 2, 4, 6, 8, 10]
+[5..1] -- 这样不行,因为 Haskell 默认递增
+[5,4..1] -- [5, 4, 3, 2, 1]
-[1..] !! 999 -- 1000
+-- 列表下标
+[0..] !! 5 -- 5
--- Haskell 计算了列表中 1 - 1000 个元素。。。但是
--- 这个无限元素的列表中剩下的元素还不存在! Haskell 不会
--- 真正地计算它们知道它需要。
+-- 在 Haskell 你可以使用无限列表
+[1..] -- 一个含有所有自然数的列表
-<FS>- 连接两个列表
+-- 无限列表的原理是,Haskell 有“惰性求值”。
+-- 这意味着 Haskell 只在需要时才会计算。
+-- 所以当你获取列表的第 1000 项元素时,Haskell 会返回给你:
+[1..] !! 999 -- 1000
+-- Haskell 计算了列表中第 1 至 1000 项元素,但这个无限列表中剩下的元素还不存在。
+-- Haskell 只有在需要时才会计算它们。
+
+-- 连接两个列表
[1..5] ++ [6..10]
-- 往列表头增加元素
0:[1..5] -- [0, 1, 2, 3, 4, 5]
--- 列表中的下标
-[0..] !! 5 -- 5
-
--- 更多列表操作
+-- 其它列表操作
head [1..5] -- 1
tail [1..5] -- [2, 3, 4, 5]
init [1..5] -- [1, 2, 3, 4]
last [1..5] -- 5
--- 列表推导
+-- 列表推导 (list comprehension)
[x*2 | x <- [1..5]] -- [2, 4, 6, 8, 10]
-- 附带条件
[x*2 | x <-[1..5], x*2 > 4] -- [6, 8, 10]
--- 元组中的每一个元素可以是不同类型的,但是一个元组
--- 的长度是固定的
+-- 元组中的每一个元素可以是不同类型,但是一个元组的长度是固定的
-- 一个元组
("haskell", 1)
--- 获取元组中的元素
+-- 获取元组中的元素(例如,一个含有 2 个元素的元祖)
fst ("haskell", 1) -- "haskell"
snd ("haskell", 1) -- 1
----------------------------------------------------
-- 3. 函数
----------------------------------------------------
+
-- 一个接受两个变量的简单函数
add a b = a + b
--- 注意,如果你使用 ghci (Hakell 解释器)
--- 你将需要使用 `let`,也就是
+-- 注意,如果你使用 ghci (Hakell 解释器),你需要使用 `let`,也就是
-- let add a b = a + b
--- 使用函数
+-- 调用函数
add 1 2 -- 3
--- 你也可以把函数放置在两个参数之间
--- 附带倒引号:
+-- 你也可以使用反引号中置函数名:
1 `add` 2 -- 3
--- 你也可以定义不带字符的函数!这使得
--- 你定义自己的操作符!这里有一个操作符
--- 来做整除
+-- 你也可以定义不带字母的函数名,这样你可以定义自己的操作符。
+-- 这里有一个做整除的操作符
(//) a b = a `div` b
35 // 4 -- 8
--- 守卫:一个简单的方法在函数里做分支
+-- Guard:一个在函数中做条件判断的简单方法
fib x
| x < 2 = x
| otherwise = fib (x - 1) + fib (x - 2)
--- 模式匹配是类型的。这里有三种不同的 fib
--- 定义。Haskell 将自动调用第一个
--- 匹配值的模式的函数。
+-- 模式匹配与 Guard 类似。
+-- 这里给出了三个不同的 fib 定义。
+-- Haskell 会自动调用第一个符合参数模式的声明
fib 1 = 1
fib 2 = 2
fib x = fib (x - 1) + fib (x - 2)
--- 元组的模式匹配:
+-- 元组的模式匹配
foo (x, y) = (x + 1, y + 2)
--- 列表的模式匹配。这里 `x` 是列表中第一个元素,
--- 并且 `xs` 是列表剩余的部分。我们可以写
--- 自己的 map 函数:
+-- 列表的模式匹配
+-- 这里 `x` 是列表中第一个元素,`xs` 是列表剩余的部分。
+-- 我们可以实现自己的 map 函数:
myMap func [] = []
myMap func (x:xs) = func x:(myMap func xs)
--- 编写出来的匿名函数带有一个反斜杠,后面跟着
--- 所有的参数。
+-- 匿名函数带有一个反斜杠,后面跟着所有的参数
myMap (\x -> x + 2) [1..5] -- [3, 4, 5, 6, 7]
--- 使用 fold (在一些语言称为`inject`)随着一个匿名的
--- 函数。foldl1 意味着左折叠(fold left), 并且使用列表中第一个值
--- 作为累加器的初始化值。
+-- 在 fold(在一些语言称 为`inject`)中使用匿名函数
+-- foldl1 意味着左折叠 (fold left), 并且使用列表中第一个值作为累加器的初始值。
foldl1 (\acc x -> acc + x) [1..5] -- 15
----------------------------------------------------
--- 4. 更多的函数
+-- 4. 其它函数
----------------------------------------------------
--- 柯里化(currying):如果你不传递函数中所有的参数,
--- 它就变成“柯里化的”。这意味着,它返回一个接受剩余参数的函数。
-
+-- 部分调用
+-- 如果你调用函数时没有给出所有参数,它就被“部分调用”。
+-- 它将返回一个接受余下参数的函数。
add a b = a + b
foo = add 10 -- foo 现在是一个接受一个数并对其加 10 的函数
foo 5 -- 15
--- 另外一种方式去做同样的事
+-- 另一种等价写法
foo = (+10)
foo 5 -- 15
--- 函数组合
--- (.) 函数把其它函数链接到一起
--- 举个列子,这里 foo 是一个接受一个值的函数。它对接受的值加 10,
--- 并对结果乘以 5,之后返回最后的值。
+-- 函列表合
+-- (.) 函数把其它函数链接到一起。
+-- 例如,这里 foo 是一个接受一个值的函数。
+-- 它对接受的值加 10,并对结果乘以 5,之后返回最后的值。
foo = (*5) . (+10)
-- (5 + 10) * 5 = 75
foo 5 -- 75
--- 修复优先级
--- Haskell 有另外一个函数称为 `$`。它改变优先级
--- 使得其左侧的每一个操作先计算然后应用到
--- 右侧的每一个操作。你可以使用 `.` 和 `$` 来除去很多
--- 括号:
+-- 修正优先级
+-- Haskell 有另外一个函数 `$` 可以改变优先级。
+-- `$` 使得 Haskell 先计算其右边的部分,然后调用左边的部分。
+-- 你可以使用 `$` 来移除多余的括号。
--- before
+-- 修改前
(even (fib 7)) -- true
--- after
+-- 修改后
even . fib $ 7 -- true
+-- 等价地
+even $ fib 7 -- true
+
----------------------------------------------------
--- 5. 类型签名
+-- 5. 类型声明
----------------------------------------------------
--- Haskell 有一个非常强壮的类型系统,一切都有一个类型签名。
+-- Haskell 有一个非常强大的类型系统,一切都有一个类型声明。
-- 一些基本的类型:
5 :: Integer
"hello" :: String
True :: Bool
--- 函数也有类型。
--- `not` 接受一个布尔型返回一个布尔型:
+-- 函数也有类型
+-- `not` 接受一个布尔型返回一个布尔型
-- not :: Bool -> Bool
--- 这是接受两个参数的函数:
+-- 这是接受两个参数的函数
-- add :: Integer -> Integer -> Integer
--- 当你定义一个值,在其上写明它的类型是一个好实践:
+-- 当你定义一个值,声明其类型是一个好做法
double :: Integer -> Integer
double x = x * 2
@@ -230,159 +234,148 @@ double x = x * 2
-- 6. 控制流和 If 语句
----------------------------------------------------
--- if 语句
+-- if 语句:
haskell = if 1 == 1 then "awesome" else "awful" -- haskell = "awesome"
--- if 语句也可以有多行,缩进是很重要的
+-- if 语句也可以有多行,注意缩进:
haskell = if 1 == 1
then "awesome"
else "awful"
--- case 语句:这里是你可以怎样去解析命令行参数
+-- case 语句
+-- 解析命令行参数:
case args of
"help" -> printHelp
"start" -> startProgram
_ -> putStrLn "bad args"
--- Haskell 没有循环因为它使用递归取代之。
--- map 应用一个函数到一个数组中的每一个元素
-
+-- Haskell 没有循环,它使用递归
+-- map 对一个列表中的每一个元素调用一个函数
map (*2) [1..5] -- [2, 4, 6, 8, 10]
-- 你可以使用 map 来编写 for 函数
for array func = map func array
--- 然后使用它
+-- 调用
for [0..5] $ \i -> show i
--- 我们也可以像这样写:
+-- 我们也可以像这样写
for [0..5] show
-- 你可以使用 foldl 或者 foldr 来分解列表
-- foldl <fn> <initial value> <list>
foldl (\x y -> 2*x + y) 4 [1,2,3] -- 43
--- 这和下面是一样的
+-- 等价于
(2 * (2 * (2 * 4 + 1) + 2) + 3)
--- foldl 是左手边的,foldr 是右手边的-
+-- foldl 从左开始,foldr 从右
foldr (\x y -> 2*x + y) 4 [1,2,3] -- 16
--- 这和下面是一样的
+-- 现在它等价于
(2 * 3 + (2 * 2 + (2 * 1 + 4)))
----------------------------------------------------
-- 7. 数据类型
----------------------------------------------------
--- 这里展示在 Haskell 中你怎样编写自己的数据类型
-
+-- 在 Haskell 中声明你自己的数据类型:
data Color = Red | Blue | Green
-- 现在你可以在函数中使用它:
-
-
say :: Color -> String
say Red = "You are Red!"
say Blue = "You are Blue!"
say Green = "You are Green!"
-- 你的数据类型也可以有参数:
-
data Maybe a = Nothing | Just a
--- 类型 Maybe 的所有
-Just "hello" -- of type `Maybe String`
-Just 1 -- of type `Maybe Int`
-Nothing -- of type `Maybe a` for any `a`
+-- 这些都是 Maybe 类型:
+Just "hello" -- `Maybe String` 类型
+Just 1 -- `Maybe Int` 类型
+Nothing -- 对任意 `a` 为 `Maybe a` 类型
----------------------------------------------------
-- 8. Haskell IO
----------------------------------------------------
--- 虽然在没有解释 monads 的情况下 IO不能被完全地解释,
--- 着手解释到位并不难。
-
--- 当一个 Haskell 程序被执行,函数 `main` 就被调用。
--- 它必须返回一个类型 `IO ()` 的值。举个列子:
+-- 虽然不解释 Monads 就无法完全解释 IO,但大致了解并不难。
+-- 当执行一个 Haskell 程序时,函数 `main` 就被调用。
+-- 它必须返回一个类型 `IO ()` 的值。例如:
main :: IO ()
main = putStrLn $ "Hello, sky! " ++ (say Blue)
--- putStrLn has type String -> IO ()
+-- putStrLn 的类型是 String -> IO ()
--- 如果你能实现你的程序依照函数从 String 到 String,那样编写 IO 是最简单的。
+-- 如果你的程序输入 String 返回 String,那样编写 IO 是最简单的。
-- 函数
-- interact :: (String -> String) -> IO ()
--- 输入一些文本,在其上运行一个函数,并打印出输出
+-- 输入一些文本,对其调用一个函数,并打印输出。
countLines :: String -> String
countLines = show . length . lines
main' = interact countLines
--- 你可以考虑一个 `IO()` 类型的值,当做一系列计算机所完成的动作的代表,
--- 就像一个以命令式语言编写的计算机程序。我们可以使用 `do` 符号来把动作链接到一起。
--- 举个列子:
-
+-- 你可以认为一个 `IO ()` 类型的值是表示计算机做的一系列操作,类似命令式语言。
+-- 我们可以使用 `do` 声明来把动作连接到一起。
+-- 举个列子
sayHello :: IO ()
sayHello = do
putStrLn "What is your name?"
- name <- getLine -- this gets a line and gives it the name "input"
+ name <- getLine -- 这里接受一行输入并绑定至 "name"
putStrLn $ "Hello, " ++ name
-- 练习:编写只读取一行输入的 `interact`
-- 然而,`sayHello` 中的代码将不会被执行。唯一被执行的动作是 `main` 的值。
--- 为了运行 `sayHello`,注释上面 `main` 的定义,并代替它:
+-- 为了运行 `sayHello`,注释上面 `main` 的定义,替换为:
-- main = sayHello
--- 让我们来更好地理解刚才所使用的函数 `getLine` 是怎样工作的。它的类型是:
+-- 让我们来更进一步理解刚才所使用的函数 `getLine` 是怎样工作的。它的类型是:
-- getLine :: IO String
--- 你可以考虑一个 `IO a` 类型的值,代表一个当被执行的时候
--- 将产生一个 `a` 类型的值的计算机程序(除了它所做的任何事之外)。我们可以保存和重用这个值通过 `<-`。
--- 我们也可以写自己的 `IO String` 类型的动作:
-
+-- 你可以认为一个 `IO a` 类型的值代表了一个运行时会生成一个 `a` 类型值的程序。
+-- (可能伴随其它行为)
+-- 我们可以通过 `<-` 保存和重用这个值。
+-- 我们也可以实现自己的 `IO String` 类型函数:
action :: IO String
action = do
putStrLn "This is a line. Duh"
input1 <- getLine
input2 <- getLine
- -- The type of the `do` statement is that of its last line.
- -- `return` is not a keyword, but merely a function
+ -- `do` 语句的类型是它的最后一行
+ -- `return` 不是关键字,只是一个普通函数
return (input1 ++ "\n" ++ input2) -- return :: String -> IO String
--- 我们可以使用这个动作就像我们使用 `getLine`:
-
+-- 我们可以像调用 `getLine` 一样调用它
main'' = do
putStrLn "I will echo two lines!"
result <- action
putStrLn result
putStrLn "This was all, folks!"
--- `IO` 类型是一个 "monad" 的例子。Haskell 使用一个 monad 来做 IO的方式允许它是一门纯函数式语言。
--- 任何与外界交互的函数(也就是 IO) 都在它的类型签名处做一个 `IO` 标志
--- 着让我们推出 什么样的函数是“纯洁的”(不与外界交互,不修改状态) 和 什么样的函数不是 “纯洁的”
-
--- 这是一个强有力的特征,因为并发地运行纯函数是简单的;因此,Haskell 中并发是非常简单的。
-
+-- `IO` 类型是一个 "Monad" 的例子。
+-- Haskell 通过使用 Monad 使得其本身为纯函数式语言。
+-- 任何与外界交互的函数(即 IO)都在它的类型声明中标记为 `IO`。
+-- 这告诉我们什么样的函数是“纯洁的”(不与外界交互,不修改状态) ,
+-- 什么样的函数不是 “纯洁的”。
+-- 这个功能非常强大,因为纯函数并发非常容易,由此在 Haskell 中做并发非常容易。
----------------------------------------------------
--- 9. The Haskell REPL
+-- 9. Haskell REPL
----------------------------------------------------
--- 键入 `ghci` 开始 repl。
+-- 键入 `ghci` 开始 REPL。
-- 现在你可以键入 Haskell 代码。
--- 任何新值都需要通过 `let` 来创建:
-
+-- 任何新值都需要通过 `let` 来创建
let foo = 5
--- 你可以查看任何值的类型,通过命令 `:t`:
-
+-- 你可以通过命令 `:t` 查看任何值的类型
>:t foo
foo :: Integer
-- 你也可以运行任何 `IO ()`类型的动作
-
> sayHello
What is your name?
Friend!
@@ -390,7 +383,7 @@ Hello, Friend!
```
-还有很多关于 Haskell,包括类型类和 monads。这些是使得编码 Haskell 是如此有趣的主意。我用一个最后的 Haskell 例子来结束:一个 Haskell 的快排实现:
+Haskell 还有许多内容,包括类型类 (typeclasses) 与 Monads。这些都是令 Haskell 编程非常有趣的好东西。我们最后给出 Haskell 的一个例子,一个快速排序的实现:
```haskell
qsort [] = []
@@ -399,9 +392,9 @@ qsort (p:xs) = qsort lesser ++ [p] ++ qsort greater
greater = filter (>= p) xs
```
-安装 Haskell 是简单的。你可以从[这里](http://www.haskell.org/platform/)获得它。
+安装 Haskell 很简单。你可以[从这里获得](http://www.haskell.org/platform/)。
你可以从优秀的
[Learn you a Haskell](http://learnyouahaskell.com/) 或者
[Real World Haskell](http://book.realworldhaskell.org/)
-找到优雅不少的入门介绍。
+找到更平缓的入门介绍。
diff --git a/zh-cn/scala-cn.html.markdown b/zh-cn/scala-cn.html.markdown
index 58f5cd47..508dd58e 100644
--- a/zh-cn/scala-cn.html.markdown
+++ b/zh-cn/scala-cn.html.markdown
@@ -4,12 +4,15 @@ filename: learnscala-zh.scala
contributors:
- ["George Petrov", "http://github.com/petrovg"]
- ["Dominic Bou-Samra", "http://dbousamra.github.com"]
+ - ["Geoff Liu", "http://geoffliu.me"]
translators:
- ["Peiyong Lin", ""]
+ - ["Jinchang Ye", "http://github.com/alwayswithme"]
+ - ["Guodong Qu", "https://github.com/jasonqu"]
lang: zh-cn
---
-Scala - 一门可拓展性的语言
+Scala - 一门可拓展的语言
```scala
@@ -17,23 +20,31 @@ Scala - 一门可拓展性的语言
自行设置:
1) 下载 Scala - http://www.scala-lang.org/downloads
- 2) unzip/untar 到你喜欢的地方,放在路径中的 bin 目录下
- 3) 在终端输入 scala,开启 Scala 的 REPL,你会看到提示符:
+ 2) unzip/untar 到您喜欢的地方,并把 bin 子目录添加到 path 环境变量
+ 3) 在终端输入 scala,启动 Scala 的 REPL,您会看到提示符:
scala>
- 这就是所谓的 REPL,你现在可以在其中运行命令,让我们做到这一点:
+ 这就是所谓的 REPL (读取-求值-输出循环,英语: Read-Eval-Print Loop),
+ 您可以在其中输入合法的表达式,结果会被打印。
+ 在教程中我们会进一步解释 Scala 文件是怎样的,但现在先了解一点基础。
*/
-println(10) // 打印整数 10
-println("Boo!") // 打印字符串 "BOO!"
+/////////////////////////////////////////////////
+// 1. 基础
+/////////////////////////////////////////////////
+// 单行注释开始于两个斜杠
-// 一些基础
+/*
+ 多行注释,如您之前所见,看起来像这样
+*/
// 打印并强制换行
println("Hello world!")
+println(10)
+
// 没有强制换行的打印
print("Hello world")
@@ -41,13 +52,19 @@ print("Hello world")
// val 声明是不可变的,var 声明是可修改的。不可变性是好事。
val x = 10 // x 现在是 10
x = 20 // 错误: 对 val 声明的变量重新赋值
-var x = 10
-x = 20 // x 现在是 20
+var y = 10
+y = 20 // y 现在是 20
-// 单行注释开始于两个斜杠
-/*
-多行注释看起来像这样。
+/*
+ Scala 是静态语言,但注意上面的声明方式,我们没有指定类型。
+ 这是因为类型推导的语言特性。大多数情况, Scala 编译器可以推测变量的类型,
+ 所以您不需要每次都输入。可以像这样明确声明变量类型:
*/
+val z: Int = 10
+val a: Double = 1.0
+
+// 注意从 Int 到 Double 的自动转型,结果是 10.0, 不是 10
+val b: Double = 10
// 布尔值
true
@@ -64,9 +81,11 @@ true == false // false
2 - 1 // 1
5 * 3 // 15
6 / 2 // 3
+6 / 4 // 1
+6.0 / 4 // 1.5
-// 在 REPL 计算一个命令会返回给你结果的类型和值
+// 在 REPL 计算一个表达式会返回给您结果的类型和值
1 + 7
@@ -77,58 +96,190 @@ true == false // false
这意味着计算 1 + 7 的结果是一个 Int 类型的对象,其值为 8
- 1+7 的结果是一样的
+ 注意 "res29" 是一个连续生成的变量名,用以存储您输入的表达式结果,
+ 您看到的输出可能不一样。
*/
+"Scala strings are surrounded by double quotes"
+'a' // Scala 的字符
+// '不存在单引号字符串' <= 这会导致错误
-// 包括函数在内,每一个事物都是对象。在 REPL 中输入:
+// String 有常见的 Java 字符串方法
+"hello world".length
+"hello world".substring(2, 6)
+"hello world".replace("C", "3")
-7 // 结果 res30: Int = 7 (res30 是一个生成的结果的 var 命名)
+// 也有一些额外的 Scala 方法,另请参见:scala.collection.immutable.StringOps
+"hello world".take(5)
+"hello world".drop(5)
-// 下一行给你一个接收一个 Int 类型并返回该数的平方的函数
-(x:Int) => x * x
+// 字符串改写:留意前缀 "s"
+val n = 45
+s"We have $n apples" // => "We have 45 apples"
-// 你可以分配给函数一个标识符,像这样:
-val sq = (x:Int) => x * x
+// 在要改写的字符串中使用表达式也是可以的
+val a = Array(11, 9, 6)
+s"My second daughter is ${a(0) - a(2)} years old." // => "My second daughter is 5 years old."
+s"We have double the amount of ${n / 2.0} in apples." // => "We have double the amount of 22.5 in apples."
+s"Power of 2: ${math.pow(2, 2)}" // => "Power of 2: 4"
-/* 上面的例子说明
-
- sq: Int => Int = <function1>
+// 添加 "f" 前缀对要改写的字符串进行格式化
+f"Power of 5: ${math.pow(5, 2)}%1.0f" // "Power of 5: 25"
+f"Square root of 122: ${math.sqrt(122)}%1.4f" // "Square root of 122: 11.0454"
- 意味着这次我们给予了 sq 这样一个显式的名字给一个接受一个 Int 类型值并返回 一个 Int 类型值的函数
+// 未处理的字符串,忽略特殊字符。
+raw"New line feed: \n. Carriage return: \r." // => "New line feed: \n. Carriage return: \r."
- sq 可以像下面那样被执行:
-*/
+// 一些字符需要转义,比如字符串中的双引号
+"They stood outside the \"Rose and Crown\"" // => "They stood outside the "Rose and Crown""
-sq(10) // 返回给你:res33: Int = 100.
+// 三个双引号可以使字符串跨越多行,并包含引号
+val html = """<form id="daform">
+ <p>Press belo', Joe</p>
+ <input type="submit">
+ </form>"""
-// Scala 允许方法和函数返回或者接受其它的函数或者方法作为参数。
-val add10: Int => Int = _ + 10 // 一个接受一个 Int 类型参数并返回一个 Int 类型值的函数
-List(1, 2, 3) map add10 // List(11, 12, 13) - add10 被应用到每一个元素
+/////////////////////////////////////////////////
+// 2. 函数
+/////////////////////////////////////////////////
+
+// 函数可以这样定义:
+//
+// def functionName(args...): ReturnType = { body... }
+//
+// 如果您以前学习过传统的编程语言,注意 return 关键字的省略。
+// 在 Scala 中, 函数代码块最后一条表达式就是返回值。
+def sumOfSquares(x: Int, y: Int): Int = {
+ val x2 = x * x
+ val y2 = y * y
+ x2 + y2
+}
-// 匿名函数可以被使用来代替有命名的函数:
-List(1, 2, 3) map (x => x + 10)
+// 如果函数体是单行表达式,{ } 可以省略:
+def sumOfSquaresShort(x: Int, y: Int): Int = x * x + y * y
-// 下划线标志,如果匿名函数只有一个参数可以被使用来表示该参数变量
-List(1, 2, 3) map (_ + 10)
+// 函数调用的语法是熟知的:
+sumOfSquares(3, 4) // => 25
-// 如果你所应用的匿名块和匿名函数都接受一个参数,那么你甚至可以省略下划线
-List("Dom", "Bob", "Natalia") foreach println
+// 在多数情况下 (递归函数是需要注意的例外), 函数返回值可以省略,
+// 变量所用的类型推导一样会应用到函数返回值中:
+def sq(x: Int) = x * x // 编译器会推断得知返回值是 Int
+// 函数可以有默认参数
+def addWithDefault(x: Int, y: Int = 5) = x + y
+addWithDefault(1, 2) // => 3
+addWithDefault(1) // => 6
-// 数据结构
+// 匿名函数是这样的:
+(x:Int) => x * x
+
+// 和 def 不同,如果语义清晰,匿名函数的参数类型也可以省略。
+// 类型 "Int => Int" 意味着这个函数接收一个 Int 并返回一个 Int。
+val sq: Int => Int = x => x * x
+
+// 匿名函数的调用也是类似的:
+sq(10) // => 100
+
+// 如果您的匿名函数中每个参数仅使用一次,
+// Scala 提供一个更简洁的方式来定义他们。这样的匿名函数极为常见,
+// 在数据结构部分会明显可见。
+val addOne: Int => Int = _ + 1
+val weirdSum: (Int, Int) => Int = (_ * 2 + _ * 3)
+
+addOne(5) // => 6
+weirdSum(2, 4) // => 16
+
+
+// return 关键字是存在的,但它只从最里面包裹了 return 的 def 函数中返回。
+// 警告: 在 Scala 中使用 return 容易出错,应该避免使用。
+// 在匿名函数中没有效果,例如:
+def foo(x: Int): Int = {
+ val anonFunc: Int => Int = { z =>
+ if (z > 5)
+ return z // 这一行令 z 成为 foo 函数的返回值!
+ else
+ z + 2 // 这一行是 anonFunc 函数的返回值
+ }
+ anonFunc(x) // 这一行是 foo 函数的返回值
+}
+
+/*
+ * 译者注:此处是指匿名函数中的 return z 成为最后执行的语句,
+ * 在 anonFunc(x) 下面的表达式(假设存在)不再执行。如果 anonFunc
+ * 是用 def 定义的函数, return z 仅返回到 anonFunc(x) ,
+ * 在 anonFunc(x) 下面的表达式(假设存在)会继续执行。
+ */
+
+
+/////////////////////////////////////////////////
+// 3. 控制语句
+/////////////////////////////////////////////////
+
+1 to 5
+val r = 1 to 5
+r.foreach( println )
+
+r foreach println
+// 附注: Scala 对点和括号的要求想当宽松,注意其规则是不同的。
+// 这有助于写出读起来像英语的 DSL(领域特定语言) 和 API(应用编程接口)。
+
+(5 to 1 by -1) foreach ( println )
+
+// while 循环
+var i = 0
+while (i < 10) { println("i " + i); i+=1 }
+
+while (i < 10) { println("i " + i); i+=1 } // 没错,再执行一次,发生了什么?为什么?
+
+i // 显示 i 的值。注意 while 是经典的循环方式,它连续执行并改变循环中的变量。
+ // while 执行很快,比 Java 的循环快,但像上面所看到的那样用组合子和推导式
+ // 更易于理解和并行化。
+
+// do while 循环
+do {
+ println("x is still less than 10");
+ x += 1
+} while (x < 10)
+
+// Scala 中尾递归是一种符合语言习惯的递归方式。
+// 递归函数需要清晰的返回类型,编译器不能推断得知。
+// 这是一个 Unit。
+def showNumbersInRange(a:Int, b:Int):Unit = {
+ print(a)
+ if (a < b)
+ showNumbersInRange(a + 1, b)
+}
+showNumbersInRange(1,14)
+
+
+// 条件语句
+
+val x = 10
+
+if (x == 1) println("yeah")
+if (x == 10) println("yeah")
+if (x == 11) println("yeah")
+if (x == 11) println ("yeah") else println("nay")
+
+println(if (x == 10) "yeah" else "nope")
+val text = if (x == 10) "yeah" else "nope"
+
+
+/////////////////////////////////////////////////
+// 4. 数据结构
+/////////////////////////////////////////////////
val a = Array(1, 2, 3, 5, 8, 13)
a(0)
a(3)
-a(21) // 这会抛出一个异常
+a(21) // 抛出异常
val m = Map("fork" -> "tenedor", "spoon" -> "cuchara", "knife" -> "cuchillo")
m("fork")
m("spoon")
-m("bottle") // 这会抛出一个异常
+m("bottle") // 抛出异常
val safeM = m.withDefaultValue("no lo se")
safeM("bottle")
@@ -137,9 +288,9 @@ val s = Set(1, 3, 7)
s(0)
s(1)
-/* 查看 map 的文档
- * 点击[这里](http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.Map)
- * 确保你可以读它
+/* 这里查看 map 的文档 -
+ * http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.Map
+ * 并确保你会阅读
*/
@@ -154,13 +305,11 @@ s(1)
(a, 2, "three")
// 为什么有这个?
-
val divideInts = (x:Int, y:Int) => (x / y, x % y)
-divideInts(10,3) // 函数 divideInts 返回你结果和余数
+divideInts(10,3) // 函数 divideInts 同时返回结果和余数
// 要读取元组的元素,使用 _._n,n是从1开始的元素索引
-
val d = divideInts(10,3)
d._1
@@ -168,234 +317,289 @@ d._1
d._2
+/////////////////////////////////////////////////
+// 5. 面向对象编程
+/////////////////////////////////////////////////
-// 选择器
-
-s.map(sq)
-
-val sSquared = s. map(sq)
-
-sSquared.filter(_ < 10)
+/*
+ 旁白: 教程中到现在为止我们所做的一切只是简单的表达式(值,函数等)。
+ 这些表达式可以输入到命令行解释器中作为快速测试,但它们不能独立存在于 Scala
+ 文件。举个例子,您不能在 Scala 文件上简单的写上 "val x = 5"。相反 Scala 文件
+ 允许的顶级结构是:
-sSquared.reduce (_+_)
+ - objects
+ - classes
+ - case classes
+ - traits
-// filter 函数接受一个预测(一个函数,形式为 A -> Boolean) 并选择出所有的元素满足这个预测
+ 现在来解释这些是什么。
+*/
-List(1, 2, 3) filter (_ > 2) // List(3)
-List(
- Person(name = "Dom", age = 23),
- Person(name = "Bob", age = 30)
-).filter(_.age > 25) // List(Person("Bob", 30))
+// 类和其他语言的类相似,构造器参数在类名后声明,初始化在类结构体中完成。
+class Dog(br: String) {
+ // 构造器代码在此
+ var breed: String = br
+ // 定义名为 bark 的方法,返回字符串
+ def bark = "Woof, woof!"
-// Scala 的 foreach 方法定义在特定的接受一个类型的集合上
-// 返回 Unit(一个 void 方法)
-aListOfNumbers foreach (x => println(x))
-aListOfNumbers foreach println
+ // 值和方法作用域假定为 public。"protected" 和 "private" 关键字也是可用的。
+ private def sleep(hours: Int) =
+ println(s"I'm sleeping for $hours hours")
+ // 抽象方法是没有方法体的方法。如果取消下面那行注释,Dog 类必须被声明为 abstract
+ // abstract class Dog(...) { ... }
+ // def chaseAfter(what: String): String
+}
+val mydog = new Dog("greyhound")
+println(mydog.breed) // => "greyhound"
+println(mydog.bark) // => "Woof, woof!"
-// For 包含
+// "object" 关键字创造一种类型和该类型的单例。
+// Scala 的 class 常常也含有一个 “伴生对象”,class 中包含每个实例的行为,所有实例
+// 共用的行为则放入 object 中。两者的区别和其他语言中类方法和静态方法类似。
+// 请注意 object 和 class 可以同名。
+object Dog {
+ def allKnownBreeds = List("pitbull", "shepherd", "retriever")
+ def createDog(breed: String) = new Dog(breed)
+}
-for { n <- s } yield sq(n)
-val nSquared2 = for { n <- s } yield sq(n)
+// Case 类是有额外内建功能的类。Scala 初学者常遇到的问题之一便是何时用类
+// 和何时用 case 类。界线比较模糊,但通常类倾向于封装,多态和行为。类中的值
+// 的作用域一般为 private , 只有方向是暴露的。case 类的主要目的是放置不可变
+// 数据。它们通常只有几个方法,且方法几乎没有副作用。
+case class Person(name: String, phoneNumber: String)
-for { n <- nSquared2 if n < 10 } yield n
+// 创造新实例,注意 case 类不需要使用 "new" 关键字
+val george = Person("George", "1234")
+val kate = Person("Kate", "4567")
-for { n <- s; nSquared = n * n if nSquared < 10} yield nSquared
+// 使用 case 类,您可以轻松得到一些功能,像 getters:
+george.phoneNumber // => "1234"
-/* 注意:这些不是 for 循环. 一个 for 循环的语义是 '重复'('repeat'),
- 然而,一个 for-包含 定义了一个两个数据结合间的关系 */
+// 每个字段的相等性比较(无需覆盖 .equals)
+Person("George", "1234") == Person("Kate", "1236") // => false
+// 简单的拷贝方式
+// otherGeorge == Person("george", "9876")
+val otherGeorge = george.copy(phoneNumber = "9876")
+// 还有很多。case 类同时可以用于模式匹配,接下来会看到。
-// 循环和迭代
-1 to 5
-val r = 1 to 5
-r.foreach( println )
+// 敬请期待 Traits !
-r foreach println
-// 注意:Scala 是相当宽容的当它遇到点和括号 - 分别地学习这些规则。
-// 这帮助你编写读起来像英语的 DSLs 和 APIs
-(5 to 1 by -1) foreach ( println )
+/////////////////////////////////////////////////
+// 6. 模式匹配
+/////////////////////////////////////////////////
-// while 循环
-var i = 0
-while (i < 10) { println("i " + i); i+=1 }
-
-while (i < 10) { println("i " + i); i+=1 } // 发生了什么?为什么?
-
-i // 展示 i 的值。注意到 while 是一个传统意义上的循环
- // 它顺序地执行并且改变循环变量的值。while 非常快,比 Java // 循环快,
- // 但是在其上使用选择器和包含更容易理解和并行。
-
-// do while 循环
-do {
- println("x is still less then 10");
- x += 1
-} while (x < 10)
+// 模式匹配是一个强大和常用的 Scala 特性。这是用模式匹配一个 case 类的例子。
+// 附注:不像其他语言, Scala 的 case 不需要 break, 其他语言中 switch 语句的
+// fall-through 现象不会发生。
-// 在 Scala中,尾递归是一种惯用的执行循环的方式。
-// 递归函数需要显示的返回类型,编译器不能推断出类型。
-// 这里它是 Unit。
-def showNumbersInRange(a:Int, b:Int):Unit = {
- print(a)
- if (a < b)
- showNumbersInRange(a + 1, b)
+def matchPerson(person: Person): String = person match {
+ // Then you specify the patterns:
+ case Person("George", number) => "We found George! His number is " + number
+ case Person("Kate", number) => "We found Kate! Her number is " + number
+ case Person(name, number) => "We matched someone : " + name + ", phone : " + number
}
+val email = "(.*)@(.*)".r // 定义下一个例子会用到的正则
+// 模式匹配看起来和 C语言家族的 switch 语句相似,但更为强大。
+// Scala 中您可以匹配很多东西:
+def matchEverything(obj: Any): String = obj match {
+ // 匹配值:
+ case "Hello world" => "Got the string Hello world"
-// 条件语句
-
-val x = 10
-
-if (x == 1) println("yeah")
-if (x == 10) println("yeah")
-if (x == 11) println("yeah")
-if (x == 11) println ("yeah") else println("nay")
+ // 匹配类型:
+ case x: Double => "Got a Double: " + x
-println(if (x == 10) "yeah" else "nope")
-val text = if (x == 10) "yeah" else "nope"
+ // 匹配时指定条件
+ case x: Int if x > 10000 => "Got a pretty big number!"
-var i = 0
-while (i < 10) { println("i " + i); i+=1 }
+ // 像之前一样匹配 case 类:
+ case Person(name, number) => s"Got contact info for $name!"
+ // 匹配正则表达式:
+ case email(name, domain) => s"Got email address $name@$domain"
+ // 匹配元组:
+ case (a: Int, b: Double, c: String) => s"Got a tuple: $a, $b, $c"
-// 面向对象特性
+ // 匹配数据结构:
+ case List(1, b, c) => s"Got a list with three elements and starts with 1: 1, $b, $c"
-// 类名是 Dog
-class Dog {
- //bark 方法,返回字符串
- def bark: String = {
- // the body of the method
- "Woof, woof!"
- }
+ // 模式可以嵌套
+ case List(List((1, 2,"YAY"))) => "Got a list of list of tuple"
}
-// 类可以包含几乎其它的构造,包括其它的类,
-// 函数,方法,对象,case 类,特性等等。
-
-
+// 事实上,你可以对任何有 "unapply" 方法的对象进行模式匹配。
+// 这个特性如此强大以致于 Scala 允许定义一个函数作为模式匹配:
+val patternFunc: Person => String = {
+ case Person("George", number) => s"George's number: $number"
+ case Person(name, number) => s"Random person's number: $number"
+}
-// Case 类
-case class Person(name:String, phoneNumber:String)
+/////////////////////////////////////////////////
+// 7. 函数式编程
+/////////////////////////////////////////////////
-Person("George", "1234") == Person("Kate", "1236")
+// Scala 允许方法和函数作为其他方法和函数的参数和返回值。
+val add10: Int => Int = _ + 10 // 一个接受一个 Int 类型参数并返回一个 Int 类型值的函数
+List(1, 2, 3) map add10 // List(11, 12, 13) - add10 被应用到每一个元素
+// 匿名函数可以被使用来代替有命名的函数:
+List(1, 2, 3) map (x => x + 10)
+// 如果匿名函数只有一个参数可以用下划线作为变量
+List(1, 2, 3) map (_ + 10)
-// 模式匹配
+// 如果您所应用的匿名块和匿名函数都接受一个参数,那么你甚至可以省略下划线
+List("Dom", "Bob", "Natalia") foreach println
-val me = Person("George", "1234")
-me match { case Person(name, number) => {
- "We matched someone : " + name + ", phone : " + number }}
+// 组合子
-me match { case Person(name, number) => "Match : " + name; case _ => "Hm..." }
+// 译注: val sq: Int => Int = x => x * x
+s.map(sq)
-me match { case Person("George", number) => "Match"; case _ => "Hm..." }
+val sSquared = s. map(sq)
-me match { case Person("Kate", number) => "Match"; case _ => "Hm..." }
+sSquared.filter(_ < 10)
-me match { case Person("Kate", _) => "Girl"; case Person("George", _) => "Boy" }
+sSquared.reduce (_+_)
-val kate = Person("Kate", "1234")
+// filter 函数接受一个 predicate (函数根据条件 A 返回 Boolean)并选择
+// 所有满足 predicate 的元素
+List(1, 2, 3) filter (_ > 2) // List(3)
+case class Person(name:String, age:Int)
+List(
+ Person(name = "Dom", age = 23),
+ Person(name = "Bob", age = 30)
+).filter(_.age > 25) // List(Person("Bob", 30))
-kate match { case Person("Kate", _) => "Girl"; case Person("George", _) => "Boy" }
+// Scala 的 foreach 方法定义在某些集合中,接受一个函数并返回 Unit (void 方法)
+// 另请参见:
+// http://www.scala-lang.org/api/current/index.html#scala.collection.IterableLike@foreach(f:A=>Unit):Unit
+val aListOfNumbers = List(1, 2, 3, 4, 10, 20, 100)
+aListOfNumbers foreach (x => println(x))
+aListOfNumbers foreach println
+// For 推导式
-// 正则表达式
+for { n <- s } yield sq(n)
-val email = "(.*)@(.*)".r // 在字符串上调用 r 会使它变成一个正则表达式
+val nSquared2 = for { n <- s } yield sq(n)
-val email(user, domain) = "henry@zkpr.com"
+for { n <- nSquared2 if n < 10 } yield n
-"mrbean@pyahoo.com" match {
- case email(name, domain) => "I know your name, " + name
-}
+for { n <- s; nSquared = n * n if nSquared < 10} yield nSquared
+/* 注意,这些不是 for 循环,for 循环的语义是‘重复’,然而 for 推导式定义
+ 两个数据集合的关系。 */
-// 字符串
+/////////////////////////////////////////////////
+// 8. 隐式转换
+/////////////////////////////////////////////////
-"Scala 字符串被双引号包围" //
-'a' // Scala 字符
-'单引号的字符串不存在' // 错误
-"字符串拥有通常的 Java 方法定义在其上".length
-"字符串也有额外的 Scala 方法".reverse
+/* 警告 警告: 隐式转换是 Scala 中一套强大的特性,因此容易被滥用。
+ * Scala 初学者在理解它们的工作原理和最佳实践之前,应抵制使用它的诱惑。
+ * 我们加入这一章节仅因为它们在 Scala 的库中太过常见,导致没有用隐式转换的库
+ * 就不可能做有意义的事情。这章节主要让你理解和使用隐式转换,而不是自己声明。
+ */
-// 参见: scala.collection.immutable.StringOps
+// 可以通过 "implicit" 声明任何值(val, 函数,对象等)为隐式值,
+// 请注意这些例子中,我们用到第5部分的 Dog 类。
+implicit val myImplicitInt = 100
+implicit def myImplicitFunction(breed: String) = new Dog("Golden " + breed)
-println("ABCDEF".length)
-println("ABCDEF".substring(2, 6))
-println("ABCDEF".replace("C", "3"))
+// implicit 关键字本身不改变值的行为,所以上面的值可以照常使用。
+myImplicitInt + 2 // => 102
+myImplicitFunction("Pitbull").breed // => "Golden Pitbull"
-val n = 45
-println(s"We have $n apples")
+// 区别在于,当另一段代码“需要”隐式值时,这些值现在有资格作为隐式值。
+// 一种情况是隐式函数参数。
+def sendGreetings(toWhom: String)(implicit howMany: Int) =
+ s"Hello $toWhom, $howMany blessings to you and yours!"
-val a = Array(11, 9, 6)
-println(s"My second daughter is ${a(2-1)} years old")
+// 如果提供值给 “howMany”,函数正常运行
+sendGreetings("John")(1000) // => "Hello John, 1000 blessings to you and yours!"
-// 一些字符需要被转义,举例来说,字符串中的双引号:
-val a = "They stood outside the \"Rose and Crown\""
+// 如果省略隐式参数,会传一个和参数类型相同的隐式值,
+// 在这个例子中, 是 “myImplicitInt":
+sendGreetings("Jane") // => "Hello Jane, 100 blessings to you and yours!"
-// 三个双引号使得字符串可以跨行并且可以包含引号(无需转义)
+// 隐式的函数参数使我们可以模拟其他函数式语言的 type 类(type classes)。
+// 它经常被用到所以有特定的简写。这两行代码是一样的:
+def foo[T](implicit c: C[T]) = ...
+def foo[T : C] = ...
-val html = """<form id="daform">
- <p>Press belo', Joe</p>
- | <input type="submit">
- </form>"""
+// 编译器寻找隐式值另一种情况是你调用方法时
+// obj.method(...)
+// 但 "obj" 没有一个名为 "method" 的方法。这样的话,如果有一个参数类型为 A
+// 返回值类型为 B 的隐式转换,obj 的类型是 A,B 有一个方法叫 "method" ,这样
+// 转换就会被应用。所以作用域里有上面的 myImplicitFunction, 我们可以这样做:
+"Retriever".breed // => "Golden Retriever"
+"Sheperd".bark // => "Woof, woof!"
+// 这里字符串先被上面的函数转换为 Dog 对象,然后调用相应的方法。
+// 这是相当强大的特性,但再次提醒,请勿轻率使用。
+// 事实上,当你定义上面的隐式函数时,编译器会作出警告,除非你真的了解
+// 你正在做什么否则不要使用。
-// 应用结果和组织
+/////////////////////////////////////////////////
+// 9. 杂项
+/////////////////////////////////////////////////
-// import
+// 导入类
import scala.collection.immutable.List
-// Import 所有的子包
+// 导入所有子包
import scala.collection.immutable._
-// 在一条语句中 Import 多个类
+// 一条语句导入多个类
import scala.collection.immutable.{List, Map}
-// 使用 '=>' 来重命名一个 import
+// 使用 ‘=>’ 对导入进行重命名
import scala.collection.immutable.{ List => ImmutableList }
-// import 除了一些类的其它所有的类。下面的例子除去了 Map 类和 Set 类:
+// 导入所有类,排除其中一些。下面的语句排除了 Map 和 Set:
import scala.collection.immutable.{Map => _, Set => _, _}
-// 在 scala 源文件中,你的程序入口点使用一个拥有单一方法 main 的对象来定义:
-
+// 在 Scala 文件用 object 和单一的 main 方法定义程序入口:
object Application {
def main(args: Array[String]): Unit = {
// stuff goes here.
}
}
-// 文件可以包含多个类和对象。由 scalac 来编译
+// 文件可以包含多个 class 和 object,用 scalac 编译源文件
// 输入和输出
-// 一行一行读取文件
+// 按行读文件
import scala.io.Source
-for(line <- Source.fromPath("myfile.txt").getLines())
+for(line <- Source.fromFile("myfile.txt").getLines())
println(line)
-// 使用 Java 的 PrintWriter 来写文件
-
+// 用 Java 的 PrintWriter 写文件
+val writer = new PrintWriter("myfile.txt")
+writer.write("Writing line for line" + util.Properties.lineSeparator)
+writer.write("Another line here" + util.Properties.lineSeparator)
+writer.close()
```