Elixir速记备忘录
Table of Contents
基本类型
- Elixir 支持多种基本类型:整数、浮点、布尔值、原子和字符串。其他数据类型,如列表和元组。
# integer 整数 iex> 1 # integer(支持2进制、8进制和16进制的整数) iex> 0x1F # float iex> 1.0 # boolean iex> true # atom / symbol iex> :atom # string iex> "elixir" # list iex> [1, 2, 3] # tuple iex> {1, 2, 3} # 原子是常量,它们的名称就是它们的值 iex> :ok # 列表(Lists) iex> list = [1, 2, 3] # 元组(Tuples) iex> tuple = {:ok, "value"}
字符串插值与拼接
iex> name = "Sean" iex> "Hello #{name}" "Hello Sean" iex> "Hello " <> "world!" "Hello world!"
变量和模式匹配
- 在 Elixir 中,使用 = 来进行赋值操作,但实际上是模式匹配。左边是模式,右边是值
x = 1 # => x 现在等于 1 {a, b} = {1, 2} # => a 等于 1,b 等于 2
函数定义
defmodule MyModule do def my_function(parameter) do # 函数体 end end
匿名函数
add = fn a, b -> a + b end
控制结构
# if/else if x > 0 do "Positive" else "Non-positive" end # case case {1, 2} do {1, x} -> "Matched #{x}" _ -> "Not matched" end # cond cond do x > 2 -> "Greater than 2" x == 2 -> "Equal to 2" true -> "Less than 2" end
基本算术
iex> 1 + 2 3 iex> 5 * 5 25 iex> 10 / 2 5.0 # 运算符/总是返回一个 float。如果你想做整数除法或得到除法余数,你可以调用 div 和 rem 函数: iex> div(10, 2) 5 # 允许在调用需要一个或多个参数的函数时删除括号 iex> div 10, 2 5 iex> rem 10, 3 1 # 可以调用 round 函数来获取与给定浮点数最接近的整数,或者调用 trunc 函数来获取浮点数的整数部分 iex> round(3.58) 4 iex> trunc(3.58) 3 # 可以使用 is_integer、is_float 或 is_number 分别检查参数是否为 integer、float 或 number 类型 iex> is_integer(1) true iex> is_float(2.0) true iex> is_number(2.0) false
布尔算术
- Elixir 提供了 ||、&& 和 ! 布尔操作符,它们支持任何类型的操作:
iex> -20 || true -20 iex> false || 42 42 iex> 42 && true true iex> 42 && nil nil iex> !42 false iex> !false true # 还有三个操作符(and、or、not),它们的第一个参数必须是布尔类型(true 和 false): iex> true and 42 42 iex> false or true true iex> not false true iex> 42 and true # ** (ArgumentError) argument error: 42 iex> not 42 # ** (ArgumentError) argument error
模块和函数导入
import List, only: [duplicate: 2]
管道操作符
result = data |> process1() |> process2()
比较运算符
iex> 1 > 2 false iex> 1 != 2 true iex> 2 == 2 true iex> 2 <= 3 true
集合
列表(list)
iex> [3.14, :pie, "Apple"] [3.14, :pie, "Apple"] iex> list = [3.14, :pie, "Apple"] iex> [3.14, :pie, "Apple"] # 列表的开头添加元素 iex> ["π" | list] ["π", 3.14, :pie, "Apple"] # 列表的尾部添加元素/列表拼接 iex> list ++ ["Cherry"] [3.14, :pie, "Apple", "Cherry"] # 获取列表的头部元素 iex> hd [3.14, :pie, "Apple"] 3.14 # 获取列表的尾部元素 iex> tl [3.14, :pie, "Apple"] [:pie, "Apple"]
元组(tuple)
iex> {3.14, :pie, "Apple"} {3.14, :pie, "Apple"}
关键字列表(keyword list)
关键字列表非常重要,它有以下的特性:
- 键(key)都是原子(atom)
- 键(key)是有序的(定义后,顺序不会改变)
- 键(key)不必是唯一的
- 因为这些原因,常见的用法是作为参数传递给函数
iex> [foo: "bar", hello: "world"] [foo: "bar", hello: "world"] iex> [{:foo, "bar"}, {:hello, "world"}] [foo: "bar", hello: "world"]
映射(map)
- Elixir 的映射(maps)是键值对结构的第一选择;
- 和关键字列表(keywords)不同,映射允许任意类型的数据作为键,而且数据并不严格排序;
- 使用 %{} 来定义映射;
iex> map = %{:foo => "bar", "hello" => :world} %{:foo => "bar", "hello" => :world} iex> map[:foo] "bar" iex> map["hello"] :world iex>
模式匹配
- 模式匹配是 Elixir 很强大的特性,它允许我们匹配简单值、数据结构、甚至函数。
匹配元组
iex> {a, b, c} = {:hello, "world", 42} {:hello, "world", 42} iex> a :hello iex> b "world"
匹配列表
iex> [a, b, c] = [1, 2, 3] [1, 2, 3] iex> a 1
匹配列表的头部元素
iex> [head | tail] = [1, 2, 3] [1, 2, 3] iex> head 1 iex> tail [2, 3]
Pin 操作符
- pin 操作符,就是用已经绑定的值去匹配,而不是重新绑定一个新值。
iex> {x, ^x} = {2, 1} {2, 1} iex> x 2 # 使用下划线 _ 忽略匹配的值 iex> [head | _] = [1, 2, 3] [1, 2, 3] iex> head 1
控制语句
if/else/end
if condition do # 条件成立时执行的代码 else # 条件不成立时执行的代码 end
case/end
case expression do pattern1 -> # 匹配 pattern1 时执行的代码 pattern2 -> # 匹配 pattern2 时执行的代码 _ -> # 其他情况执行的代码 end
cond/end
cond do condition1 -> # 条件1成立时执行的代码 condition2 -> # 条件2成立时执行的代码 true -> # 如果没有任何条件成立,执行这里的代码 end
unless/do/end
unless condition do # 条件为假时执行的代码 end
try/rescue/end
try do # 可能会引发异常的代码 rescue pattern1 -> # 匹配 pattern1 的异常处理代码 pattern2 -> # 匹配 pattern2 的异常处理代码 _ -> # 其他异常处理代码 end
case
- 允许将一个值与许多模式进行比较,直到找到匹配的模式:
iex> case {1, 2, 3} do {4, 5, 6} -> "This clause won't match" {1, x, 3} -> "该子句将匹配并绑定 x 到该子句中的 2" _ -> "This clause would match any value" end "该子句将匹配并绑定 x 到该子句中的 2"
- 还可以使用when指定额外的条件
iex> case {1, 2, 3} do {1, x, 3} when x > 0 -> "Will match" _ -> "如果不满足保护条件,将匹配" end "Will match"
cond
- 当我们需要根据条件进行匹配而不是值时,类似于其他语言的 else if 或 elsif,可以使用 cond 控制结构。
iex> cond do 2 + 2 == 5 -> "This will not be true" 2 * 2 == 3 -> "Nor this" 1 + 1 == 2 -> "But this will" end "But this will"
如果所有的条件都返回 nil 或 false,则会引发一个错误(CondClauseError)。因此,需要添加一个 final 条件,等于 true,它将始终匹配:
iex> cond do 2 + 2 == 5 -> "This is never true" 2 * 2 == 3 -> "Nor this" true -> "这始终为真(等同于 else)" end "这始终为真(等同于 else)"
变量的作用域
- 如果在if、case和类似的构造中声明或更改了任何变量,则声明和更改将只在构造中可见。
iex> x = 1 1 if true do x = x + 1 end 2 iex> x 1 # 如果要更改值,则必须从if返回值: iex> x = 1 1 iex> x = if true do x + 1 else x end 2
函数
函数定义
def function_name(param1, param2) do # 函数体 end # 示例: def sum(a, b) do a + b end
模式匹配的多个函数定义
def fun_name(:atom) do # 对于 :atom 的处理 end def fun_name("string") do # 对于 "string" 的处理 end def fun_name(number) when is_integer(number) do # 对于整数的处理 end # 示例: def is_positive(number) when number > 0 do true end def is_positive(_), do: false
函数调用
module_name.function_name(arg1, arg2) # 示例: Enum.map([1, 2, 3], &(&1 * 2))
函数参数默认值
def function_name(parameter \\ 默认值) do # 函数体 end # 示例: def greet(name \\ "World") do "Hello, #{name}!" end
可变参数数量
def function_name(param1, param2 \\ []) do # 函数体 end # 示例: def sum(numbers) do Enum.sum(numbers) end
函数文档注释
@doc """ 这是函数的文档。 """ 示例: @doc """ Adds two numbers together. ## 示例 iex> MyModule.add(1, 2) 3 """ def add(a, b) do a + b end
匿名函数
iex> sum = fn (a, b) -> a + b end iex> sum.(2, 3) 5 可以使用 & 语法来简化匿名函数的定义: iex> sum = &(&1 + &2) iex> sum.(2, 3) 5
闭包
- 匿名函数去引用外部的变量,这通常被称为闭包。
iex> double = fn a -> add.(a, a) end #Function<6.71889879/1 in :erl_eval.expr/5> double.(2) 4
闭包与守卫
iex> f = fn x, y when x > 0 -> x + y x, y -> x * y end iex> f.(1, 3) 4 iex> f.(-1, 3) -3
命名函数
- 命名函数是通过 def 关键字定义在某个模块中
defmodule Greeter do def hello(name) do "Hello, " <> name end end iex> Greeter.hello("Sean") "Hello, Sean" # 简写为一行: defmodule Greeter do def hello(name), do: "Hello, " <> name end
私有函数
defmodule Greeter do def hello(name), do: phrase <> name # 使用defp来定义私有函数 defp phrase, do: "Hello, " end # 函数的默认参数:使用 \\ 来定义默认参数 defmodule Greeter do def hello(name, language_code \\ "en") do phrase(language_code) <> name end defp phrase("en"), do: "Hello, " defp phrase("es"), do: "Hola, " end
Enumerables 与 Streams
- Elixir 提供了 Enum 和 Stream 两个模块,用于处理集合。
Enum
- Enum 模块提供了对集合的常用操作,如 map、filter、reduce、sort、chunk、join、into 等。
iex> Enum.map([1, 2, 3], fn x -> x * 2 end) [2, 4, 6] iex> Enum.map(%{1 => 2, 3 => 4}, fn {k, v} -> k * v end) [2, 12]
Stream
- 作为Enum的替代品,Elixir提供了支持懒惰操作的Stream模块
iex> 1..100_000 |> Stream.map(&(&1 * 3)) |> Stream.filter(odd?) |> Enum.sum() 7500000000 # 流文件操作 iex> stream = File.stream!("path/to/file") %File.Stream{ line_or_bytes: :line, modes: [:raw, :read_ahead, :binary], path: "path/to/file", raw: true } Enum.take(stream, 10)
- 上面的例子将提取所选文件的前10行。这意味着流对于处理大型文件甚至是网络资源等慢速资源非常有用。