From c67add4b0d96706a860e0c16aafa70442a994153 Mon Sep 17 00:00:00 2001 From: coderhh Date: Sat, 30 Apr 2016 23:34:39 -0400 Subject: [PATCH 1/2] re-order chapter --- README.md | 6 +- getting_started/Chapter17.md | 170 ++++++++-------------------- getting_started/Chapter18.md | 177 +++++++++++++++++++++-------- getting_started/Chapter19.md | 213 +++++++++++++++++------------------ 4 files changed, 283 insertions(+), 283 deletions(-) diff --git a/README.md b/README.md index ea1a21f..60a156c 100644 --- a/README.md +++ b/README.md @@ -35,11 +35,11 @@ Translate [official elixir getting started guide](http://elixir-lang.org/getting ### [第十六章 - 协议](./getting_started/Chapter16.md) ✓ -### [第十七章 - `try`,`catch`和`rescue`](./getting_started/Chapter17.md) ✓ +### [第十七章 - Comprehensions](./getting_started/Chapter17.md) ✓ -### [第十八章 - Comprehensions](./getting_started/Chapter18.md) ✓ +### [第十八章 - Sigils](./getting_started/Chapter18.md) ✓ -### [第十九章 - Sigils](./getting_started/Chapter19.md) ✓ +### [第十九章 - `try`,`catch`和`rescue`](./getting_started/Chapter19.md) ✓ ### [第二十章 - 更多内容](./getting_started/Chapter20.md) ✓ diff --git a/getting_started/Chapter17.md b/getting_started/Chapter17.md index 7e14ea1..dcb7f4f 100644 --- a/getting_started/Chapter17.md +++ b/getting_started/Chapter17.md @@ -1,169 +1,89 @@ -# 17 `try`, `catch`和`rescue` +# 18 Comprehensions -Elixir有三个错误处理机制:错误,抛出和退出。在这一章我们将一一探索它们,包括应该在什么时候使用它们。 +在Elixir中,常常用到循环历遍一个可枚举类,用于过滤结果,和映射到另一个列表的值上。 -## 17.1 错误 +Comprehension是对这一中结构的语法糖, 把这些常见的任务用一个特殊的形式`for`组织起来。 -第一个典型的错误是试图把一个数字和原子相加: +例如,我们能用下面的方式得到一个列表值的平方: ``` -iex> :foo + 1 -** (ArithmeticError) bad argument in arithmetic expression - :erlang.+(:foo, 1) +iex> for n <- [1, 2, 3, 4], do: n * n +[1, 4, 9, 16] ``` -在运行时调用宏`raise/1 `导致一个错误: +一个Compreshension由三个部分组成: 生成器,过滤器和集合。 -``` -iex> raise "oops" -** (RuntimeError) oops -``` +## 18.1 生成器和过滤器 -另一些错误能通过给`raise/2`传递一个错误名和一个关键字列表来形成: +在上面的表达式中,`n <- [1, 2, 3,4]`是生成器。产出了供后面comprehension的值。任何的可枚举类都能被放置在产生器表达式的右侧: ``` -iex> raise ArgumentError, message: "invalid argument foo" -** (ArgumentError) invalid argument foo +iex> for n <- 1..4, do: n * n +[1, 4, 9, 16] ``` -你也可以用宏`defexception/2`定义你自己的错误。最常见的例子是定义一个带有message域的异常: +生成器表达式也支持模式匹配,来丢弃所有不匹配的模式。想象一下假如不用范围,我们有一个关键字列表,这个列表有两种键`:good `和`:bad`。而我们只关心好的值的计算结果: ``` -iex> defexception MyError, message: "default message" -iex> raise MyError -** (MyError) default message -iex> raise MyError, message: "custom message" -** (MyError) custom message +iex> values = [good: 1, good: 2, bad: 3, good: 4] +iex> for {:good, n} <- values, do: n * n +[1, 4, 16] ``` -异常可以被通过`try/rescue`挽救: +除此之外,过滤器能被用于过滤一些特定的元素。例如, 我们能只计算奇数的平方: ``` -iex> try do -...> raise "oops" -...> rescue -...> e in RuntimeError -> e -...> end -RuntimeError[message: "oops"] +iex> require Integer +iex> for n <- 1..4, Integer.odd?(n), do: n * n +[1, 9] ``` -上面的例子挽救了一个运行时错误并返回这个错误,并在`iex`的session中打印出来。在实践中Elixir开发者很少使用`try/rescue`结构。例如,许多语言中但一个文件无法被打开时,会强制你去挽救一个错误。相反Elixir提供了一个函数`File.read/1`,它返回一个包含文件是否被成功打开的相关信息的元组。 +一个过滤器会保留除了`false`和`nil`之外的所有值。 -``` -iex> File.read "hello" -{:error, :enoent} -iex. File.write "hello", "world" -:ok -iex> File.read "hello" -{:ok, "world"} -``` - -这里没有`try/rescue`。如果你想要处理开打文件的不同后果,你可以非常方便地使用`case`做模式识别: +相比直接使用`Enum`和`Stream `模块中的函数,comprehension提供了一种简洁的多的表现形式。而且,comprehension也允许多个生成器和过滤器。这里是一个例子用来接受一个文件夹的列表,然后删除每个文件夹中的所有文件: ``` -iex> case File.read "hello" do -...> {:ok, body} -> IO.puts "got ok" -...> {:error, body} -> IO.puts "got error" -...> end +for dir <- dirs, + file <- File.ls!(dir), + path = Path.join(dir, file), + File.regular?(path) do + File.rm!(path) +end ``` -当然,最终还是取决你自己的应用来决定打开一个文件是不是一个错误。这也是为什么Elixir没有在`File.read/1`和其他函数中只用异常。它把选择最佳的处理方式的决定留给了开发者。 +谨记,在comprehension内不赋值的变量,无论是在生成器内,还是在过滤器内,不会影响到comprehension外的环境 -在某些情况下,当你期待一个文件的确存在(并如果没有这个文件,这的确是一个错误),有可以轻松地嗲用`File.read!/1`: +## 18.2 比特串生成器 -``` -iex> File.read! "unknown" -** (File.Error) could not read file unknown: no such file or directory - (elixir) lib/file.ex:305: File.read!/1 -``` - -用另一种话来说,我们避免使用`try/rescue`因为我们不用错误来做控制流程。在Eliixr中,我们对错误的理解是字面意义上的:它们就是没有预期的或留给意外的或情况的。如果你的确需要流程控制结构,就需要用到throw。这也是下一章的内容。 - -## 17.2 抛出 - -在Elixir中,一个抛出的值能之后被捕获。`throw`和`catch`是用于除了是用`throw`和`catch`之外没法获取一个值的情况。 - -这些情况在时间中是不常见的,除非当你要和一些API定义地不好的库打交道的时候。例如,让我们想象`v`模块没有提供提供寻找值的API,所以我们必须去找到第一个是13的倍数的数字: +比特串生成器也支持的,而且当你需要组织比特串流的时候会非常有用。下面的这个例子从一个二进制接受一个列表的像素,每个像素用红绿蓝三色的数值表示,把它们转成三元组。 ``` -iex> try do -...> Enum.each -50..50, fn(x) -> -...> if rem(x, 13) == 0, do: throw(x) -...> end -...> "Got nothing" -...> catch -...> x -> "Got #{x}" -...> end -"Got -39" +iex> pixels = <<213, 45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15>> +iex> for <>, do: {r, g, b} +[{213,45,132},{64,76,32},{76,0,0},{234,32,15}] ``` -然而,在实际上`Enum.find/2`就可以轻松做到: +一个比特串的生成器能和“普通”的可枚举类产生器混合,并提供过滤器。 -``` -iex> Enum.find -50..50, &(rem(&1, 13) == 0) --39 -``` +# 18.3 Into -## 17.3 退出 +在上面的例子中,comprehension返回一个列表作为结果。 -所有的Eliixr代码都运行在进程中,进程之间互相通信。当一个进程死亡,它会发送一个`exit`信号。也可以手动发送一个退出信号来杀死一个进程: +然而,用传递`:into`选项,compreshension的结果能被插入不同的数据结果。例如,我们能用比特串产生器和`:into`选项来轻松地删除字符串中的所有空格: ``` -iex> spawn_link fn -> exit(1) end -#PID<0.56.0> -** (EXIT from #PID<0.56.0>) 1 +iex> for <>, c != ?\s, into: "", do: <> +"helloworld" ``` +`Set`, `maps`和其他类型的字典也能被给予`:into`选项。总的来说,`:into`接受任何一种数据结构,只要它实现了`Collectable`协议。 -在上面的例子中,我们链接了死亡的进程之后发送了退出信号,它的值是1.Elixir控制台自动处理这些消息,并把它们打印出来: - -`exit`也能被``捕获: +例如,模块提供的流,它们既是`Enumerable`又是`Collectable`。你能用comprehension实现一个echo控制台,它返回所有接受到的输入和大写形式。 ``` -iex> try do -...> exit "I am exiting" -...> catch -...> :exit, _ -> "not really" +iex> stream = IO.stream(:stdio, :line) +iex> for line <- stream, into: stream do +...> String.upcase(line) <> "\n" ...> end -"not really" ``` -用`try/catch`已经是非常罕见的,用它们来捕获退出更是少见。 - -`exit`信号是Eralng虚拟机提供的tolerant机制的重要组成部分。进程通常在监控树之下运行,它们其实是一个等待被监控的进程的退出信号的进程。当一个收到一个退出信号,它们的监控策略被触发并将死亡的被监控进程重启。 - -正式这个监控系统使得`try/catch`和`try/rescue`在Elixir中用的这么少。与其挽救一个错误,我们更愿意让他`先失败`,因为监控树会保证我们的应用在错误之后,会重回到一个已知的初始状态。 - -## 17.4 之后 - -有时的确有必要时候`try/after`来保证一个资源在某些特定的动作之后被清理。例如,我们打开了一个文件并用` try/after`来保证它的关闭。 - -``` -iex> {:ok, file} = File.open "sample", [:utf8, :write] -iex> try do -...> IO.write file, "josé" -...> raise "oops, something went wrong" -...> after -...> File.close(file) -...> end -** (RuntimeError) oops, something went wrong -``` - -## 17.5 变量作用域 - -牢记在`try/catch/rescue/after `内部定义的变量并不会泄漏到外部环境中。这是因为`try`块也会会失败,所以变量也许从一开始就是找不到的。换一句话来说,这些代码是非法的: - -``` -iex> try do -...> from_try = true -...> after -...> from_after = true -...> end -iex> from_try -** (RuntimeError) undefined function: from_try/0 -iex> from_after -** (RuntimeError) undefined function: from_after/0 -``` - -到这里,我们结束了我们对`try`,`catch`和`rescue`的介绍。你会发现和在其他语言中相比,它们在Elixir中用的不多。当然在某些特定的情况下当一个库或一些特定的代码“不按规矩出牌”的时候,也许它们会有用。 - -是时候让我们谈谈一些Elixir的构建比如comprehension和sigil了。 +如果你在那个控制台里输入任何字符串,你将会看到同样的值会以大写的形式被打印出来。不幸的是,这个comprehension会锁死你的终端,所以你必须敲击两次``才能退出程序。:) diff --git a/getting_started/Chapter18.md b/getting_started/Chapter18.md index dcb7f4f..7555cfb 100644 --- a/getting_started/Chapter18.md +++ b/getting_started/Chapter18.md @@ -1,89 +1,176 @@ -# 18 Comprehensions +# 19 Sigils -在Elixir中,常常用到循环历遍一个可枚举类,用于过滤结果,和映射到另一个列表的值上。 +我们已经学习到了Elixir提供了双引号的字符串和单引号的字符列表。然而,这只是这个语言中那些具有文字表现形式的结构的表层现象。比如,原子,是另一种常常通过``形式创建的结构。 -Comprehension是对这一中结构的语法糖, 把这些常见的任务用一个特殊的形式`for`组织起来。 +Elixir的其中一个目标是可扩展性:开发者应该能够在某个领域扩展语言。计算机科学已经变得如此宽广的范围,以至于已经不可能仅仅用一种语言的核心就能解决所有的问题。我们最佳的赌注是使得语言可扩展,那样开发者,公司或者社区能在相关的领域内扩展语言。 -例如,我们能用下面的方式得到一个列表值的平方: +在这一章,我们将探索sigil,它是语言层面提供的能用于字面呈现的机制之一。 + +# 19.1 正则表达式 + +Sigils由一个约等于号(`~`)开始,跟着一个字母,再跟着是一个分隔符。最常见的sigil是`~r`,用于[正则表达式](https://en.wikipedia.org/wiki/Regular_Expressions): + +``` +# A regular expression that returns true if the text has foo or bar +iex> regex = ~r/foo|bar/ +~r/foo|bar/ +iex> "foo" =~ regex +true +iex> "bat" =~ regex +false +``` + +Elixir提供了Perl兼容的正则表达式(regrexs),类似于在[PCRE](http://www.pcre.org/)库中实现的。正则也支持修饰符。例如,修饰符`i`会使得一个正则表达式无关大小写: + +``` +iex> "HELLO" =~ ~r/hello/ +false +iex> "HELLO" =~ ~r/hello/i +true +``` + +在[`Regex`](http://elixir-lang.org/docs/stable/Regex.html)模块的文档中有更详细的有关正则表达式的其他修饰符和所支持的其他操作的内容。 + +到目前为止,所有的例子都使用了`/`来分割正则表达式。然而sigil支持8中不同的分隔符: + +``` +~r/hello/ +~r|hello| +~r"hello" +~r'hello' +~r(hello) +~r[hello] +~r{hello} +~r +``` + +只所以支持这么多不同的操作符是因为不同的分隔符能更方便地在不同的sigils中使用。例如,在正则中使用圆括号也许就不合适,因为这会和正则内部的圆括号混淆。然而, 圆括号在其他的sigil中就非常适用,我们将在下面看到例证。 + +## 19.2 字符串,字符列表和其他sigils + +除了正则,Elixir默认提供了其他三中sigils。 + +`~s`sigils被用于产生字符串,同双引号类似。 ``` -iex> for n <- [1, 2, 3, 4], do: n * n -[1, 4, 9, 16] +iex> ~s(this is a string with "quotes") +"this is a string with \"quotes\"" ``` -一个Compreshension由三个部分组成: 生成器,过滤器和集合。 +而`~c`sigil用于产生字符列表: -## 18.1 生成器和过滤器 +``` +iex> ~c(this is a string with "quotes") +'this is a string with "quotes"' +``` -在上面的表达式中,`n <- [1, 2, 3,4]`是生成器。产出了供后面comprehension的值。任何的可枚举类都能被放置在产生器表达式的右侧: +`~w`sigil用于产生一个用空格分割的词汇的列表: ``` -iex> for n <- 1..4, do: n * n -[1, 4, 9, 16] +iex> ~w(foo bar bat) +["foo", "bar", "bat"] ``` -生成器表达式也支持模式匹配,来丢弃所有不匹配的模式。想象一下假如不用范围,我们有一个关键字列表,这个列表有两种键`:good `和`:bad`。而我们只关心好的值的计算结果: +`~w`sigil也能接受修饰符`c`,`s`和`a`,来选择结果的格式: ``` -iex> values = [good: 1, good: 2, bad: 3, good: 4] -iex> for {:good, n} <- values, do: n * n -[1, 4, 16] +iex> ~w(foo bar bat)a +[:foo, :bar, :bat] ``` -除此之外,过滤器能被用于过滤一些特定的元素。例如, 我们能只计算奇数的平方: +除了小写字母的sigils, Elixir也支持大写形式的sigils。虽然`~s`和`~S`都返回字符串,前者能允许代码忽略和融合,而后者不能: + ``` -iex> require Integer -iex> for n <- 1..4, Integer.odd?(n), do: n * n -[1, 9] +iex> ~s(String with escape codes \x26 interpolation) +"String with escape codes & interpolation" +iex> ~S(String without escape codes and without #{interpolation}) +"String without escape codes and without \#{interpolation}" ``` -一个过滤器会保留除了`false`和`nil`之外的所有值。 +下面这些忽略符号能被用于字符串和字符列表: +* `\"` - 双引号 +* `\''` - 单引号 +* `\\` - 单斜杠 +* `\a` - bell/alert +* `\b` - backspace +* `\d` - 删除 +* `\e` - 忽略 +* `\f` - form feed +* `\n` - 换行符 +* `\r` - 回车 +* `\s` - 空格 +* `\t` - tab +* `\v` - vertial tab +* `\DDD`,`\DD`,`\D` - 用八进制表示的字符 DDD,DD或D(例如`\377`) +* `\xDD` - 用十六进制表示的字符 DD, 例如(`\x37`) +* `\x{D...}` - 用十六禁止表示的字符,至少含有一个十六进制字符(例如, `/x{abc123}`) -相比直接使用`Enum`和`Stream `模块中的函数,comprehension提供了一种简洁的多的表现形式。而且,comprehension也允许多个生成器和过滤器。这里是一个例子用来接受一个文件夹的列表,然后删除每个文件夹中的所有文件: +Sigil也支持heredocs,用三双引号或三单引号来作为分隔符: ``` -for dir <- dirs, - file <- File.ls!(dir), - path = Path.join(dir, file), - File.regular?(path) do - File.rm!(path) -end +iex> ~s""" +...> this is +...> a heredoc string +...> """ +``` + +最常见的heredocs sigil是用于编写文档。例如,如果你需要在你的文档中包含忽略字符,会很麻烦,因为它需要对某些字符做双重忽略: + ``` +@doc """ +Converts double-quotes to single-quotes. -谨记,在comprehension内不赋值的变量,无论是在生成器内,还是在过滤器内,不会影响到comprehension外的环境 +## Examples -## 18.2 比特串生成器 + iex> convert("\\\"foo\\\"") + "'foo'" -比特串生成器也支持的,而且当你需要组织比特串流的时候会非常有用。下面的这个例子从一个二进制接受一个列表的像素,每个像素用红绿蓝三色的数值表示,把它们转成三元组。 +""" +def convert(...) +``` + +但有了`-S`, 我们能彻底避免这个问题: ``` -iex> pixels = <<213, 45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15>> -iex> for <>, do: {r, g, b} -[{213,45,132},{64,76,32},{76,0,0},{234,32,15}] +@doc ~S""" +Converts double-quotes to single-quotes. + +## Examples + + iex> convert("\"foo\"") + "'foo'" + +""" +def convert(...) ``` -一个比特串的生成器能和“普通”的可枚举类产生器混合,并提供过滤器。 +# 19.3 定制sigil + +正如在开头暗示的那样,sigil是Elixir中可扩展的。实际上,sigil`sigil_r`相当于用两个参数调用函数`sigil_r`: -# 18.3 Into +``` +iex> sigil_r(<<"foo">>, 'i') +~r"foo"i +``` -在上面的例子中,comprehension返回一个列表作为结果。 +这就是说,我们通过函数`sigil_r`来得到sigil`~r`的文档: -然而,用传递`:into`选项,compreshension的结果能被插入不同的数据结果。例如,我们能用比特串产生器和`:into`选项来轻松地删除字符串中的所有空格: ``` -iex> for <>, c != ?\s, into: "", do: <> -"helloworld" +iex> h sigil_r +... ``` -`Set`, `maps`和其他类型的字典也能被给予`:into`选项。总的来说,`:into`接受任何一种数据结构,只要它实现了`Collectable`协议。 -例如,模块提供的流,它们既是`Enumerable`又是`Collectable`。你能用comprehension实现一个echo控制台,它返回所有接受到的输入和大写形式。 +我们也能通过实现何时的函数,我们也能实现自己的sigil。例如,让我们实现sigil`~i(13)`来回返一个整数: ``` -iex> stream = IO.stream(:stdio, :line) -iex> for line <- stream, into: stream do -...> String.upcase(line) <> "\n" +iex> defmodule MySigils do +...> def sigil_i(binary, []), do: binary_to_integer(binary) ...> end +iex> import MySigils +iex> ~i(13) +13 ``` -如果你在那个控制台里输入任何字符串,你将会看到同样的值会以大写的形式被打印出来。不幸的是,这个comprehension会锁死你的终端,所以你必须敲击两次``才能退出程序。:) +sigil也能在宏的帮助下,在编译时发挥作用。例如,在Elixir中正则表达式在编译时会被编译成高效的代码形式,随后就无需在运行时做这些事情了。如果你对这个主题感兴趣,我们推荐你学习宏并且查阅这些sigils是如何在`Kernel`模块中实现的。 diff --git a/getting_started/Chapter19.md b/getting_started/Chapter19.md index 7555cfb..7e14ea1 100644 --- a/getting_started/Chapter19.md +++ b/getting_started/Chapter19.md @@ -1,176 +1,169 @@ -# 19 Sigils +# 17 `try`, `catch`和`rescue` -我们已经学习到了Elixir提供了双引号的字符串和单引号的字符列表。然而,这只是这个语言中那些具有文字表现形式的结构的表层现象。比如,原子,是另一种常常通过``形式创建的结构。 +Elixir有三个错误处理机制:错误,抛出和退出。在这一章我们将一一探索它们,包括应该在什么时候使用它们。 -Elixir的其中一个目标是可扩展性:开发者应该能够在某个领域扩展语言。计算机科学已经变得如此宽广的范围,以至于已经不可能仅仅用一种语言的核心就能解决所有的问题。我们最佳的赌注是使得语言可扩展,那样开发者,公司或者社区能在相关的领域内扩展语言。 +## 17.1 错误 -在这一章,我们将探索sigil,它是语言层面提供的能用于字面呈现的机制之一。 - -# 19.1 正则表达式 - -Sigils由一个约等于号(`~`)开始,跟着一个字母,再跟着是一个分隔符。最常见的sigil是`~r`,用于[正则表达式](https://en.wikipedia.org/wiki/Regular_Expressions): +第一个典型的错误是试图把一个数字和原子相加: ``` -# A regular expression that returns true if the text has foo or bar -iex> regex = ~r/foo|bar/ -~r/foo|bar/ -iex> "foo" =~ regex -true -iex> "bat" =~ regex -false +iex> :foo + 1 +** (ArithmeticError) bad argument in arithmetic expression + :erlang.+(:foo, 1) ``` -Elixir提供了Perl兼容的正则表达式(regrexs),类似于在[PCRE](http://www.pcre.org/)库中实现的。正则也支持修饰符。例如,修饰符`i`会使得一个正则表达式无关大小写: +在运行时调用宏`raise/1 `导致一个错误: ``` -iex> "HELLO" =~ ~r/hello/ -false -iex> "HELLO" =~ ~r/hello/i -true +iex> raise "oops" +** (RuntimeError) oops ``` -在[`Regex`](http://elixir-lang.org/docs/stable/Regex.html)模块的文档中有更详细的有关正则表达式的其他修饰符和所支持的其他操作的内容。 - -到目前为止,所有的例子都使用了`/`来分割正则表达式。然而sigil支持8中不同的分隔符: +另一些错误能通过给`raise/2`传递一个错误名和一个关键字列表来形成: ``` -~r/hello/ -~r|hello| -~r"hello" -~r'hello' -~r(hello) -~r[hello] -~r{hello} -~r +iex> raise ArgumentError, message: "invalid argument foo" +** (ArgumentError) invalid argument foo ``` -只所以支持这么多不同的操作符是因为不同的分隔符能更方便地在不同的sigils中使用。例如,在正则中使用圆括号也许就不合适,因为这会和正则内部的圆括号混淆。然而, 圆括号在其他的sigil中就非常适用,我们将在下面看到例证。 - -## 19.2 字符串,字符列表和其他sigils - -除了正则,Elixir默认提供了其他三中sigils。 - -`~s`sigils被用于产生字符串,同双引号类似。 +你也可以用宏`defexception/2`定义你自己的错误。最常见的例子是定义一个带有message域的异常: ``` -iex> ~s(this is a string with "quotes") -"this is a string with \"quotes\"" +iex> defexception MyError, message: "default message" +iex> raise MyError +** (MyError) default message +iex> raise MyError, message: "custom message" +** (MyError) custom message ``` -而`~c`sigil用于产生字符列表: +异常可以被通过`try/rescue`挽救: ``` -iex> ~c(this is a string with "quotes") -'this is a string with "quotes"' +iex> try do +...> raise "oops" +...> rescue +...> e in RuntimeError -> e +...> end +RuntimeError[message: "oops"] ``` -`~w`sigil用于产生一个用空格分割的词汇的列表: +上面的例子挽救了一个运行时错误并返回这个错误,并在`iex`的session中打印出来。在实践中Elixir开发者很少使用`try/rescue`结构。例如,许多语言中但一个文件无法被打开时,会强制你去挽救一个错误。相反Elixir提供了一个函数`File.read/1`,它返回一个包含文件是否被成功打开的相关信息的元组。 ``` -iex> ~w(foo bar bat) -["foo", "bar", "bat"] +iex> File.read "hello" +{:error, :enoent} +iex. File.write "hello", "world" +:ok +iex> File.read "hello" +{:ok, "world"} ``` -`~w`sigil也能接受修饰符`c`,`s`和`a`,来选择结果的格式: +这里没有`try/rescue`。如果你想要处理开打文件的不同后果,你可以非常方便地使用`case`做模式识别: ``` -iex> ~w(foo bar bat)a -[:foo, :bar, :bat] +iex> case File.read "hello" do +...> {:ok, body} -> IO.puts "got ok" +...> {:error, body} -> IO.puts "got error" +...> end ``` -除了小写字母的sigils, Elixir也支持大写形式的sigils。虽然`~s`和`~S`都返回字符串,前者能允许代码忽略和融合,而后者不能: +当然,最终还是取决你自己的应用来决定打开一个文件是不是一个错误。这也是为什么Elixir没有在`File.read/1`和其他函数中只用异常。它把选择最佳的处理方式的决定留给了开发者。 +在某些情况下,当你期待一个文件的确存在(并如果没有这个文件,这的确是一个错误),有可以轻松地嗲用`File.read!/1`: ``` -iex> ~s(String with escape codes \x26 interpolation) -"String with escape codes & interpolation" -iex> ~S(String without escape codes and without #{interpolation}) -"String without escape codes and without \#{interpolation}" +iex> File.read! "unknown" +** (File.Error) could not read file unknown: no such file or directory + (elixir) lib/file.ex:305: File.read!/1 ``` -下面这些忽略符号能被用于字符串和字符列表: -* `\"` - 双引号 -* `\''` - 单引号 -* `\\` - 单斜杠 -* `\a` - bell/alert -* `\b` - backspace -* `\d` - 删除 -* `\e` - 忽略 -* `\f` - form feed -* `\n` - 换行符 -* `\r` - 回车 -* `\s` - 空格 -* `\t` - tab -* `\v` - vertial tab -* `\DDD`,`\DD`,`\D` - 用八进制表示的字符 DDD,DD或D(例如`\377`) -* `\xDD` - 用十六进制表示的字符 DD, 例如(`\x37`) -* `\x{D...}` - 用十六禁止表示的字符,至少含有一个十六进制字符(例如, `/x{abc123}`) +用另一种话来说,我们避免使用`try/rescue`因为我们不用错误来做控制流程。在Eliixr中,我们对错误的理解是字面意义上的:它们就是没有预期的或留给意外的或情况的。如果你的确需要流程控制结构,就需要用到throw。这也是下一章的内容。 + +## 17.2 抛出 -Sigil也支持heredocs,用三双引号或三单引号来作为分隔符: +在Elixir中,一个抛出的值能之后被捕获。`throw`和`catch`是用于除了是用`throw`和`catch`之外没法获取一个值的情况。 + +这些情况在时间中是不常见的,除非当你要和一些API定义地不好的库打交道的时候。例如,让我们想象`v`模块没有提供提供寻找值的API,所以我们必须去找到第一个是13的倍数的数字: ``` -iex> ~s""" -...> this is -...> a heredoc string -...> """ +iex> try do +...> Enum.each -50..50, fn(x) -> +...> if rem(x, 13) == 0, do: throw(x) +...> end +...> "Got nothing" +...> catch +...> x -> "Got #{x}" +...> end +"Got -39" ``` -最常见的heredocs sigil是用于编写文档。例如,如果你需要在你的文档中包含忽略字符,会很麻烦,因为它需要对某些字符做双重忽略: +然而,在实际上`Enum.find/2`就可以轻松做到: ``` -@doc """ -Converts double-quotes to single-quotes. +iex> Enum.find -50..50, &(rem(&1, 13) == 0) +-39 +``` -## Examples +## 17.3 退出 - iex> convert("\\\"foo\\\"") - "'foo'" +所有的Eliixr代码都运行在进程中,进程之间互相通信。当一个进程死亡,它会发送一个`exit`信号。也可以手动发送一个退出信号来杀死一个进程: -""" -def convert(...) ``` - -但有了`-S`, 我们能彻底避免这个问题: - +iex> spawn_link fn -> exit(1) end +#PID<0.56.0> +** (EXIT from #PID<0.56.0>) 1 ``` -@doc ~S""" -Converts double-quotes to single-quotes. -## Examples +在上面的例子中,我们链接了死亡的进程之后发送了退出信号,它的值是1.Elixir控制台自动处理这些消息,并把它们打印出来: - iex> convert("\"foo\"") - "'foo'" +`exit`也能被``捕获: -""" -def convert(...) +``` +iex> try do +...> exit "I am exiting" +...> catch +...> :exit, _ -> "not really" +...> end +"not really" ``` -# 19.3 定制sigil +用`try/catch`已经是非常罕见的,用它们来捕获退出更是少见。 -正如在开头暗示的那样,sigil是Elixir中可扩展的。实际上,sigil`sigil_r`相当于用两个参数调用函数`sigil_r`: +`exit`信号是Eralng虚拟机提供的tolerant机制的重要组成部分。进程通常在监控树之下运行,它们其实是一个等待被监控的进程的退出信号的进程。当一个收到一个退出信号,它们的监控策略被触发并将死亡的被监控进程重启。 -``` -iex> sigil_r(<<"foo">>, 'i') -~r"foo"i -``` +正式这个监控系统使得`try/catch`和`try/rescue`在Elixir中用的这么少。与其挽救一个错误,我们更愿意让他`先失败`,因为监控树会保证我们的应用在错误之后,会重回到一个已知的初始状态。 -这就是说,我们通过函数`sigil_r`来得到sigil`~r`的文档: +## 17.4 之后 +有时的确有必要时候`try/after`来保证一个资源在某些特定的动作之后被清理。例如,我们打开了一个文件并用` try/after`来保证它的关闭。 ``` -iex> h sigil_r -... +iex> {:ok, file} = File.open "sample", [:utf8, :write] +iex> try do +...> IO.write file, "josé" +...> raise "oops, something went wrong" +...> after +...> File.close(file) +...> end +** (RuntimeError) oops, something went wrong ``` -我们也能通过实现何时的函数,我们也能实现自己的sigil。例如,让我们实现sigil`~i(13)`来回返一个整数: +## 17.5 变量作用域 + +牢记在`try/catch/rescue/after `内部定义的变量并不会泄漏到外部环境中。这是因为`try`块也会会失败,所以变量也许从一开始就是找不到的。换一句话来说,这些代码是非法的: ``` -iex> defmodule MySigils do -...> def sigil_i(binary, []), do: binary_to_integer(binary) +iex> try do +...> from_try = true +...> after +...> from_after = true ...> end -iex> import MySigils -iex> ~i(13) -13 +iex> from_try +** (RuntimeError) undefined function: from_try/0 +iex> from_after +** (RuntimeError) undefined function: from_after/0 ``` -sigil也能在宏的帮助下,在编译时发挥作用。例如,在Elixir中正则表达式在编译时会被编译成高效的代码形式,随后就无需在运行时做这些事情了。如果你对这个主题感兴趣,我们推荐你学习宏并且查阅这些sigils是如何在`Kernel`模块中实现的。 +到这里,我们结束了我们对`try`,`catch`和`rescue`的介绍。你会发现和在其他语言中相比,它们在Elixir中用的不多。当然在某些特定的情况下当一个库或一些特定的代码“不按规矩出牌”的时候,也许它们会有用。 + +是时候让我们谈谈一些Elixir的构建比如comprehension和sigil了。 From f1c44b721f86522a73106eecd97ea663b85a6844 Mon Sep 17 00:00:00 2001 From: coderhh Date: Sat, 30 Apr 2016 23:38:04 -0400 Subject: [PATCH 2/2] change title number --- getting_started/Chapter17.md | 8 ++++---- getting_started/Chapter18.md | 8 ++++---- getting_started/Chapter19.md | 12 ++++++------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/getting_started/Chapter17.md b/getting_started/Chapter17.md index dcb7f4f..472df61 100644 --- a/getting_started/Chapter17.md +++ b/getting_started/Chapter17.md @@ -1,4 +1,4 @@ -# 18 Comprehensions +# 17 Comprehensions 在Elixir中,常常用到循环历遍一个可枚举类,用于过滤结果,和映射到另一个列表的值上。 @@ -13,7 +13,7 @@ iex> for n <- [1, 2, 3, 4], do: n * n 一个Compreshension由三个部分组成: 生成器,过滤器和集合。 -## 18.1 生成器和过滤器 +## 17.1 生成器和过滤器 在上面的表达式中,`n <- [1, 2, 3,4]`是生成器。产出了供后面comprehension的值。任何的可枚举类都能被放置在产生器表达式的右侧: @@ -53,7 +53,7 @@ end 谨记,在comprehension内不赋值的变量,无论是在生成器内,还是在过滤器内,不会影响到comprehension外的环境 -## 18.2 比特串生成器 +## 17.2 比特串生成器 比特串生成器也支持的,而且当你需要组织比特串流的时候会非常有用。下面的这个例子从一个二进制接受一个列表的像素,每个像素用红绿蓝三色的数值表示,把它们转成三元组。 @@ -65,7 +65,7 @@ iex> for <>, do: {r, g, b} 一个比特串的生成器能和“普通”的可枚举类产生器混合,并提供过滤器。 -# 18.3 Into +# 17.3 Into 在上面的例子中,comprehension返回一个列表作为结果。 diff --git a/getting_started/Chapter18.md b/getting_started/Chapter18.md index 7555cfb..2b0d648 100644 --- a/getting_started/Chapter18.md +++ b/getting_started/Chapter18.md @@ -1,4 +1,4 @@ -# 19 Sigils +# 18 Sigils 我们已经学习到了Elixir提供了双引号的字符串和单引号的字符列表。然而,这只是这个语言中那些具有文字表现形式的结构的表层现象。比如,原子,是另一种常常通过``形式创建的结构。 @@ -6,7 +6,7 @@ Elixir的其中一个目标是可扩展性:开发者应该能够在某个领 在这一章,我们将探索sigil,它是语言层面提供的能用于字面呈现的机制之一。 -# 19.1 正则表达式 +# 18.1 正则表达式 Sigils由一个约等于号(`~`)开始,跟着一个字母,再跟着是一个分隔符。最常见的sigil是`~r`,用于[正则表达式](https://en.wikipedia.org/wiki/Regular_Expressions): @@ -46,7 +46,7 @@ true 只所以支持这么多不同的操作符是因为不同的分隔符能更方便地在不同的sigils中使用。例如,在正则中使用圆括号也许就不合适,因为这会和正则内部的圆括号混淆。然而, 圆括号在其他的sigil中就非常适用,我们将在下面看到例证。 -## 19.2 字符串,字符列表和其他sigils +## 18.2 字符串,字符列表和其他sigils 除了正则,Elixir默认提供了其他三中sigils。 @@ -145,7 +145,7 @@ Converts double-quotes to single-quotes. def convert(...) ``` -# 19.3 定制sigil +# 18.3 定制sigil 正如在开头暗示的那样,sigil是Elixir中可扩展的。实际上,sigil`sigil_r`相当于用两个参数调用函数`sigil_r`: diff --git a/getting_started/Chapter19.md b/getting_started/Chapter19.md index 7e14ea1..aad06f3 100644 --- a/getting_started/Chapter19.md +++ b/getting_started/Chapter19.md @@ -1,8 +1,8 @@ -# 17 `try`, `catch`和`rescue` +# 19 `try`, `catch`和`rescue` Elixir有三个错误处理机制:错误,抛出和退出。在这一章我们将一一探索它们,包括应该在什么时候使用它们。 -## 17.1 错误 +## 19.1 错误 第一个典型的错误是试图把一个数字和原子相加: @@ -79,7 +79,7 @@ iex> File.read! "unknown" 用另一种话来说,我们避免使用`try/rescue`因为我们不用错误来做控制流程。在Eliixr中,我们对错误的理解是字面意义上的:它们就是没有预期的或留给意外的或情况的。如果你的确需要流程控制结构,就需要用到throw。这也是下一章的内容。 -## 17.2 抛出 +## 19.2 抛出 在Elixir中,一个抛出的值能之后被捕获。`throw`和`catch`是用于除了是用`throw`和`catch`之外没法获取一个值的情况。 @@ -104,7 +104,7 @@ iex> Enum.find -50..50, &(rem(&1, 13) == 0) -39 ``` -## 17.3 退出 +## 19.3 退出 所有的Eliixr代码都运行在进程中,进程之间互相通信。当一个进程死亡,它会发送一个`exit`信号。也可以手动发送一个退出信号来杀死一个进程: @@ -133,7 +133,7 @@ iex> try do 正式这个监控系统使得`try/catch`和`try/rescue`在Elixir中用的这么少。与其挽救一个错误,我们更愿意让他`先失败`,因为监控树会保证我们的应用在错误之后,会重回到一个已知的初始状态。 -## 17.4 之后 +## 19.4 之后 有时的确有必要时候`try/after`来保证一个资源在某些特定的动作之后被清理。例如,我们打开了一个文件并用` try/after`来保证它的关闭。 @@ -148,7 +148,7 @@ iex> try do ** (RuntimeError) oops, something went wrong ``` -## 17.5 变量作用域 +## 19.5 变量作用域 牢记在`try/catch/rescue/after `内部定义的变量并不会泄漏到外部环境中。这是因为`try`块也会会失败,所以变量也许从一开始就是找不到的。换一句话来说,这些代码是非法的: