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行。这意味着流对于处理大型文件甚至是网络资源等慢速资源非常有用。

资源

Date: 2024-08-24 Sat 13:01