Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitafe6804

Browse files
committed
update comprehens
1 parent11c7f1b commitafe6804

File tree

1 file changed

+111
-52
lines changed

1 file changed

+111
-52
lines changed

‎18-comprehensions.md

Lines changed: 111 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,160 @@
1-
18-列表速构(Comprehension)
1+
18-速构(Comprehension)
22
========
33

4-
>*Comprehensions* 翻译成“速构”不知道贴不贴切,
5-
这参照了《Erlang/OTP in Action》译者的用辞。国内一些Python书(是的,Python也有这个概念)
6-
中翻译为“推导式”、“递推式”。不过这里请不要纠结它翻译成啥,主要是弄明白它是啥。本章
7-
(或者本文)中出现的话,一般翻译成“速构”或是保留英文。
8-
“速构”是函数式语言中常见的概念,它大体上指的是用一套规则(比如从另一个列表,过滤掉一些元素)
9-
来生成元素填充新列表。
4+
>*Comprehensions* 翻译成“速构”是参照了《Erlang/OTP in Action》译者的用辞。
5+
国内一些Python书(是的,Python也有这个概念)中翻译为“推导式”、“递推式”。
6+
这里不用纠结它的翻译,更重要的是需要弄明白它是什么。
7+
8+
>“速构”是函数式语言中常见的概念,指的是定义规则来生成一系列元素填充新的数据集合。
109
这个概念我们在中学的数学课上其实就已经接触过,在大学高数中更为常见:
11-
```{ x | x ∈ N }```,字面上意思是其表示了一个集合,这个集合里所有元素属于自然数N范围
12-
(也就是自然数集合)。
13-
相关知识可见[WIKI](http://en.wikipedia.org/wiki/List_comprehension)
10+
`{ x | x ∈ N }`这个表达式,字面上意思是:这是一个集合,
11+
这个集合里每个元素x符合“x属于自然数N”这个条件。即,用自然数集合的所有元素来构成这个集合。
12+
相关知识可参考[WIKI](http://en.wikipedia.org/wiki/List_comprehension)
13+
14+
Elixir中,使用枚举类型(Enumerable,如列表)来做循环操作是很常见的,
15+
通常还搭配过滤(filtering)和映射(mapping)行为。
16+
速构(comprehensions)就是为此目的诞生的语法糖:把这些常见任务分组,放到特殊的`for`指令中表达出来。
1417

15-
Elixir中,使用枚举类型(如列表)来做循环操作是很常见的。对对象列表进行枚举时,
16-
通常要有选择性地过滤掉其中一些元素,还有可能做一些变换。
17-
列表速构(comprehensions)就是为此目的诞生的语法糖:把这些常见任务分组,
18-
放到特殊的```for```中执行。
18+
例如,我们可以这样,生成原列表中每个元素的平方:
1919

20-
例如,我们可以这样计算列表中每个元素的平方:
2120
```elixir
2221
iex>for n<- [1,2,3,4],do: n* n
2322
[1,4,9,16]
2423
```
25-
注意看,```<-```符号就是模拟自``````的形象。
24+
25+
>注意看,`<-`符号其实是模拟符号``的形象。
2626
这个例子用熟悉(当然,如果你高数课没怎么听那就另当别论)的数学符号表示就是:
27+
2728
```
28-
S = { X^2 | X ∈ [1,4], X ∈ N}
29+
S = { X^2 | X ∈ [1,4], X ∈ N}
2930
```
30-
这个例子用常见的编程语言去理解,基本上类似于foreach...in...什么的。但是更强大。
3131

32-
一个列表速构由三部分组成:生成器,过滤器和收集器。
32+
速构由三部分组成:生成器,过滤器和收集式。
33+
34+
##生成器和过滤器
35+
36+
在上面的例子中,`n <- [1, 2, 3, 4]`就是生成器。
37+
它字面意思上生成了即将要在速构中使用的数值。任何枚举类型(Enumerable)都可以传递给生成器表达式的右端:
3338

34-
##18.2-生成器和过滤器
35-
在上面的例子中,```n <- [1, 2, 3, 4]```就是生成器。
36-
它字面意思上生成了即将要在速构中使用的数值。任何枚举类型都可以传递给生成器表达式的右端:
3739
```elixir
3840
iex>for n<-1..4,do: n* n
3941
[1,4,9,16]
4042
```
41-
这个例子中的生成器是一个__范围__
4243

43-
生成器表达式支持模式匹配,它会忽略所有不匹配的模式。
44-
想象一下如果不用范围而是用一个键值列表,键只有```:good``````:bad```两种,
45-
来计算中间被标记成‘good’的元素的平方:
44+
生成器表达式左操作数支持模式匹配,它会**忽略**所有不匹配的模式。
45+
想象一下如果不用范围而是用一个键值列表作为生成器的数据源,它的键只有`:good``:bad`两种,
46+
我们仅要计算键为‘:good’的元素值的平方:
47+
4648
```elixir
4749
iex> values= [good:1,good:2,bad:3,good:4]
4850
iex>for {:good, n}<- values,do: n* n
4951
[1,4,16]
5052
```
5153

52-
过滤器能过滤掉某些产生的值。例如我们可以只对奇数进行平方运算:
54+
除了使用模式匹配,过滤器也可以用来选择某些特定数值。
55+
例如我们可以只选择3的倍数,而丢弃其它数值:
56+
5357
```elixir
54-
iex>requireInteger
55-
iex>for n<-1..4,Integer.odd?(n),do: n* n
56-
[1,9]
58+
iex>multiple_of_3?=fn(n)->rem(n,3)==0end
59+
iex>for n<-0..5,multiple_of_3?.(n),do: n* n
60+
[0,9]
5761
```
58-
过滤器会保留所有判断结果是非nil或非false的值。
5962

60-
总的来说,速构比直接使用枚举或流模块的函数提供了更精确的表述。
61-
不但如此,速构还接受多个生成器和过滤器。下面就是一个例子,代码接受目录列表,
63+
速构过程会丢弃过滤器表达式结果为`false``nil`的值;其它值都会被保留。
64+
65+
总的来说,速构提供了比直接使用`Enum``Stream`模块的函数更精确的表达。
66+
不但如此,速构还可以接受多个生成器和过滤器。下面就是一个例子,代码接受目录列表,
6267
删除这些目录下的所有文件:
68+
6369
```elixir
6470
for dir<- dirs,
65-
file<-File.ls!(dir),
66-
path=Path.join(dir, file),
67-
File.regular?(path)do
68-
File.rm!(path)
71+
file<-File.ls!(dir),
72+
path=Path.join(dir, file),
73+
File.regular?(path)do
74+
File.stat!(path).size
6975
end
7076
```
7177

72-
需要记住的是,在速构中,变量赋值这种事应在生成器中进行。
73-
因为在过滤器或代码块中的赋值操作不会反映到速构外面去。
78+
多生成器还可以用来生成两个列表的笛卡尔积:
79+
80+
```elixir
81+
iex>for i<- [:a,:b,:c], j<- [1,2],do: {i, j}
82+
[a:1,a:2,b:1,b:2,c:1,c:2]
83+
```
84+
85+
关于多生成器、过滤器的更高级些的例子:计算毕达哥拉斯三元数(Pythagorean triples)。
86+
毕氏三元数一组正整数满足`a * a + b * b = c * c`,让我们在文件`triples.exs`里写这个速构:
87+
88+
```elixir
89+
defmoduleTripledo
90+
defpythagorean(n)when n>0do
91+
for a<-1..n,
92+
b<-1..n,
93+
c<-1..n,
94+
a+ b+ c<= n,
95+
a*a+ b*b== c*c,
96+
do: {a, b, c}
97+
end
98+
end
99+
```
100+
101+
然后,在终端里:
102+
103+
```
104+
iex triple.exs
105+
```
106+
107+
```elixir
108+
iex>Triple.pythagorean(5)
109+
[]
110+
iex>Triple.pythagorean(12)
111+
[{3,4,5}, {4,3,5}]
112+
iex>Triple.pythagorean(48)
113+
[{3,4,5}, {4,3,5}, {5,12,13}, {6,8,10}, {8,6,10}, {8,15,17},
114+
{9,12,15}, {12,5,13}, {12,9,15}, {12,16,20}, {15,8,17}, {16,12,20}]
115+
```
116+
117+
Finally, keep in mind that variable assignments inside the comprehension, be it in generators, filters or inside the block, are not reflected outside of the comprehension.
118+
119+
需要记住的是,在生成器、过滤器或者代码块中赋值的变量,不会暴露到速构外面去。
120+
121+
##比特串生成器
122+
123+
速构也支持比特串作为生成器,这种生成器在处理比特流时非常有用。
124+
下面的例子中,程序接收一个表示像素颜色的二进制串(格式为<<像素1的R值,像素1的G值,像素1的B值,
125+
像素2的R值,像素2的G...>>),把它转换为三元元组的列表:
74126

75-
##18.2-比特串生成器
76-
速构也支持比特串作为生成器,而且这种生成器在组织处理比特串的流时非常有用。
77-
下面的例子中,程序从二进制数据(表示为<<像素1的R值,像素1的G值,像素1的B值,
78-
像素2的R值,像素2的G...>>)中接收一个像素的列表,把它们转换为元组:
79127
```elixir
80128
iex> pixels=<<213,45,132,64,76,32,76,0,0,234,32,15>>
81129
iex>for<<r::8, g::8, b::8<- pixels>>,do: {r, g, b}
82-
[{213,45,132},{64,76,32},{76,0,0},{234,32,15}]
130+
[{213,45,132},{64,76,32},{76,0,0},{234,32,15}]
83131
```
132+
84133
比特串生成器可以和“普通的”枚举类型生成器混合使用,过滤器也是。
85134

86-
##18.3-Into
87-
在上面的例子中,速构返回一个列表作为结果。
88-
但是,通过使用```:into```选项,速构的结果可以插入到一个不同的数据结构中。
89-
例如,你可以使用比特串生成器加上```:into```来轻松地构成无空格字符串:
135+
##`:into`选项
136+
137+
在上面的例子中,速构返回列表作为结果。
138+
但是,通过使用```:into```选项,速构的结果可以插入到不同的数据结构中。
139+
例如,你可以使用比特串生成器加上```:into```来轻松地移除字符串中的空格:
140+
90141
```elixir
91142
iex>for<<c<-" hello world">>, c!=?\s,into:"",do:<<c>>
92143
"helloworld"
93144
```
94145

95-
集合、图以及其他字典类型都可以传递给```:into```选项。总的来说,```:into```接受任何实现了_Collectable_协议的数据结构。
146+
集合、图、其他字典类型都可以传递给`:into`选项。总的来说,`:into`接受任何实现了_Collectable_协议的数据结构。
147+
148+
`:into`选项一个常见的作用是,不用操作键,而改变图中元素的值:
149+
150+
```elixir
151+
iex>for {key, val}<- %{"a"=>1,"b"=>2},into: %{},do: {key, val* val}
152+
%{"a"=>1,"b"=>4}
153+
```
154+
155+
再来一个使用流的例子。因为`IO`模块提供了流(既是Enumerable也是Collectable)。
156+
你可以使用速构实现一个回声终端,让其返回任何输入的字母的大写形式:
96157

97-
例如,IO模块提供了流。流既是Enumerable也是Collectable。
98-
你可以使用速构实现一个回声终端,让其返回任何输入的东西的大写形式:
99158
```elixir
100159
iex> stream=IO.stream(:stdio,:line)
101160
iex>for line<- stream,into: streamdo
@@ -104,4 +163,4 @@ iex> for line <- stream, into: stream do
104163
```
105164

106165
现在在终端中输入任意字符串,你会看到同样的内容以大写形式被打印出来。
107-
不幸的是,这个例子会让你的shell陷入到该速构代码中,只能用Ctrl+C两次来退出。
166+
不幸的是,这个例子会让你的shell陷入到该速构代码中,只能用Ctrl+C两次来退出:-)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp