diff --git a/README.md b/README.md index fb2e7bb..3e06104 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ Quick Reference [Docker](./docs/docker.md) [Dockerfile](./docs/dockerfile.md) [Django](./docs/django.md) +[Elixir](./docs/elixir.md) [Flask](./docs/flask.md) [FastAPI](./docs/fastapi.md) [Flutter](./docs/flutter.md) diff --git a/assets/elixir.svg b/assets/elixir.svg new file mode 100644 index 0000000..871251f --- /dev/null +++ b/assets/elixir.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/elixir.md b/docs/elixir.md new file mode 100644 index 0000000..9c3e7de --- /dev/null +++ b/docs/elixir.md @@ -0,0 +1,487 @@ +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)_