learnxinyminutes-docs/zh-cn/elixir-cn.html.markdown
2014-03-31 19:49:46 +08:00

398 lines
9.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
language: elixir
contributors:
- ["Joao Marques", "http://github.com/mrshankly"]
- ["lidashuang", "http://github.com/lidashuang"]
filename: learnelixir.ex
---
Elixir 是一门构建在Elang VM 之上的函数式编程语言。Elixir 完全兼容 Eralng,
另外还提供了更标准的语法,特性。
```elixir
# 这是单行注释, 注释以井号开头
# 没有多行注释
# 但你可以堆叠多个注释。
# elixir shell 使用命令 `iex` 进入。
# 编译模块使用 `elixirc` 命令。
# 如果安装正确,这些命令都会在环境变量里
## ---------------------------
## -- 基本类型
## ---------------------------
# 数字
3 # 整型
0x1F # 整型
3.0 # 浮点类型
# 原子(Atoms),以 `:`开头
:hello # atom
# Tuples that are stored contiguously in memory.
{1,2,3} # tuple
# 使用`elem`函数访问元组(tuple)里的元素:
elem({1, 2, 3}, 0) #=> 1
# 列表(list)
[1,2,3] # list
# 可以用下面的方法访问列表的头尾元素:
[head | tail] = [1,2,3]
head #=> 1
tail #=> [2,3]
# 在elixir,就像在Erlang, `=` 表示模式匹配 (pattern matching)
# 不是赋值。
#
# This means that the left-hand side (pattern) is matched against a
# right-hand side.
#
# This is how the above example of accessing the head and tail of a list works.
# 当左右两边不匹配时会返回error, 在这个
# 例子中,元组大小不一样。
# {a, b, c} = {1, 2} #=> ** (MatchError) no match of right hand side value: {1,2}
# 还有二进制类型 (binaries)
<<1,2,3>> # binary
# 字符串(Strings) 和 字符列表(char lists)
"hello" # string
'hello' # char list
# 多行字符串
"""
I'm a multi-line
string.
"""
#=> "I'm a multi-line\nstring.\n"
# 所有的字符串(Strings)以UTF-8编码
"héllò" #=> "héllò"
# 字符串(Strings)本质就是二进制类型(binaries), 字符列表(char lists)本质是列表(lists)
<<?a, ?b, ?c>> #=> "abc"
[?a, ?b, ?c] #=> 'abc'
# 在 elixir中`?a`返回 `a` 的 ASCII 整型值
?a #=> 97
# 合并列表使用 `++`, 对于二进制类型则使用 `<>`
[1,2,3] ++ [4,5] #=> [1,2,3,4,5]
'hello ' ++ 'world' #=> 'hello world'
<<1,2,3>> <> <<4,5>> #=> <<1,2,3,4,5>>
"hello " <> "world" #=> "hello world"
## ---------------------------
## -- 操作符(Operators)
## ---------------------------
# 一些数学运算
1 + 1 #=> 2
10 - 5 #=> 5
5 * 2 #=> 10
10 / 2 #=> 5.0
# 在 elixir 操作符 `/` 返回值总是浮点数。
# 做整数除法使用 `div`
div(10, 2) #=> 5
# 为了得到余数使用 `rem`
rem(10, 3) #=> 1
# 还有 boolean 操作符: `or`, `and` and `not`.
# 第一个参数必须是boolean 类型
true and true #=> true
false or true #=> true
# 1 and true #=> ** (ArgumentError) argument error
# Elixir 也提供了 `||`, `&&` 和 `!` 可以接受任意的类型
# 除了`false` 和 `nil` 其它都会被当作true.
1 || true #=> 1
false && 1 #=> false
nil && 20 #=> nil
!true #=> false
# 比较有: `==`, `!=`, `===`, `!==`, `<=`, `>=`, `<` 和 `>`
1 == 1 #=> true
1 != 1 #=> false
1 < 2 #=> true
# `===` 和 `!==` 在比较整型和浮点类型时更为严格:
1 == 1.0 #=> true
1 === 1.0 #=> false
# 我们也可以比较两种不同的类型:
1 < :hello #=> true
# 总的排序顺序定义如下:
# number < atom < reference < functions < port < pid < tuple < list < bit string
# To quote Joe Armstrong on this: "The actual order is not important,
# but that a total ordering is well defined is important."
## ---------------------------
## -- 控制结构(Control Flow)
## ---------------------------
# `if` 表达式
if false do
"This will never be seen"
else
"This will"
end
# 还有 `unless`
unless true do
"This will never be seen"
else
"This will"
end
# Remember pattern matching? Many control-flow structures in elixir rely on it.
# `case` allows us to compare a value against many patterns:
case {:one, :two} do
{:four, :five} ->
"This won't match"
{:one, x} ->
"This will match and assign `x` to `:two`"
_ ->
"This will match any value"
end
# 模式匹配时,如果不需要某个值,通用的做法是把值 匹配到 `_`
# 例如,我们只需要要列表的头元素:
[head | _] = [1,2,3]
head #=> 1
# 下面的方式效果一样,但可读性更好
[head | _tail] = [:a, :b, :c]
head #=> :a
# `cond` lets us check for many conditions at the same time.
# Use `cond` instead of nesting many `if` expressions.
cond do
1 + 1 == 3 ->
"I will never be seen"
2 * 5 == 12 ->
"Me neither"
1 + 2 == 3 ->
"But I will"
end
# It is common to see a last condition equal to `true`, which will always match.
cond do
1 + 1 == 3 ->
"I will never be seen"
2 * 5 == 12 ->
"Me neither"
true ->
"But I will (this is essentially an else)"
end
# `try/catch` is used to catch values that are thrown, it also supports an
# `after` clause that is invoked whether or not a value is catched.
try do
throw(:hello)
catch
message -> "Got #{message}."
after
IO.puts("I'm the after clause.")
end
#=> I'm the after clause
# "Got :hello"
## ---------------------------
## -- 模块和函数(Modules and Functions)
## ---------------------------
# 匿名函数 (注意点)
square = fn(x) -> x * x end
square.(5) #=> 25
# They also accept many clauses and guards.
# Guards let you fine tune pattern matching,
# they are indicated by the `when` keyword:
f = fn
x, y when x > 0 -> x + y
x, y -> x * y
end
f.(1, 3) #=> 4
f.(-1, 3) #=> -3
# Elixir 提供了很多内建函数
# 在默认作用域都是可用的
is_number(10) #=> true
is_list("hello") #=> false
elem({1,2,3}, 0) #=> 1
# 你可以在一个模块里定义多个函数,定义函数使用 `def`
defmodule Math do
def sum(a, b) do
a + b
end
def square(x) do
x * x
end
end
Math.sum(1, 2) #=> 3
Math.square(3) #=> 9
# 保存到 `math.ex`,使用 `elixirc` 编译你的 Math 模块
# 在终端里: elixirc math.ex
# 在模块中可以使用`def`定义函数,使用 `defp` 定义私有函数
# 使用`def` 定义的函数可以被其它模块调用
# 私有函数只能在本模块内调用
defmodule PrivateMath do
def sum(a, b) do
do_sum(a, b)
end
defp do_sum(a, b) do
a + b
end
end
PrivateMath.sum(1, 2) #=> 3
# PrivateMath.do_sum(1, 2) #=> ** (UndefinedFunctionError)
# Function declarations also support guards and multiple clauses:
defmodule Geometry do
def area({:rectangle, w, h}) do
w * h
end
def area({:circle, r}) when is_number(r) do
3.14 * r * r
end
end
Geometry.area({:rectangle, 2, 3}) #=> 6
Geometry.area({:circle, 3}) #=> 28.25999999999999801048
# Geometry.area({:circle, "not_a_number"})
#=> ** (FunctionClauseError) no function clause matching in Geometry.area/1
#由于不变性递归是Elixir的重要组成部分
defmodule Recursion do
def sum_list([head | tail], acc) do
sum_list(tail, acc + head)
end
def sum_list([], acc) do
acc
end
end
Recursion.sum_list([1,2,3], 0) #=> 6
# Elixir modules support attributes, there are built-in attributes and you
# may also add custom attributes.
defmodule MyMod do
@moduledoc """
This is a built-in attribute on a example module.
"""
@my_data 100 # This is a custom attribute.
IO.inspect(@my_data) #=> 100
end
## ---------------------------
## -- Records and Exceptions
## ---------------------------
# Records are basically structures that allow you to associate a name with
# a particular value.
defrecord Person, name: nil, age: 0, height: 0
joe_info = Person.new(name: "Joe", age: 30, height: 180)
#=> Person[name: "Joe", age: 30, height: 180]
# 访问name的值
joe_info.name #=> "Joe"
# Update the value of age
joe_info = joe_info.age(31) #=> Person[name: "Joe", age: 31, height: 180]
# The `try` block with the `rescue` keyword is used to handle exceptions
try do
raise "some error"
rescue
RuntimeError -> "rescued a runtime error"
_error -> "this will rescue any error"
end
# All exceptions have a message
try do
raise "some error"
rescue
x in [RuntimeError] ->
x.message
end
## ---------------------------
## -- 并发(Concurrency)
## ---------------------------
# Elixir relies on the actor model for concurrency. All we need to write
# concurrent programs in elixir are three primitives: spawning processes,
# sending messages and receiving messages.
# To start a new process we use the `spawn` function, which takes a function
# as argument.
f = fn -> 2 * 2 end #=> #Function<erl_eval.20.80484245>
spawn(f) #=> #PID<0.40.0>
# `spawn` returns a pid (process identifier), you can use this pid to send
# messages to the process. To do message passing we use the `<-` operator.
# For all of this to be useful we need to be able to receive messages. This is
# achived with the `receive` mechanism:
defmodule Geometry do
def area_loop do
receive do
{:rectangle, w, h} ->
IO.puts("Area = #{w * h}")
area_loop()
{:circle, r} ->
IO.puts("Area = #{3.14 * r * r}")
area_loop()
end
end
end
# Compile the module and create a process that evaluates `area_loop` in the shell
pid = spawn(fn -> Geometry.area_loop() end) #=> #PID<0.40.0>
# Send a message to `pid` that will match a pattern in the receive statement
pid <- {:rectangle, 2, 3}
#=> Area = 6
# {:rectangle,2,3}
pid <- {:circle, 2}
#=> Area = 12.56000000000000049738
# {:circle,2}
# shell也是一个进程(process), 你可以使用`self`获取当前 pid
self() #=> #PID<0.27.0>
```
## References
* [Getting started guide](http://elixir-lang.org/getting_started/1.html) from [elixir webpage](http://elixir-lang.org)
* [Elixir Documentation](http://elixir-lang.org/docs/master/)
* ["Learn You Some Erlang for Great Good!"](http://learnyousomeerlang.com/) by Fred Hebert
* "Programming Erlang: Software for a Concurrent World" by Joe Armstrong