Files
reference/docs/elixir.md
gowshwah 8d60181b96 feat: add docs/elixir.md (#587)
Co-authored-by: gowshwah <1099764281@qq.com>
2024-04-02 15:16:48 +08:00

488 lines
8.9 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.

Elixir 备忘清单
===
提供基本语法和方法的 Elixir 快速参考备忘单。
入门
------
### 安装Elixir
> 每个操作系统的安装说明可以在 elixir-lang.org 网站上 [Installing Elixir](http://elixir-lang.org/install.html) 部分找到。
> Elixir 自带了`iex`这样一个交互 shell可以随时计算 Elixir 表达式的值,运行`iex`命令,继续输入几个简单的表达式试试:
```shell
iex 2+3
5
iex 2+3 == 5
true
iex String.length("The quick brown fox jumps over the lazy dog")
43
```
### hello.exs
```elixir
IO.puts("Hello world from Elixir")
```
Elixir 运行命令
```shell
$ elixir hello.exs
```
### 基本类型
>
> Elixir 支持多种基本类型:整数、浮点、布尔值、原子和字符串。其他数据类型,如列表和元组
```elixir
iex> 1 # integer
iex> 0x1F # integer支持二进制、八进制和十六进制的整数
iex> 1.0 # float
iex> true # boolean
iex> :atom # atom / symbol
iex> "elixir" # string
iex> [1, 2, 3] # list
iex> {1, 2, 3} # tuple
```
### 基本算术
```elixir
iex> 1 + 2
3
iex> 5 * 5
25
iex> 10 / 2
5.0
```
> 运算符`/`总是返回一个float。如果你想做整数除法或得到除法余数你可以调用div和rem函数:
```elixir
iex> div(10, 2)
5
iex> div 10, 2 # Elixir允许在调用需要一个或多个参数的函数时删除括号
5
iex> rem 10, 3
1
```
> 可以调用round函数来获取与给定浮点数最接近的整数或者调用trunc函数来获取浮点数的整数部分
```elixir
iex> round(3.58)
4
iex> trunc(3.58)
3
```
> 可以使用is_integer、is_float或is_number分别检查参数是否为integer、float或number类型
```elixir
iex> is_integer(1)
true
iex> is_float(2.0)
true
iex> is_number(2.0)
false
```
### 布尔算术
>
> Elixir 提供了 `||`、`&&` 和 `!` 布尔操作符,它们支持任何类型的操作:
```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:
```elixir
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
```
### 比较运算符
>
> 比较运算符 `==`, `!=`, `===`, `!==`, `<=`, `>=`, `<` 和 `>`
```elixir
iex> 1 > 2
false
iex> 1 != 2
true
iex> 2 == 2
true
iex> 2 <= 3
true
```
### 字符串插值与拼接
```elixir
iex> name = "Sean"
iex> "Hello #{name}"
"Hello Sean"
iex> "Hello " <> "world!"
"Hello world!"
```
集合
------
>
> 列表list、元组tuple、关键字列表keyword list、映射map
>
### 列表List
```elixir
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
```elixir
iex> {3.14, :pie, "Apple"}
{3.14, :pie, "Apple"}
```
### 关键字列表Keyword List
```elixir
iex> [foo: "bar", hello: "world"]
[foo: "bar", hello: "world"]
iex> [{:foo, "bar"}, {:hello, "world"}]
[foo: "bar", hello: "world"]
```
> 关键字列表非常重要,它有以下的特性:
-key都是原子atom
-key是有序的定义后顺序不会改变
-key不必是唯一的
> 因为这些原因,关键字列表最常见的用法是作为参数传递给函数。
### 映射Map
> Elixir 的映射maps是键值对结构的第一选择和关键字列表keywords不同映射允许任意类型的数据作为键而且数据并不严格排序。 你可以使用 %{} 来定义映射:
```elixir
iex> map = %{:foo => "bar", "hello" => :world}
%{:foo => "bar", "hello" => :world}
iex> map[:foo]
"bar"
iex> map["hello"]
:world
```
模式匹配
------
>
> 模式匹配是 Elixir 很强大的特性,它允许我们匹配简单值、数据结构、甚至函数。
>
### 匹配操作符
```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 操作符,就是用已经绑定的值去匹配,而不是重新绑定一个新值。
```elixir
iex> {x, ^x} = {2, 1}
{2, 1}
iex> x
2
# 使用下划线_忽略匹配的值
iex> [head | _] = [1, 2, 3]
[1, 2, 3]
iex> head
1
```
控制语句
------
>
> case, cond, and if
>
### case
>
> case允许我们将一个值与许多模式进行比较直到找到匹配的模式
```elixir
iex> case {1, 2, 3} do
{4, 5, 6} ->
"This clause won't match"
{1, x, 3} ->
"This clause will match and bind x to 2 in this clause"
_ ->
"This clause would match any value"
end
"This clause will match and bind x to 2 in this clause"
# 还可以使用when指定额外的条件
iex> case {1, 2, 3} do
{1, x, 3} when x > 0 ->
"Will match"
_ ->
"Would match, if guard condition were not satisfied"
end
"Will match"
```
### cond
> 当我们需要匹配条件而不是值的时候,可以使用 cond这和其他语言的 else if 或者 elsif 相似
```elixir
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 ->
"This is always true (equivalent to else)"
end
"This is always true (equivalent to else)"
```
### if/unless
```elixir
iex> if true do
"This works!"
end
"This works!"
iex> unless true do
"This will never be seen"
end
nil
# if...else...
iex> if nil do
"This won't be seen"
else
"This will"
end
"This will"
# 关于Elixir中变量的作用域如果在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
```
函数
------
### 匿名函数
>
> 使用 fn 和 end 关键字来定义匿名函数,在这两者之间,可以定义任意数量的参数和函数体,它们用 -> 分隔开。
```elixir
iex> sum = fn (a, b) -> a + b end
iex> sum.(2, 3)
5
# 可以使用 & 语法来简化匿名函数的定义:
iex> sum = &(&1 + &2)
iex> sum.(2, 3)
5
```
### 闭包
>
> 匿名函数去引用外部的变量,这通常被称为闭包。
```elixir
iex> double = fn a -> add.(a, a) end
#Function<6.71889879/1 in :erl_eval.expr/5>
double.(2)
4
```
> 闭包与守卫
```elixir
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 关键字定义在某个模块中
```elixir
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 phrase, do: "Hello, " # 使用defp来定义私有函数
end
```
> 函数的默认参数:使用`\\`来定义默认参数
```elixir
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 等。
```elixir
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模块
```elixir
iex> 1..100_000 |> Stream.map(&(&1 * 3)) |> Stream.filter(odd?) |> Enum.sum()
7500000000
```
> 流文件操作
```elixir
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行。这意味着流对于处理大型文件甚至是网络资源等慢速资源非常有用。
```
另见
----
- [Elixir 官方](https://elixir-lang.org/) _(elixir-lang.org)_
- [Elixir School](https://elixirschool.com/) _(elixirschool.com)_