Kernel
提供Elixir导入到您的环境中的默认宏和函数。
这些宏和函数可以跳过或通过import/2
宏。例如,如果您想告诉Elixir不要导入if/2
宏,您可以:
import Kernel, except: [if: 2]
Elixir也有特殊的形式,总是进口,不能跳过。这些描述在Kernel.SpecialForms
。
本模块中描述的一些功能由Elixir编译器内联到erlang模块中的Erlang对应内容中。 这些函数在Erlang-land中称为BIF(内置内部函数),并且它们展现出有趣的属性,因为它们中的一些被允许在守卫中,另一些被用于编译器优化。
捕捉功能时,大部分内联函数都可以看到效果:
iex> &Kernel.is_atom/1
&:erlang.is_atom/1
这些函数将在文档中明确标记为“由编译器内联”。
功能
!value
Boolean not
left != right
如果这两项不相等,返回true
left !== right
如果这两项不完全相等,返回true
left && right
提供一个短路运算符,仅当第一个表达式评估为true
(即既不是nil
也不是false
)时才评估并返回第二个表达式。否则返回第一个表达式
left * right
算术乘法
+值
算术一元加
left + right
算术加法
left ++ right
列出一个适当的列表和一个术语,并返回一个列表。
-value
算术一元减
left - right
算术减法
left -- right
移除右侧每个项目的左列表中项的第一个匹配项。
first..last
返回具有指定值的范围。first
和last
整数
left / right
算术划分
left < right
如果左小于右,返回true
left <= right
如果左小于或等于右,返回true
left <> right
连接两个二进制文件
left == right
如果两项相等,返回true
left === right
如果这两项完全相等,返回true
left =~ right
将左侧的术语与右侧的正则表达式或字符串匹配。
left > right
如果左多于右,返回true
left >= right
如果左大于或等于右,返回true
@expr
读取和写入当前模块的属性。
abs(number)
返回整数或浮点数,该整数或浮点数是number
alias!(alias)
当在引用中使用时,标记指定的别名不应该卫生。这意味着在展开宏时将展开别名。
left and right
Boolean and
apply(fun, args)
调用给定的fun
与参数列表args
apply(module, fun, args)
用参数args列表从模块中调用给定的fun
binary_part(binary, start, length)
从长度开始提取二进制文件的一部分。 二进制文件是零索引的
binding(context \ nil)
将给定上下文的绑定作为关键字列表返回。
bit_size(bitstring)
返回一个整数,其大小是bitstring的大小
byte_size(bitstring)
返回包含bitstring所需的字节数
def(call, expr \ nil)
定义具有给定名称和正文的函数。
defdelegate(funs, opts)
定义委托给另一个模块的函数。
defexception(fields)
定义异常
defimpl(name, opts, do_block \ [])
定义给定协议的实现。
defmacro(call, expr \ nil)
定义具有给定名称和正文的宏。
defmacrop(call, expr \ nil)
定义具有给定名称和正文的私有宏。
defmodule(alias, do_block)
用给定的内容定义按名称指定的模块。
defoverridable(keywords_or_behaviour)
使当前模块中的给定函数可重写。
defp(call, expr \ nil)
定义具有给定名称和正文的私有函数。
defprotocol(name, do_block)
定义协议
defstruct(fields)
定义结构
destructure(left, right)
构造两个列表,将右边的每个项分配给左侧的匹配项。
div(dividend, divisor)
执行整数除法
elem(tuple, index)
获取元组中从零开始索引处的元素
exit(reason)
在给定的原因下停止调用进程的执行。
function_exported?(module, function, arity)
如果模块已加载并且包含具有给定元素的公共函数,则返回true,否则返回false
get_and_update_in(路径,函数)
获取值并通过给定值更新嵌套的数据结构 path
get_and_update_in(数据,键,函数)
获取一个值并更新嵌套结构。
get_in(数据,键)
获取嵌套结构的值。
hd(list)
返回列表的头部。如果列表为空,则引发ArgumentError
if(condition, clauses)
提供一个if/2
宏
左转右
检查左侧的元素是否为右侧集合的成员。
inspect(term, opts \ [])
根据Inspect
协议。第二个参数是带有控制检查选项的关键字列表。
is_atom(term)
如果term是一个原子,则返回true; 否则返回false
is_binary(term)
如果term是二进制,则返回true; 否则返回false
is_bitstring(term)
如果term是一个位串(包括二进制),则返回true; 否则返回false
is_boolean(term)
如果term是原子true或原子false,则返回true(即,布尔值); 否则返回false
is_float(term)
如果term是一个浮点数,则返回true; 否则返回false
is_function(term)
回报true
如果term
是一个函数,否则返回false
is_function(term, arity)
如果term是一个可以用参数数量来应用的函数,则返回true; 否则返回false
is_integer(term)
如果term是一个整数,则返回true; 否则返回false
is_list(term)
如果term是包含零个或多个元素的列表,则返回true; 否则返回false
is_map(term)
如果term是map,则返回true; 否则返回false
is_nil(term)
如果term是零,则返回true,否则返回false
is_number(term)
如果term是整数或浮点数,则返回true; 否则返回false
is_pid(term)
如果term是一个PID(进程标识符),则返回true; 否则返回false
is_port(term)
如果term是一个端口标识符,则返回true; 否则返回false
is_reference(term)
如果term是引用,则返回true; 否则返回false
is_tuple(term)
如果term是一个元组,则返回true; 否则返回false
length(list)
返回list
macro_exported?(module, macro, arity)
如果模块已加载并且包含具有给定元素的公共宏,则返回true,否则返回false
make_ref()
返回几乎唯一的引用。
map_size(map)
返回地图的大小。
match?(pattern, expr)
一个方便的宏,用于检查右侧(表达式)是否匹配左侧(一个模式)
max(first, second)
根据Erlang的项排序返回两个给定项中的最大项
min(first, second)
根据Erlang项的顺序返回两个给定项中最小的一个
node()
返回表示本地节点名称的原子。如果节点不活动,则返回:nonode@nohost
node(arg)
返回给定参数所在的节点。参数可以是PID、引用或端口。如果本地节点没有活动,:nonode@nohost
返回
非价值
布尔非
左或右
布尔或
pop_in(path)
从嵌套结构中弹出密钥path
pop_in(data, keys)
从给定的嵌套结构中弹出密钥。
put_elem(tuple, index, value)
在元组中插入给定的从零开始的索引值
put_in(path, value)
通过给定的path
put_in(data, keys, value)
将值放入嵌套结构中。
raise(message)
引发异常
raise(exception, attributes)
引发异常
rem(dividend, divisor)
计算整数除法的剩余部分。
reraise(message, stacktrace)
引发保存以前堆栈跟踪的异常。
reraise(exception, attributes, stacktrace)
引发保存以前堆栈跟踪的异常。
round(number)
将数字舍入到最近的整数。
self()
返回调用进程的PID(进程标识符)
send(dest, message)
向给定的dest
并返回消息
sigil_C(term, modifiers)
处理信号~C
sigil_D(date, modifiers)
处理~D
日期的印记
sigil_N(date, modifiers)
处理~N
单纯时间的印记
sigil_R(term, modifiers)
处理信号~R
sigil_S(term, modifiers)
处理信号~S
sigil_T(date, modifiers)
处理信号~T
几次
sigil_W(term, modifiers)
处理信号~W
sigil_c(term, modifiers)
处理信号~c
sigil_r(term, modifiers)
处理信号~r
sigil_s(term, modifiers)
处理信号~s
sigil_w(term, modifiers)
处理信号~w
spawn(fun)
生成给定函数并返回其PID。
spawn(module, fun, args)
生成给定函数fun
从给定的module
把它传递给args
并返回它的PID
spawn_link(fun)
生成给定的函数,将其链接到当前进程,并返回其PID。
spawn_link(module, fun, args)
生成给定函数fun
从给定的module
把它传递给args
,将其链接到当前进程,并返回其PID。
spawn_monitor(fun)
生成给定的函数,监视它,并返回它的PID和监视引用。
spawn_monitor(module, fun, args)
生成传递给定args的给定模块和函数,监视它,并返回它的PID和监视引用。
struct(struct, fields \ [])
创建和更新结构
struct!(struct, fields \ [])
类似于struct/2
但检查关键有效性
throw(term)
来自函数的非本地返回
返回列表的尾部。提高ArgumentError
如果列表为空
返回列表的尾部。如果列表为空,则引发ArgumentError
to_charlist(term)
根据List.Chars
协议将给定的术语转换为charlist
to_string(term)
根据String.Chars
协议将参数转换为字符串
trunc(number)
返回 number
整数部分
tuple_size(tuple)
返回元组的大小
unless(condition, clauses)
提供一个unless
宏
update_in(path, fun)
更新嵌套结构中的键
update_in(data, keys, fun)
在当前上下文中使用给定模块。
use(module, opts \ [])
在当前上下文中使用给定的模块
var!(var, context \ nil)
在引用中使用时,标记给定的变量不应卫生
left |> right
left || right
提供一个短路运算符,仅当第一个表达式不计算为true
(即,它是nil
或false
)时才评估并返回第二个表达式。否则返回第一个表达式
!值(宏)
不是布尔值。
接收任何参数(不只是布尔值)并返回true
参数为false
或nil
; 否则返回false
。
警卫条款不允许。
例子
iex> !Enum.empty?([])
false
iex> !List.first([])
true
left != right
term != term :: boolean
如果两个项目不相等则返回true
。
该运算符认为1和1.0是相等的。为了比较比较,请改用!==
。
Elixir中的所有术语都可以相互比较。
允许在警卫测试。由编译器内联。
例子
iex> 1 != 2
true
iex> 1 != 1.0
false
left !== right
term !== term :: boolean
如果两个项目不完全相等则返回true
。
Elixir中的所有术语都可以相互比较。
允许在警卫测试。由编译器内联。
例子
iex> 1 !== 2
true
iex> 1 !== 1.0
true
left && right (macro)
提供一个短路运算符,仅当第一个表达式的值为真时(即,既不为零也不为假)评估并返回第二个表达式。 否则返回第一个表达式。
警卫条款不允许。
例子
iex> Enum.empty?([]) && Enum.empty?([])
true
iex> List.first([]) && true
nil
iex> Enum.empty?([]) && List.first([1])
1
iex> false && throw(:bad)
false
请注意,与and/2
此不同,此运算符接受任何表达式作为第一个参数,而不仅仅是布尔值。
left * right
float * integer :: float
整数* float :: float
float * float :: float
integer * integer :: integer
算术乘法。
允许在警卫测试。由编译器内联。
例子
iex> 1 * 2
2
+值
+value :: value when value: number
算术一元加。
允许在警卫测试。由编译器内联。
例子
iex> +1
1
向左+向右
float + integer :: float
integer + float :: float
float + float :: float
integer + integer :: integer
算术加法。
允许在警卫测试。由编译器内联。
例子
iex> 1 + 2
3
左++右
list ++ term :: maybe_improper_list
连接一个正确的列表和一个术语,返回一个列表。
a ++ b的复杂度与长度(a)成正比,因此避免重复附加到任意长度的列表,例如 列表++ [item]。 相反,请考虑通过[item |预先考虑 休息]然后倒车。
如果right
操作数不是一个正确的列表,它将返回一个不正确的列表。如果left
操作数不是一个适当的列表,它会引发ArgumentError
。
由编译器内联。
例子
iex> [1] ++ [2, 3]
[1, 2, 3]
iex> 'foo' ++ 'bar'
'foobar'
# returns an improper list
iex> [1] ++ 2
[1 | 2]
# returns a proper list
iex> [1] ++ [2]
[1, 2]
# improper list on the right will return an improper list
iex> [1] ++ [2 | 3]
[1, 2 | 3]
-值
-float :: float
-neg_integer :: pos_integer
-pos_integer :: neg_integer
-0 :: 0
算术一元减号。
允许在警卫测试。由编译器内联。
例子
iex> -2
-2
left - right
float - integer :: float
integer - float :: float
float - float :: float
integer - integer :: integer
算术减法。
允许在警卫测试。由编译器内联。
例子
iex> 1 - 2
-1
left -- right
list -- list :: list
删除右侧每个项目左侧列表中第一次出现的项目。
在复杂a -- b
成正比length(a) * length(b)
,这意味着它会很慢,如果双方a
和b
很长的列表。在这种情况下,请考虑将每个列表转换为MapSet
并使用MapSet.difference/2
。
由编译器内联。
例子
iex> [1, 2, 3] -- [1, 2]
[3]
iex> [1, 2, 3, 2, 1] -- [1, 2, 2]
[3, 1]
first..last(宏)
返回具有指定first
和last
整数的范围。
如果最后一个比第一个大,则范围将从第一个到最后一个增加。如果第一个比上一个大,则范围将从第一个到最后一个减少。如果第一个等于最后一个,则范围将包含一个元素,它是数字本身。
例子
iex> 0 in 1..3
false
iex> 1 in 1..3
true
iex> 2 in 1..3
true
iex> 3 in 1..3
true
left / right
number / number :: float
算术分割。
结果总是一个浮动。使用div/2
,rem/2
如果你想要一个整数除法或余数。
如果right为0或0.0,则引发ArithmeticError。
允许在警卫测试。由编译器内联。
例子
1 / 2
#=> 0.5
-3.0 / 2.0
#=> -1.5
5 / 1
#=> 5.0
7 / 0
#=> **(ArithmeticError)算术表达式中的错误参数
left < right
term < term :: boolean
如果left小于right,则返回true
。
Elixir中的所有术语都可以相互比较。
允许在警卫测试。由编译器内联。
例子
iex> 1 < 2
true
left <= right
term <= term :: boolean
如果left小于或等于right,则返回true
。
Elixir中的所有术语都可以相互比较。
允许在警卫测试。由编译器内联。
例子
iex> 1 <= 2
true
left <> right (macro)
连接两个二进制文件。
例子
iex> "foo" <> "bar"
"foobar"
<>
只要第一部分是文字二进制,操作符也可用于模式匹配(和保护子句):
iex> "foo" <> x = "foobar"
iex> x
"bar"
x <> "bar" = "foobar"
会导致一个CompileError
例外。
left == right
term == term :: boolean
如果两个项目相等则返回true
。
该运算符认为1和1.0是相等的。对于更严格的语义,请改用===
。
Elixir中的所有术语都可以相互比较。
允许在警卫测试。由编译器内联。
例子
iex> 1 == 2
false
iex> 1 == 1.0
true
left === right
term === term :: boolean
如果两个项目完全相等则返回true
。
如果这些项目具有相同的值并且属于同一类型,则只会将其视为完全相同。例如,1 == 1.0
返回true,但由于它们是不同的类型,所以1 === 1.0
返回false。
Elixir中的所有术语都可以相互比较。
允许在警卫测试。由编译器内联。
例子
iex> 1 === 2
false
iex> 1 === 1.0
false
left =~ right
String.t =~ String.t | Regex.t :: boolean
匹配右侧的术语与右侧的正则表达式或字符串。
如果left匹配正确(如果它是正则表达式)或包含right(如果它是一个字符串),则返回true。
例子
iex> "abcd" =~ ~r/c(d)/
true
iex> "abcd" =~ ~r/e/
false
iex> "abcd" =~ "bc"
true
iex> "abcd" =~ "ad"
false
iex> "abcd" =~ ""
true
left > right
term> term :: boolean
如果left超过正确值,则返回true。
Elixir中的所有术语都可以相互比较。
允许在警卫测试。由编译器内联。
例子
iex> 1 > 2
false
left >= right
term> = term :: boolean
如果left大于或等于right,则返回true
。
Elixir中的所有术语都可以相互比较。
允许在警卫测试。由编译器内联。
例子
iex> 1 >= 2
false
@expr(宏)
读取和写入当前模块的属性。
属性的规范示例注释了模块实现OTP行为的方式,例如GenServer
:
defmodule MyServer do
@behaviour GenServer
# ... callbacks ...
end
默认情况下,Elixir支持Erlang支持的所有模块属性,但也可以使用自定义属性:
defmodule MyServer do
@my_data 13
IO.inspect @my_data #=> 13
end
与Erlang不同,这些属性默认不存储在模块中,因为在Elixir中通常使用自定义属性来存储在编译时可用的临时数据。通过使用自定义属性可以配置为更接近Erlang Module.register_attribute/3
。
最后,请注意,属性也可以在函数内读取:
defmodule MyServer do
@my_data 11
def first_data, do: @my_data
@my_data 13
def second_data, do: @my_data
end
MyServer.first_data #=> 11
MyServer.second_data #=> 13
重要的是要注意,读取属性需要获取其当前值的快照。换句话说,该值是在编译时读取的,而不是在运行时读取的。检查Module
模块是否有其他功能来操作模块属性。
ABS(数)
abs(number) :: number
返回一个整数或浮点数,它是算术绝对值number
。
允许在警卫测试。由编译器内联。
例子
iex> abs(-3.33)
3.33
iex> abs(-3)
3
alias!(alias) (macro)
在引用中使用时,标记给定的别名不应卫生。这意味着当扩展宏时别名将被扩展。
查看Kernel.SpecialForms.quote / 2了解更多信息
left and right (macro)
布尔和。
如果第一个参数是false
,则返回false
; 否则,返回第二个参数。
由于短路,只需要第一个参数为布尔值。如果第一个参数不是布尔值,则会引发异常ArgumentError
。
允许在警卫测试。
例子
iex> true and false
false
iex> true and "yay!"
"yay!"
apply(fun, args)
apply((... -> any), [any]) :: any
fun
用参数列表调用给定的参数args
。
由编译器内联。
例子
iex> apply(fn x -> x * 2 end, [2])
4
apply(module, fun, args)
apply(module, atom, [any]) :: any
fun
从module
参数列表中调用给定args
。
由编译器内联。
例子
iex> apply(Enum, :reverse, [[1, 2, 3]])
[3, 2, 1]
binary_part(binary, start, length)
binary_part(binary, non_neg_integer, integer) :: binary
从start
长度开始提取二进制文件的部分length
。二进制文件是零索引的。
如果以任何方式在二进制外引用start
或length
引用,则会引发异常ArgumentError
。
允许在警卫测试。由编译器内联。
例子
iex> binary_part("foo", 1, 2)
"oo"
length
可以使用负数来提取字节前面的字节start
:
iex> binary_part("Hello", 5, -3)
"llo"
绑定(context \ nil)(宏)
将给定上下文的绑定作为关键字列表返回。
在返回的结果中,键是变量名,值是相应的变量值。
如果给定context
是nil
(默认情况下是),则返回当前上下文的绑定。
例子
iex> x = 1
iex> binding()
[x: 1]
iex> x = 2
iex> binding()
[x: 2]
iex> binding(:foo)
[]
iex> var!(x, :foo) = 1
1
iex> binding(:foo)
[x: 1]
bit_size(bitstring)
bit_size(bitstring) :: non_neg_integer
返回一个大小为整数的整数bitstring
。
允许在警卫测试。由编译器内联。
例子
iex> bit_size(<<433::16, 3::3>>)
19
iex> bit_size(<<1, 2, 3>>)
24
byte_size(比特串)
byte_size(bitstring) :: non_neg_integer
返回需要包含的字节数bitstring
。
也就是说,如果位数bitstring
不能被8整除,则结果字节数将被四舍五入(超出)。此操作在不断的时间内发生。
允许在警卫测试。由编译器内联。
例子
iex> byte_size(<<433::16, 3::3>>)
3
iex> byte_size(<<1, 2, 3>>)
3
def(call,expr \ nil)(宏)
用给定的名称和正文定义一个函数。
例子
defmodule Foo do
def bar, do: :baz
end
Foo.bar #=> :baz
需要参数的函数可以定义如下:
defmodule Foo do
def sum(a, b) do
a + b
end
end
在上面的例子中,sum/2
定义了一个函数。这个函数接收两个参数并返回它们的总和。
默认参数
\\
用于指定函数参数的默认值。例如:
defmodule MyMath do
def multiply_by(number, factor \\ 2) do
number * factor
end
end
MyMath.multiply_by(4, 3) #=> 12
MyMath.multiply_by(4) #=> 8
编译器将其转换为具有不同arity的多个函数,这里Foo.multiply_by/1
和Foo.multiply_by/2
表示带有默认值的参数的参数被传递或未传递的情况。
当使用默认参数定义函数以及多个显式声明的子句时,您必须编写一个声明默认值的函数头。例如:
defmodule MyString do
def join(string1, string2 \\ nil, separator \\ " ")
def join(string1, nil, _separator) do
string1
end
def join(string1, string2, separator) do
string1 <> separator <> string2
end
end
请注意,\\
不能与匿名函数一起使用,因为它们只能有一个元素。
函数和变量名称
函数和变量名称具有以下语法:小写ASCII字母或下划线,后跟任意数量的小写或大写ASCII字母,数字或下划线。可选地,它们可以以感叹号或问号结束。
对于变量,任何以下划线开头的标识符都应指示一个未使用的变量。例如:
def foo(bar) do
[]
end
#=> warning: variable bar is unused
def foo(_bar) do
[]
end
#=> no warning
def foo(_bar) do
_bar
end
#=> warning: the underscored variable "_bar" is used after being set
rescue/catch/after
FunctionForms.try / 1的功能体支持救援,捕获和追踪。 以下两个功能是等同的:
def format(value) do
try do
format!(value)
catch
:exit, reason -> {:error, reason}
end
end
def format(value) do
format!(value)
catch
:exit, reason -> {:error, reason}
end
defdelegate(funs, opts) (macro)
定义一个委托给另一个模块的函数。
使用定义的函数defdelegate/2
是公共的,并且可以从它们定义的模块外部调用(例如,如果它们是使用定义的def/2
)。当需要作为私人功能委托时,import/2
应该使用。
授权只适用于功能; 不支持委派宏。
检查def/2
命名和默认参数的规则。
选项
:to
- 要发送到的模块。
:as
- 调用中给出的目标的函数:to
。该参数是可选的,并且默认为被委派的名称(funs
)。
例子
defmodule MyList do
defdelegate reverse(list), to: :lists
defdelegate other_reverse(list), to: :lists, as: :reverse
end
MyList.reverse([1, 2, 3])
#=> [3, 2, 1]
MyList.other_reverse([1, 2, 3])
#=> [3, 2, 1]
defexception(字段)(宏)
定义一个例外。
异常是由实现该Exception
行为的模块支持的结构。该Exception
行为需要执行两个功能:
exception/1
- 接收给定的参数raise/2
并返回异常结构。默认实现接受一组合并到结构中的关键字参数或一个字符串作为异常的消息。
message/1
- 接收到异常结构并且必须返回它的消息。最常见的例外有一个消息字段,默认情况下该消息字段由该函数访问。但是,如果某个异常没有消息字段,则必须明确实现此功能。
由于异常是结构,所支持的API defstruct/1
也可以在defexception/1
。
提出例外
引发异常的最常见方式是raise/2
:
defmodule MyAppError do
defexception [:message]
end
value = [:hello]
raise MyAppError,
message: "did not get what was expected, got: #{inspect value}"
在许多情况下,传递期望值以raise / 2并在Exception.exception / 1回调中生成消息更方便:
defmodule MyAppError do
defexception [:message]
def exception(value) do
msg = "did not get what was expected, got: #{inspect value}"
%MyAppError{message: msg}
end
end
raise MyAppError, value
上面的示例显示了定制异常消息的首选策略。
defimpl(name,opts,do_block \ [])(宏)
定义给定协议的实现。
请参阅defprotocol/2
有关协议的更多信息和示例。
在一个实现中,协议的名称可以通过@protocol
和当前目标访问@for
。
defmacro(call,expr \ nil)(宏)
用给定的名称和正文定义一个宏。
检查def/2
命名和默认参数的规则。
例子
defmodule MyLogic do
defmacro unless(expr, opts) do
quote do
if !unquote(expr), unquote(opts)
end
end
end
require MyLogic
MyLogic.unless false do
IO.puts "It works"
end
defmacrop(call,expr \ nil)(宏)
用给定的名称和正文定义一个私有宏。
私有宏只能从定义它们的相同模块访问。
检查defmacro/2
更多信息,并检查def/2
有关命名和默认参数的规则。
defmodule(别名,do_block)(宏)
用给定内容定义由名称给出的模块。
这个宏定义了一个模块,其中给定alias
的名称和给定的内容。它返回一个包含四个元素的元组:
- 内容块的评价结果
- 模块名称
- 模块的二进制内容
- 评估内容块的结果
例子
iex> defmodule Foo do
...> def bar, do: :baz
...> end
iex> Foo.bar
:baz
嵌套
在另一个模块中嵌套模块会影响嵌套模块的名称:
defmodule Foo do
defmodule Bar do
end
end
在上面的例子中,创建了两个模块--Foo和Foo.Bar。 嵌套时,Elixir会自动为内部模块创建一个别名,允许第二个模块Foo.Bar在Bar定义的同一词汇范围(Foo模块)中被访问。
如果Foo.Bar
模块移到别的地方,到引用Bar
了在Foo
模块需要进行更新,以完全限定域名(Foo.Bar
)或别名具有对中明确设置Foo
的帮助下模块Kernel.SpecialForms.alias/2
。
defmodule Foo.Bar do
# code
end
defmodule Foo do
alias Foo.Bar
# code here can refer to "Foo.Bar" as just "Bar"
end
模块名称
模块名称可以是任何原子,但Elixir提供了通常用于模块名称的特殊语法。所谓的模块名称是大写ASCII字母,后跟任意数量的小写或大写ASCII字母,数字或下划线。这个标识符相当于一个前缀为Elixir.
。的原子。所以在这个defmodule Foo
例子Foo
中相当于:"Elixir.Foo"
动态名称
Elixir模块名称可以动态生成。这在使用宏时非常有用。例如,可以写:
defmodule String.to_atom("Foo#{1}") do
# contents ...
end
只要表达式作为第一个参数传递defmodule/2
给一个原子,Elixir将接受任何模块名称。请注意,当使用动态名称时,Elixir不会在当前模块下嵌套名称,也不会自动设置别名。
defoverridable(keywords_or_behaviour)(宏)
使当前模块中的给定函数可覆盖。
一个可重写的函数被懒惰地定义,允许开发人员覆盖它。
例
defmodule DefaultMod do
defmacro __using__(_opts) do
quote do
def test(x, y) do
x + y
end
defoverridable [test: 2]
end
end
end
defmodule InheritMod do
use DefaultMod
def test(x, y) do
x * y + super(x, y)
end
end
如上例所示,super
可用于调用默认实现。
如果@behaviour
已经定义,defoverridable
也可以用模块作为参数来调用。所有从调用上的行为实现的回调defoverridable
将被标记为可覆盖。
例
defmodule Behaviour do
@callback foo :: any
end
defmodule DefaultMod do
defmacro __using__(_opts) do
quote do
@behaviour Behaviour
def foo do
"Override me"
end
defoverridable Behaviour
end
end
end
defmodule InheritMod do
use DefaultMod
def foo do
"Overriden"
end
end
defp(call,expr \ nil)(宏)
用给定的名称和正文定义一个私有函数。
专用功能只能从定义它们的模块中访问。尝试从模块外部访问私有函数会导致UndefinedFunctionError
异常。
检查def/2
更多信息。
例子
defmodule Foo do
def bar do
sum(1, 2)
end
defp sum(a, b), do: a + b
end
Foo.bar #=> 3
Foo.sum(1, 2) #=> ** (UndefinedFunctionError) undefined function Foo.sum/2
defprotocol(name,do_block)(宏)
定义一个协议。
协议指定了一个应该由其实现定义的API。
例子
在Elixir中,我们有两个用于检查数据结构中有多少项的动词:length
和size
。length
意味着必须计算信息。例如,length(list)
需要遍历整个列表来计算其长度。在另一方面,tuple_size(tuple)
和byte_size(binary)
作为大小信息的数据结构中预先计算不依赖于元组和二进制文件的大小。
尽管Elixir包含特定的功能tuple_size
,binary_size
并且map_size
有时我们希望能够检索数据结构的大小,而不管其类型如何。在Elixir中,我们可以使用协议编写多态代码,即可以使用不同形状/类型的代码。大小协议可以实现如下:
defprotocol Size do
@doc "Calculates the size (and not the length!) of a data structure"
def size(data)
end
现在该协议可以针对每个数据结构实施,该协议可能具有符合以下要求的实施:
defimpl Size, for: BitString do
def size(binary), do: byte_size(binary)
end
defimpl Size, for: Map do
def size(map), do: map_size(map)
end
defimpl Size, for: Tuple do
def size(tuple), do: tuple_size(tuple)
end
注意,我们没有为列表实现它,因为我们没有size
列表上的信息,而是它的值需要用来计算length
。
有可能为所有Elixir类型实现协议:
- 结构(见下文)
Tuple
Atom
List
BitString
Integer
Float
Function
PID
Map
Port
Reference
Any
(如下)
协议和结构
协议的真正好处是与结构混合在一起。 例如,Elixir附带有许多数据类型,如MapSet。 我们也可以为这些类型实现Size协议:
defimpl Size, for: MapSet do
def size(map_set), do: MapSet.size(map_set)
end
在实现结构的协议时,:for
如果defimpl
调用位于定义结构的模块内部,则该选项可以省略:
defmodule User do
defstruct [:email, :name]
defimpl Size do
def size(%User{}), do: 2 # two fields
end
end
如果没有找到给定类型的协议实现,则调用该协议将会引发,除非它被配置为回退到Any
。在现有的基础上构建实现的便利也可用,请查看defstruct/1
有关派生协议的更多信息。
回退到任何地方
在某些情况下,为所有类型提供默认实现可能会很方便。这可以通过在协议定义中设置@fallback_to_any
属性来实现true
:
defprotocol Size do
@fallback_to_any true
def size(data)
end
该Size
协议现在可以用于Any
:
defimpl Size, for: Any do
def size(_), do: 0
end
虽然上面的实现可能是不合理的。例如,说一个PID或一个整数的大小为0是没有意义的。这@fallback_to_any
就是选择加入行为的原因之一。对于大多数协议,当协议未实现时引发错误是正确的行为。
类型
定义一个协议会自动定义一个名为的类型t
,可以按如下方式使用:
@spec print_size(Size.t) :: :ok
def print_size(data) do
IO.puts(case Size.size(data) do
0 -> "data has no items"
1 -> "data has one item"
n -> "data has #{n} items"
end)
end
在@spec
上述表示,允许以实现给定协议的所有类型是有效的参数类型为给定的功能。
反射
任何协议模块都包含三个额外功能:
__protocol__/1
-返回协议名称时:name
,给出与所述协议的功能和它们的arities关键字列表时:functions
给出,并且当实现的列表:impls
给出
impl_for/1
- 接收结构并返回实现该结构协议的模块,nil
否则返回
impl_for!/1
- 与上面相同,但如果未找到实现会引发错误
Enumerable.__protocol__(:functions) #=> [count: 1, member?: 2, reduce: 3] Enumerable.impl_for([]) #=> Enumerable.List Enumerable.impl_for(42) #=> nil
合并
为了处理开发中的代码加载,Elixir中的协议提供了特定于开发的协议调度的缓慢实现。
为了加速生产环境中的调度,所有的实施都在预先知道的情况下进行,Elixir提供了一项称为协议合并的功能。因此,无论编译器设置的选项如何,都将所有协议编译为debug_info
设置为。调试信息虽然可能在合并后被删除。trueelixirc
协议合并默认应用于所有Mix项目。要手动应用合并,请检查Protocol
模块或mix compile.protocols
任务中的功能。
defstruct(字段)查看源代码(宏)
定义一个结构。
结构是一个标记的映射,它允许开发人员为键,标签提供默认值,用于多态派发和编译时间断言。
要定义一个结构,开发人员必须定义两者__struct__/0
和 __struct__/1
函数。defstruct/1
是一个方便的宏,它定义了这些函数的一些便利。
有关结构的更多信息,请查看Kernel.SpecialForms.%/2
。
例子
defmodule User do
defstruct name: nil, age: nil
end
Struct字段在编译时进行评估,这允许它们是动态的。在下面的例子中,10 + 11
在编译时进行评估,并且年龄字段存储值为21
:
defmodule User do
defstruct name: nil, age: 10 + 11
end
该fields
参数通常是一个关键字列表与字段名称为原子键和默认值作为相应的值。defstruct/1
也支持原子列表作为它的参数:在这种情况下,列表中的原子将用作结构的字段名称,它们都将默认为nil
。
defmodule Post do
defstruct [:title, :content, :author]
end
派生
虽然结构是地图,但默认情况下,结构不会实现任何为地图实现的协议。例如,尝试在User
结构中使用协议会导致错误:
john = %User{name: "John"}
MyProtocol.call(john)
** (Protocol.UndefinedError) protocol MyProtocol not implemented for %User{...}
defstruct/1
但是,它允许派生出协议实现 。这可以通过@derive
在调用之前将属性定义为列表来完成defstruct/1
:
defmodule User do
@derive [MyProtocol]
defstruct name: nil, age: 10 + 11
end
MyProtocol.call(john) #=> works
对于@derive
列表中的每个协议,Elixir将断言该协议的任何实现(无论是否回退到任何位置true
),并检查任何实现是否定义了__deriving__/3
回调。如果是这样,则调用回调函数,否则会自动导出一个指向任何实现的实现。
执行密钥
当构建一个结构体时,Elixir将自动保证所有的键属于该结构体:
%User{name: "john", unknown: :key}
** (KeyError) key :unknown not found in: %User{age: 21, name: nil}
Elixir还允许开发人员在构建结构时必须始终执行某些键:
defmodule User do
@enforce_keys [:name]
defstruct name: nil, age: 10 + 11
end
现在尝试构建一个没有名称键的结构将会失败:
%User{age: 21}
** (ArgumentError) the following keys must also be given when building struct User: [:name]
请记住@enforce_keys
,构建结构时帮助开发人员提供简单的编译时保证。它不会在更新上执行,并且不提供任何类型的值验证。
类型
建议为结构定义类型。按照惯例这种类型被称为t
。为了在一个类型中定义一个结构体,使用了struct literal语法:
defmodule User do
defstruct name: "John", age: 25
@type t :: %User{name: String.t, age: non_neg_integer}
end
建议在定义结构类型时只使用结构语法。当提到另一个结构时,最好使用User.t
而不是 %User{}
。
未包含在%User{}
默认 的结构字段的类型term
。
内部结构对本地模块是私有的结构(模式匹配它们或直接访问它们的字段不应该被允许)应该使用该@opaque
属性。内部结构公开的结构应该使用@type
。
destructure(left, right)View Source(macro)
Destructures two lists, assigning each term in the right one to the matching term in the left one.
Unlike pattern matching via =
, if the sizes of the left and right lists don’t match, destructuring simply stops instead of raising an error.
破坏两个列表,将右边的每个术语分配给左边的匹配术语。
与通过模式匹配不同=
,如果左右列表的大小不匹配,则解构只会停止,而不会引发错误。
例子
iex> destructure([x, y, z], [1, 2, 3, 4, 5])
iex> {x, y, z}
{1, 2, 3}
在上面的例子中,即使右侧列表中的条目比左侧条目多,解构也可以正常工作。如果右侧列表较小,则剩余项目可简单设置为nil
:
iex> destructure([x, y, z], [1])
iex> {x, y, z}
{1, nil, nil}
左侧支持您在匹配左侧使用的任何表达式:
x = 1
destructure([^x, y, z], [1, 2, 3])
上面的例子只有在x
匹配右列表中的第一个值时才有效。否则,它会引起MatchError
(像=
运营商会这样做)。
div(dividend, divisor)View Source
div(integer, neg_integer | pos_integer) :: integer
执行整数除法。
ArithmeticError
如果其中一个参数不是整数,或者divisor
是,则引发异常0
。
允许在警卫测试。由编译器内联。
div/2
执行截断整数除法。这意味着结果总是趋于零。
如果你想执行flooded整数除法(向负无穷大舍入),请Integer.floor_div/2
改为使用。
例子
div(5, 2)
#=> 2
div(6, -4)
#=> -1
div(-99, 2)
#=> -49
div(100, 0)
#=> ** (ArithmeticError) bad argument in arithmetic expression
elem(tuple, index)View Source
elem(tuple, non_neg_integer) :: term
获取的元素零基础index
在tuple
。
ArgumentError
索引为负数或超出元组元素范围时引发。
允许在警卫测试。由编译器内联。
例子
tuple = {:foo, :bar, 3}
elem(tuple, 1)
#=> :bar
elem({}, 0)
#=> ** (ArgumentError) argument error
elem({:foo, :bar}, 2)
#=> ** (ArgumentError) argument error
exit(reason)View Source
exit(term) :: no_return
按照给定的原因停止执行调用进程。
由于评估此函数会导致进程终止,因此它没有返回值。
由编译器内联。
例子
当进程结束时,默认情况下它会随着原因退出:normal
。exit/1
如果您想要终止某个流程,但不发出任何失败信号,您还可以明确调用:
exit(:normal)
exit(:shutdown)
exit({:shutdown, term})
出于任何其他原因被认为是不正常的,并被视为崩溃。这意味着默认的主管行为开始了,发布了错误报告等。
这种行为在许多不同的地方依赖。例如,ExUnit
使用exit(:shutdown)
退出测试过程来表示链接过程,监督树等也要礼貌地关闭。
CLI退出
建立在上述退出信号的基础上,如果由命令行启动的进程因上述三种原因而退出,则其退出被认为是正常的,并且操作系统进程将以状态0退出。
但是,可以通过调用以下操作来定制操作系统出口信号:
exit({:shutdown, integer})
这将导致操作系统进程退出,integer
同时通知所有链接的OTP进程有礼貌地关闭。
任何其他退出原因都会导致操作系统进程退出时状态1
和链接的OTP进程崩溃。
function_exported?(module, function, arity)
function_exported?(module, atom, arity) :: boolean
如果模块已加载并且包含具有给定元素的公共函数,则返回true,否则返回false。
请注意,如果未加载模块,此功能不会加载模块。检查Code.ensure_loaded/1
更多信息。
例子
iex> function_exported?(Enum, :member?, 2)
true
get_and_update_in(path, fun) (macro)
获取值并通过给定值更新嵌套的数据结构path
。
这与get_and_update_in / 3类似,只不过路径是通过宏提取的,而不是传递一个列表。 例如:
get_and_update_in(opts[:foo][:bar], &{&1, &1 + 1})
相当于:
get_and_update_in(opts, [:foo, :bar], &{&1, &1 + 1})
请注意,为了使这个宏能够工作,完整的路径必须总能被这个宏看到。请参阅下面的路径部分。
例子
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> get_and_update_in(users["john"].age, &{&1, &1 + 1})
{27, %{"john" => %{age: 28}, "meg" => %{age: 23}}}
路径
路径可以以变量,本地或远程调用开始,并且必须后跟一个或多个:
foo[bar]
-访问密钥bar
中foo
; 如果foo
是零,nil
则返回
foo.bar
- 访问地图/结构字段; 如果该字段不存在,则会引发错误
以下是一些有效的路径:
users["john"][:age]
users["john"].age
User.all["john"].age
all_users()["john"].age
这里有一些无效的:
# Does a remote call after the initial value
users["john"].do_something(arg1, arg2)
# Does not access any key or field
users
get_and_update_in(数据,键,函数)
get_and_update_in(structure :: Access.t, keys, (term -> {get_value, update_value} | :pop)) :: {get_value, structure :: Access.t} when keys: [any, ...], update_value: term, get_value: var
获取一个值并更新嵌套结构。
data
是一个嵌套结构(即实现该Access
行为的映射,关键字列表或结构)。
该fun
参数接收key
(或nil
如果key
不存在)的值并且必须返回两元素元组:“获取”值(检索到的值,可以在返回之前对其进行操作)以及要存储的新值key
。该fun
还可以返回:pop
,这意味着当前值应从结构中删除并返回。
它使用Access
模块根据给定的结构遍历结构keys
,除非key
是函数。
如果一个键是一个函数,则会调用该函数传递三个参数,即operation(:get_and_update
),要访问的数据以及下一个要调用的函数。
这意味着get_and_update_in/3
可以扩展以提供自定义查找。缺点是函数不能作为键存储在被访问的数据结构中。
例子
当需要检索当前值(或根据当前值计算的某个值)并在同一时间更新时,此函数非常有用。例如,它可以用来将用户的年龄增加1,并且一次返回以前的年龄:
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> get_and_update_in(users, ["john", :age], &{&1, &1 + 1})
{27, %{"john" => %{age: 28}, "meg" => %{age: 23}}}
当其中一个键是一个函数时,该函数被调用。在下面的例子中,我们使用一个函数来获取和递增列表中的所有年龄:
iex> users = [%{name: "john", age: 27}, %{name: "meg", age: 23}]
iex> all = fn :get_and_update, data, next ->
...> Enum.map(data, next) |> :lists.unzip
...> end
iex> get_and_update_in(users, [all, :age], &{&1, &1 + 1})
{[27, 23], [%{name: "john", age: 28}, %{name: "meg", age: 24}]}
如果在调用函数之前的前一个值是nil
,函数将nil
作为一个值接收,并且必须相应地处理它(通过失败或提供一个理智的默认值)。
该Access
模块附带许多便利访问器函数,如all
上面定义的匿名函数。看Access.all/0
,Access.key/2
和其他作为例子。
get_in(data, keys)
get_in(Access.t, [term, ...]) :: term
从嵌套结构获取值。
除非是函数,否则使用Access
模块根据给定的结构遍历结构。keyskey
如果一个键是一个函数,则会调用该函数传递三个参数,即operation(:get
),要访问的数据以及下一个要调用的函数。
这意味着get_in/2
可以扩展以提供自定义查找。缺点是函数不能作为键存储在被访问的数据结构中。
例子
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> get_in(users, ["john", :age])
27
在任何情况下,在中间的回报条目nil
,nil
将返回为每个接入模块:
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> get_in(users, ["unknown", :age])
nil
当其中一个键是一个函数时,该函数被调用。在下面的例子中,我们使用一个函数来获取列表中的所有地图:
iex> users = [%{name: "john", age: 27}, %{name: "meg", age: 23}]
iex> all = fn :get, data, next -> Enum.map(data, next) end
iex> get_in(users, [all, :age])
[27, 23]
如果在调用函数之前的前一个值是nil
,该函数将nil
作为一个值接收,并且必须相应地处理它。
hd(list)
hd(nonempty_maybe_improper_list(elem, any)) :: elem when elem: term
返回列表的头部。ArgumentError
如果列表为空,则引发。
它适用于不正确的列表。
允许在警卫测试。由编译器内联。
例子
hd([1, 2, 3, 4])
#=> 1
hd([])
#=> ** (ArgumentError) argument error
hd([1 | 2])
#=> 1
if(condition, clauses) (macro)
提供一个if/2
宏。
这个宏期望第一个参数是一个条件,第二个参数是一个关键字列表。
一个例子
if(foo, do: bar)
在上面的例子中,如果foo的值为真(即既不是false也不为零),bar会被返回。 否则,将返回零。
else
可以给出一个选项来指定相反的:
if(foo, do: bar, else: baz)
块示例
也可以将一个块传递给if/2
宏。上面的第一个例子将被翻译为:
if foo do
bar
end
请注意,do/end
成为分隔符。第二个例子将转化为:
if foo do
bar
else
baz
end
为了比较两个以上的子句,cond/1
必须使用该宏。
left in right (macro)
检查左侧的元素是否是右侧集合的成员。
例子
iex> x = 1
iex> x in [1, 2, 3]
true
这个运算符(这是一个宏)简单地转换为一个呼叫Enum.member?/2
。上面的例子将转化为:
Enum.member?([1, 2, 3], x)
Elixir也支持left not in right
,其评估not(left in right)
如下:
iex> x = 1
iex> x not in [1, 2, 3]
false
Guards
只要右侧是范围或列表,in/2
操作员(以及not in
)可用于警戒条款。在这种情况下,Elixir会将运营商扩展到有效的警戒表达。例如:
when x in [1, 2, 3]
转化为:
when x === 1 or x === 2 or x === 3
使用范围时:
when x in 1..3
转化为:
when is_integer(x) and x >= 1 and x <= 3
请注意,只有整数可以被考虑在范围in
内。
AST 考虑
left not in right
被编译器解析为AST:
{:not, _, [{:in, _, [left, right]}]}
这与AST相同not(left in right)
。
另外,Macro.to_string/2
将把这个AST的所有事件翻译成left not in right
。
inspect(term, opts \ [])
inspect(Inspect.t, keyword) :: String.t
根据Inspect
协议检查给定的参数。第二个参数是一个关键字列表,其中包含控制检查的选项。
选项
inspect/2
接受内部翻译为Inspect.Opts
结构的选项列表。查看文档Inspect.Opts
以查看支持的选项。
例子
iex> inspect(:foo)
":foo"
iex> inspect [1, 2, 3, 4, 5], limit: 3
"[1, 2, 3, ...]"
iex> inspect [1, 2, 3], pretty: true, width: 0
"[1,\n 2,\n 3]"
iex> inspect("olá" <> <<0>>)
"<<111, 108, 195, 161, 0>>"
iex> inspect("olá" <> <<0>>, binaries: :as_strings)
"\"olá\\0\""
iex> inspect("olá", binaries: :as_binaries)
"<<111, 108, 195, 161>>"
iex> inspect('bar')
"'bar'"
iex> inspect([0 | 'bar'])
"[0, 98, 97, 114]"
iex> inspect(100, base: :octal)
"0o144"
iex> inspect(100, base: :hex)
"0x64"
请注意,该Inspect
协议不一定会返回Elixir术语的有效表示。在这种情况下,检查结果必须以#
。例如,检查一个函数将返回:
inspect fn a, b -> a + b end
#=> #Function<...>
is_atom(term)
is_atom(term) :: boolean
如果term
是原子则返回true; 否则返回false
。
允许在警卫测试。由编译器内联。
is_binary(term)
is_binary(term) :: boolean
如果term
是二进制则返回true; 否则返回false
。
二进制文件总是包含完整的字节数。
允许在警卫测试。由编译器内联。
例子
iex> is_binary "foo"
true
iex> is_binary <<1::3>>
false
is_bitstring(项)
is_bitstring(term) :: boolean
如果term
是位串(包括二进制),则返回true; 否则返回false
。
允许在警卫测试。由编译器内联。
例子
iex> is_bitstring "foo"
true
iex> is_bitstring <<1::3>>
true
is_boolean(项)
is_boolean(term) :: boolean
如果term是原子true或原子false,则返回true(即,布尔值); 否则返回false。
允许在警卫测试。由编译器内联。
is_float(项)
is_float(term)::布尔值
如果term是一个浮点数,则返回true; 否则返回false。
允许在警卫测试。由编译器内联。
is_function(项)
is_function(term) :: boolean
如果term是一个函数,则返回true; 否则返回false。
允许在警卫测试。由编译器内联。
is_function(term,arity)
is_function(term,non_neg_integer)::布尔值
如果term是一个可以用参数数量来应用的函数,则返回true; 否则返回false。
允许在警卫测试。由编译器内联。
例子
iex> is_function(fn(x) -> x * 2 end, 1)
true
iex> is_function(fn(x) -> x * 2 end, 2)
false
is_integer(项)
is_integer(term) :: boolean
如果term是一个整数,则返回true; 否则返回false。
允许在警卫测试。由编译器内联。
is_list(项)
is_list(term) :: boolean
如果term是包含零个或多个元素的列表,则返回true; 否则返回false。
允许在警卫测试。由编译器内联。
is_map(项)
is_map(term):: boolean
如果term是地图,则返回true; 否则返回false。
允许在警卫测试。由编译器内联。
is_nil(term) (macro)
如果term是零,则返回true,否则返回false。
允许在守卫条款。
例子
iex> is_nil(1)
false
iex> is_nil(nil)
true
is_number(term)
is_number(term) :: boolean
如果term是整数或浮点数,则返回true; 否则返回false。
允许在警卫测试。由编译器内联。
is_pid(项)
is_pid(term) :: boolean
如果term是一个PID(进程标识符),则返回true; 否则返回false。
允许在警卫测试。由编译器内联。
is_port(项)
is_port(term) :: boolean
如果term是一个端口标识符,则返回true; 否则返回false。
允许在警卫测试。由编译器内联。
is_reference(项)
is_reference(term) :: boolean
如果term是引用,则返回true; 否则返回false。
允许在警卫测试。由编译器内联。
is_tuple(term)
is_tuple(term):: boolean
如果term是一个元组,则返回true; 否则返回false。
允许在警卫测试。由编译器内联。
length(list)
length(list) :: non_neg_integer
返回list
的长度。
允许在警卫测试。由编译器内联。
例子
iex> length([1, 2, 3, 4, 5, 6, 7, 8, 9])
9
macro_exported?(module, macro, arity)
macro_exported?(module, atom, arity) :: boolean
如果模块已加载并且包含具有给定元素的公共宏,则返回true,否则返回false。
请注意,如果未加载模块,此功能不会加载模块。检查Code.ensure_loaded/1
更多信息。
如果module
是Erlang模块(而不是Elixir模块),则此函数始终返回false
。
例子
iex> macro_exported?(Kernel, :use, 2)
true
iex> macro_exported?(:erlang, :abs, 1)
false
make_ref()
make_ref() :: reference
返回几乎唯一的参考。
大约2 ^ 82次通话后,返回的参考将重新发生; 因此它在实践中足够独特。
由编译器内联。
例子
make_ref() #=> #Reference<0.0.0.135>
map_size(map)
map_size(map) :: non_neg_integer
返回地图的大小。
地图的大小是地图包含的键值对的数量。
此操作在不断的时间内发生。
允许在警卫测试。由编译器内联。
例子
iex> map_size(%{a: "foo", b: "bar"})
2
match?(pattern, expr) (macro)
一个方便的宏,用于检查右侧(表达式)是否匹配左侧(一个模式)。
例子
iex> match?(1, 1)
true
iex> match?(1, 2)
false
iex> match?({1, _}, {1, 2})
true
iex> map = %{a: 1, b: 2}
iex> match?(%{a: _}, map)
true
iex> a = 1
iex> match?(^a, 1)
true
match?/2
过滤在枚举中查找值时非常有用:
iex> list = [a: 1, b: 2, a: 3]
iex> Enum.filter(list, &match?({:a, _}, &1))
[a: 1, a: 3]
警卫条款也可以用于比赛:
iex> list = [a: 1, b: 2, a: 3]
iex> Enum.filter(list, &match?({:a, x} when x < 2, &1))
[a: 1]
但是,在匹配中分配的变量在函数调用之外将不可用(与=
运算符匹配的常规模式不同):
iex> match?(_x, 1)
true
iex> binding()
[]
max(first, second)
max(first, second) ::
first |
second when first: term, second: term
根据Erlang的术语顺序,返回两个给定项中最大的一个。
如果术语比较相等,则返回第一个。
由编译器内联。
例子
iex> max(1, 2)
2
iex> max(:a, :b)
:b
min(first, second)
min(first, second) ::
first |
second when first: term, second: term
根据Erlang的术语顺序,返回两个给定项中最小的一个。
如果术语比较相等,则返回第一个。
由编译器内联。
例子
iex> min(1, 2)
1
iex> min("foo", "bar")
"bar"
节点()
node() :: node
返回表示本地节点名称的原子。如果节点不活动,则返回:nonode@nohost
。
允许在警卫测试。由编译器内联。
节点(ARG)
node(pid | reference | port) :: node
返回给定参数所在的节点。参数可以是PID,参考或端口。如果本地节点不活动,则返回:nonode@nohost
允许在警卫测试。由编译器内联。
没有值
not false :: true
not true :: false
非布尔值。
arg
必须是一个布尔值; 如果不是,则会引发异常ArgumentError
。
允许在警卫测试。由编译器内联。
例子
iex> not false
true
left or right (macro)
布尔或。
如果第一个参数是true
,true
则被返回; 否则,返回第二个参数。
由于短路,只需要第一个参数为布尔值。如果第一个参数不是布尔值,ArgumentError
则会引发异常。
允许在警卫测试。
例子
iex> true or false
true
iex> false or 42
42
pop_in(路径)(宏)
通过给定的嵌套结构弹出一个键path
。
这与之类似pop_in/2
,除了通过宏提取路径而不是传递列表。例如:
pop_in(opts[:foo][:bar])
相当于:
pop_in(opts, [:foo, :bar])
请注意,为了使这个宏能够工作,完整的路径必须总能被这个宏看到。有关支持的路径表达式的更多信息,请查看get_and_update_in/2
文档。
例子
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> pop_in(users["john"][:age])
{27, %{"john" => %{}, "meg" => %{age: 23}}}
iex> users = %{john: %{age: 27}, meg: %{age: 23}}
iex> pop_in(users.john[:age])
{27, %{john: %{}, meg: %{age: 23}}}
在任何条目返回的情况下nil
,其密钥将被删除,删除将被视为成功。
pop_in(数据,键)
pop_in(data, [Access.get_and_update_fun(term, data) | term, ...]) :: {term, data} when data: Access.container
从给定的嵌套结构中弹出一个键。
使用Access协议根据给定的键遍历结构,除非键是函数。 如果该键是一个函数,它将按照get_and_update_in / 3中的指定进行调用。
例子
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> pop_in(users, ["john", :age])
{27, %{"john" => %{}, "meg" => %{age: 23}}}
在任何条目返回的情况下nil
,其密钥将被删除,删除将被视为成功。
put_elem(元组,索引,值)
put_elem(tuple, non_neg_integer, term) :: tuple
插入value
从零开始在给定index
在tuple
。
由编译器内联。
例子
iex> tuple = {:foo, :bar, 3}
iex> put_elem(tuple, 0, :baz)
{:baz, :bar, 3}
put_in(路径,值)(宏)
通过给定的值在嵌套结构中放置一个值path
。
这与put_in/3
类似,除了通过宏提取路径而不是传递列表。例如:
put_in(opts[:foo][:bar], :baz)
相当于:
put_in(opts, [:foo, :bar], :baz)
请注意,为了使这个宏能够工作,完整的路径必须总能被这个宏看到。有关支持的路径表达式的更多信息,请查看get_and_update_in/2
文档。
例子
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> put_in(users["john"][:age], 28)
%{"john" => %{age: 28}, "meg" => %{age: 23}}
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> put_in(users["john"].age, 28)
%{"john" => %{age: 28}, "meg" => %{age: 23}}
put_in(数据,键,值)
put_in(Access.t, [term, ...], term) :: Access.t
在嵌套结构中放置一个值。
使用Access模块根据给定的键遍历结构,除非键是函数。 如果该键是一个函数,它将按照get_and_update_in / 3中的指定进行调用。
例子
iex> users =%{“john”=>%{age:27},“meg”=>%{age:23}}
iex> put_in(users,[“john”,:age],28)
%{“john”=>%{age:28},“meg”=>%{age:23}}
如果中间的任何条目返回nil
,则下一次尝试访问时会出现错误。
raise(message) (macro)
引发一个例外。
如果参数msg
是二进制文件,则会RuntimeError
使用给定参数作为消息引发异常。
如果msg是一个原子,它只需要以atom作为第一个参数并将[]作为第二个参数来调用raise / 2。
如果msg
是一个异常结构,它会按原样产生。
如果msg
还有其他事情,raise
将会失败,并发生ArgumentError
异常。
例子
iex> raise "oops"
** (RuntimeError) oops
try do
1 + :foo
rescue
x in [ArithmeticError] ->
IO.puts "that was expected"
raise x
end
raise(exception, attributes) (macro)
引发一个例外。
在给定参数(必须是模块名称,如ArgumentError或RuntimeError)上传递attrs作为属性以检索异常结构时调用异常/ 1函数。
任何包含对defexception/1
宏的调用的模块都会自动实现Exception.exception/1
预期的回调raise/2
。有关更多信息,请参阅defexception/1
。
例子
iex> raise(ArgumentError, message: "Sample")
** (ArgumentError) Sample
rem(dividend, divisor)
rem(integer, neg_integer | pos_integer) :: integer
计算整数除法的其余部分。
rem/2
使用截断除法,这意味着结果将始终具有符号dividend
。
如果其中一个参数不是整数,或者除数为0,则引发ArithmeticError异常。
允许在警卫测试。由编译器内联。
例子
iex> rem(5, 2)
1
iex> rem(6, -4)
2
reraise(message, stacktrace) (macro)
引发一个保存前一个堆栈跟踪的异常。
适用raise/1
但不会生成新的堆栈跟踪。
注意System.stacktrace/0
返回最后一个异常的栈跟踪。也就是说,将stacktrace分配为rescue
子句中的第一个表达式是常见的,因为rescue子句和raise调用之间可能引发(并拯救)的任何其他异常可能会更改该System.stacktrace/0
值。
例子
try do
raise "oops"
rescue
exception ->
stacktrace = System.stacktrace
if Exception.message(exception) == "oops" do
reraise exception, stacktrace
end
end
reraise(exception, attributes, stacktrace) (macro)
引发一个保存前一个堆栈跟踪的异常。
reraise / 3像reraise / 2一样工作,除了它将参数传递给异常/ 1函数(如raise / 2中所述)。
例子
try do
raise "oops"
rescue
exception ->
stacktrace = System.stacktrace
reraise WrapperError, [exception: exception], stacktrace
end
round(number)
round(value) :: value when value: integer
round(float) :: integer
将数字四舍五入为最接近的整数。
允许在警卫测试。由编译器内联。
例子
iex> round(5.6)
6
iex> round(5.2)
5
iex> round(-9.9)
-10
iex> round(-9)
-9
self()
self() :: pid
返回调用进程的PID(进程标识符)。
允许在守卫条款。由编译器内联。
send(dest, message)
send(dest :: pid | port | atom | {atom, node}, message) :: message when message: any
将消息发送给给定的dest
并返回消息。
dest
可以是远程或本地PID,(本地)端口,本地注册的名称或{registered_name, node}
用于另一个节点处的注册名称的元组。
由编译器内联。
例子
iex> send self(), :hello
:hello
sigil_C(term, modifiers) (macro)
处理印记~C
。
它只是返回一个charlist而不会转义字符和插值。
例子
iex> ~C(foo)
'foo'
iex> ~C(f#{o}o)
'f\#{o}o'
sigil_D(date, modifiers) (macro)
处理~D
日期的印记。
小写~d
变体不存在,因为插值和转义字符对日期标记没有用处。
例子
n阶>〜d [2015年1月13日]
〜d [2015年1月13日]
sigil_N(日期,修饰符)(宏)
处理~N
单纯时间的印记。
小写~n
变体不存在,因为插值和转义字符对日期时间标记没有用处。
例子
iex> ~N[2015-01-13 13:00:07]
~N[2015-01-13 13:00:07]
iex> ~N[2015-01-13T13:00:07.001]
~N[2015-01-13 13:00:07.001]
sigil_R(术语,修饰符)(宏)
处理印记~R
。
它返回一个正则表达式模式而不会转义或解释插值。
有关正则表达式的更多信息可以在Regex
模块中找到。
例子
iex> Regex.match?(~R(f#{1,3}o), "f#o")
true
sigil_S(term, modifiers) (macro)
处理印记~S
。
它只是返回一个字符串而不会转义字符,也不会插入。
例子
iex> ~S(foo)
"foo"
iex> ~S(f#{o}o)
"f\#{o}o"
sigil_T(date, modifiers) (macro)
处理~T
时间印记。
小写~t
变体不存在,因为插值和转义字符对时间标记没有用处。
例子
iex> ~T[13:00:07]
~T[13:00:07]
iex> ~T[13:00:07.001]
~T[13:00:07.001]
sigil_W(term, modifiers) (macro)
处理印记~W
。
它返回一个由空格分隔的“单词”列表,不会转义或解释插值。
修饰符
s
:列表中的单词是字符串(默认)
a
:列表中的单词是原子
c
:列表中的单词是charlists
例子
iex> ~W(foo #{bar} baz)
["foo", "\#{bar}", "baz"]
sigil_c(term, modifiers) (macro)
处理印记~c
。
它返回一个charlist,就好像它是一个单引号字符串,不需要转义字符并替换插值。
例子
iex> ~c(foo)
'foo'
iex> ~c(f#{:o}o)
'foo'
iex> ~c(f\#{:o}o)
'f\#{:o}o'
sigil_r(term, modifiers) (macro)
处理印记~r
。
它返回正则表达式模式,消除字符并替换插值。
有关正则表达式的更多信息可以在Regex
模块中找到。
例子
iex> Regex.match?(~r(foo), "foo")
true
iex> Regex.match?(~r/abc/, "abc")
true
sigil_s(term, modifiers) (macro)
处理印记~s
。
它返回一个字符串,就好像它是一个双引号字符串,避免了字符和替换插值。
例子
iex> ~s(foo)
"foo"
iex> ~s(f#{:o}o)
"foo"
iex> ~s(f\#{:o}o)
"f\#{:o}o"
sigil_w(term, modifiers) (macro)
处理印记~w
。
它返回一个由空格分隔的“单词”列表。对每个单词都进行字符消隐和内插。
修饰符
s
:列表中的单词是字符串(默认)
a
:列表中的单词是原子
c
:列表中的单词是charlists
例子
iex> ~w(foo #{:bar} baz)
["foo", "bar", "baz"]
iex> ~w(foo #{" bar baz "})
["foo", "bar", "baz"]
iex> ~w(--source test/enum_test.exs)
["--source", "test/enum_test.exs"]
iex> ~w(foo bar baz)a
[:foo, :bar, :baz]
spawn(fun)
spawn((() -> any)) :: pid
产生给定的函数并返回它的PID。
通常,开发人员不使用spawn函数,而是使用构建在spawn之上的抽象函数(如Task,GenServer和Agent),这些抽象函数在内省和调试方面产生更多便利。
检查Process
模块以获取更多与过程相关的功能。
匿名函数接收0个参数,并且可以返回任何值。
由编译器内联。
例子
current = self()
child = spawn(fn -> send current, {self(), 1 + 2} end)
receive do
{^child, 3} -> IO.puts "Received 3 back"
end
spawn(module, fun, args)
spawn(module, atom, list) :: pid
产卵给定函数fun
从给定的module
传递给定的args
,并返回其PID。
通常,开发商不使用spawn
的功能,而不是他们使用的抽象,例如Task
,GenServer
和Agent
,建在上面spawn
,会派生更多的便利流程内省和调试方面。
检查Process
模块以获取更多与过程相关的功能。
由编译器内联。
例子
spawn(SomeModule, :function, [1, 2, 3])
spawn_link(fun)
spawn_link((() -> any)) :: pid
生成给定的函数,将其链接到当前进程,并返回其PID。
通常,开发人员不使用spawn函数,而是使用构建在spawn之上的抽象函数(如Task,GenServer和Agent),这些抽象函数在内省和调试方面产生更多便利。
检查Process
模块以获取更多与过程相关的功能。有关链接的更多信息,请检查Process.link/1
。
匿名函数接收0个参数,并且可以返回任何值。
由编译器内联。
例子
current = self()
child = spawn_link(fn -> send(current, {self(), 1 + 2}) end)
receive do
{^child, 3} -> IO.puts "Received 3 back"
end
spawn_link(module, fun, args)
spawn_link(module, atom, list) :: pid
根据给定的给定函数传递给定函数fun
,module
将其args
链接到当前进程并返回其PID。
通常,开发人员不使用spawn函数,而是使用构建在spawn之上的抽象函数(如Task,GenServer和Agent),这些抽象函数在内省和调试方面产生更多便利。
检查Process
模块以获取更多与过程相关的功能。有关链接的更多信息,请检查Process.link/1
。
由编译器内联。
例子
spawn_link(SomeModule, :function, [1, 2, 3])
spawn_monitor(fun)
spawn_monitor((() -> any)) :: {pid, reference}
生成给定的函数,监视它并返回其PID和监视参考。
通常,开发人员不使用spawn函数,而是使用构建在spawn之上的抽象函数(如Task,GenServer和Agent),这些抽象函数在内省和调试方面产生更多便利。
检查Process
模块以获取更多与过程相关的功能。
匿名函数接收0个参数,并且可以返回任何值。
由编译器内联。
例子
current = self()
spawn_monitor(fn -> send current, {self(), 1 + 2} end)
spawn_monitor(module, fun, args)
spawn_monitor(module, atom, list) :: {pid, reference}
产生给定的模块和函数传递给定的参数,监视它并返回其PID和监视参考。
通常,开发商不使用spawn
的功能,而不是他们使用的抽象,例如Task
,GenServer
和Agent
,建在上面spawn
,会派生更多的便利流程内省和调试方面。
检查Process
模块以获取更多与过程相关的功能。
由编译器内联。
例子
spawn_monitor(SomeModule, :function, [1, 2, 3])
struct(struct, fields \ [])
struct(module | struct, Enum.t) :: struct
创建并更新结构。
所述struct
参数可以是一个原子(其定义defstruct
)或struct
本身。第二个参数是任何Enumerable
在枚举过程中发出两元元组(键 - 值对)的元素。
Enumerable中不存在于结构中的键将被自动丢弃。 请注意,键必须是原子,因为定义结构时只允许使用原子。
此功能对于动态创建和更新结构以及将地图转换为结构非常有用; 在后一种情况下,只需:__struct__
在地图中插入适当的字段可能不够,struct/2
应该使用它。
例子
defmodule User do
defstruct name: "john"
end
struct(User)
#=> %User{name: "john"}
opts = [name: "meg"]
user = struct(User, opts)
#=> %User{name: "meg"}
struct(user, unknown: "value")
#=> %User{name: "meg"}
struct(User, %{name: "meg"})
#=> %User{name: "meg"}
# String keys are ignored
struct(User, %{"name" => "meg"})
#=> %User{name: "john"}
struct!(struct, fields \ [])
struct!(module | struct, Enum.t) :: struct | no_return
类似于struct/2
但检查关键有效性。
该函数struct!/2
模拟结构体的编译时行为。这意味着:
- 当构建一个结构体
struct!(SomeStruct, key: :value)
时它就等价于%SomeStruct{key: :value}
,因此这个函数将检查每个给定的键值是否属于该结构体。如果结构正在执行任何密钥通过@enforce_keys
,那么这些也将被强制执行;
- 当更新一个结构体时,
struct!(%SomeStruct{}, key: :value)
就等同于%SomeStruct{struct | key: :value}
,因此这个函数将检查每个给定的键值是否属于该结构体。但是,更新结构不强制执行密钥,因为密钥仅在构建时才执行;
throw(term)
throw(term) :: no_return
来自函数的非本地返回。
检查Kernel.SpecialForms.try/1
更多信息。
由编译器内联。
tl(list)
tl(nonempty_maybe_improper_list(elem, tail)) ::
maybe_improper_list(elem, tail) |
tail when elem: term, tail: term
返回列表的尾部。如果列表为空,则引发ArgumentError
。
它适用于不正确的列表。
允许在警卫测试。由编译器内联。
例子
tl([1, 2, 3, :go])
#=> [2, 3, :go]
tl([])
#=> ** (ArgumentError) argument error
tl([:one])
#=> []
tl([:a, :b | :c])
#=> [:b | :c]
tl([:a | %{b: 1}])
#=> %{b: 1}
to_charlist(term) (macro)
根据List.Chars
协议将给定的术语转换为charlist 。
例子
iex> to_charlist(:foo)
'foo'
to_string(term) (macro)
根据String.Chars
协议将参数转换为字符串。
这是在有字符串插值时调用的函数。
例子
iex> to_string(:foo)
"foo"
trunc(number)
trunc(float) :: integer
trunc(value) :: value when value: integer
返回整数部分number
。
允许在警卫测试。由编译器内联。
例子
iex> trunc(5.4)
5
iex> trunc(-5.99)
-5
iex> trunc(-5)
-5
tuple_size(tuple)
tuple_size(tuple) :: non_neg_integer
返回元组的大小。
此操作在不断的时间内发生。
允许在警卫测试。由编译器内联。
例子
iex> tuple_size {:a, :b, :c}
3
unless(condition, clauses) (macro)
提供一个unless
宏。
此宏评估并返回do
作为第二个参数传入的块,除非clause
求值为true
。否则,它返回else
块的值(如果存在的话)或者否则返回nil
。
另见if/2
。
例子
iex> unless(Enum.empty?([]), do: "Hello")
nil
iex> unless(Enum.empty?([1, 2, 3]), do: "Hello")
"Hello"
iex> unless Enum.sum([2, 2]) == 5 do
...> "Math still works"
...> else
...> "Math is broken"
...> end
"Math still works"
update_in(path, fun) (macro)
通过给定的更新嵌套结构path
。
这与update_in/3
类似,除了通过宏提取路径而不是传递列表。例如:
update_in(opts[:foo][:bar], &(&1 + 1))
相当于:
update_in(opts, [:foo, :bar], &(&1 + 1))
请注意,为了使这个宏能够工作,完整的路径必须总能被这个宏看到。有关支持的路径表达式的更多信息,请查看get_and_update_in/2
文档。
例子
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> update_in(users["john"][:age], &(&1 + 1))
%{"john" => %{age: 28}, "meg" => %{age: 23}}
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> update_in(users["john"].age, &(&1 + 1))
%{"john" => %{age: 28}, "meg" => %{age: 23}}
update_in(data, keys, fun)
update_in(Access.t, [term, ...], (term -> term)) :: Access.t
更新嵌套结构中的密钥。
除非是函数,否则使用Access
模块根据给定的结构遍历结构。如果该键是一个函数,它将按照中指定的方式被调用。keyskeyget_and_update_in/3
例子
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> update_in(users, ["john", :age], &(&1 + 1))
%{"john" => %{age: 28}, "meg" => %{age: 23}}
如果中间的任何条目返回nil
,则下一次尝试访问时会出现错误。
use(module, opts \ []) (macro)
在当前上下文中使用给定的模块。
调用时:
use MyModule, some: :options
调用MyModule模块中的__using __ / 1宏,并传递第二个参数作为参数。 由于__using __ / 1是一个宏,所有常用的宏规则都适用,并且它的返回值应该是带引号的代码,然后插入调用use / 2的地方。
例子
例如,为了使用ExUnit
Elixir提供的框架编写测试用例,开发人员应该使用use
以下ExUnit.Case
模块:
defmodule AssertionTest do
use ExUnit.Case, async: true
test "always pass" do
assert true
end
end
在这个例子中,ExUnit.Case.__using__/1
以关键字列表[async: true]
作为参数被调用; use/2
转化为:
defmodule AssertionTest do
require ExUnit.Case
ExUnit.Case.__using__([async: true])
test "always pass" do
assert true
end
end
ExUnit.Case
然后将定义__using__/1
宏:
defmodule ExUnit.Case do
defmacro __using__(opts) do
# do something with opts
quote do
# return some code to inject in the caller
end
end
end
最佳实践
__using__/1
通常在需要将某些状态(通过模块属性)或回调(例如@before_compile
,请参阅文档以Module
获取更多信息)设置给调用者时使用。
__using__/1
也可以用于别名,要求或从不同模块导入功能:
defmodule MyModule do
defmacro __using__(_opts) do
quote do
import MyModule.Foo
import MyModule.Bar
import MyModule.Baz
alias MyModule.Repo
end
end
end
但是,如果它只是导入,别名或需要模块本身,请不要提供__using __ / 1。 例如,避免这种情况:
defmodule MyModule do
defmacro __using__(_opts) do
quote do
import MyModule
end
end
end
在这种情况下,开发人员应该直接导入或别名模块,以便他们可以按自己的意愿自定义这些模块,而不需要间接寻址use/2
。
最后,开发人员还应该避免在__using__/1
回调中定义函数,除非这些函数是以前定义的默认实现@callback
或者是被覆盖的函数(请参阅参考资料defoverridable/1
)。即使在这些情况下,定义函数也应该被视为“最后的手段”。
如果您想为用户模块提供一些现有功能,请将其定义在将相应导入的模块中; 例如,ExUnit.Case
没有定义test/3
调用的模块中的宏use ExUnit.Case
,但是它定义ExUnit.Case.test/3
并在使用时将其导入到调用者中。
var!(var, context \ nil) (macro)
在引用中使用时,标记给定的变量不应卫生。
参数可以是一个未加引号的变量,也可以是标准元组形式{name, meta, context}
。
检查Kernel.SpecialForms.quote/2
更多信息。
left |> right (macro)
管道运营商。
该运算符在左侧引入表达式作为右侧函数调用的第一个参数。
例子
iex> [1, [2], 3] |> List.flatten()
[1, 2, 3]
上面的例子与调用List.flatten([1, [2], 3])
相同
|>
当需要执行一系列类似于流水线的操作时,操作员非常有用:
iex> [1, [2], 3] |> List.flatten |> Enum.map(fn x -> x * 2 end)
[2, 4, 6]
在上面的例子中,列表[1, [2], 3]
作为List.flatten/1
函数的第一个参数传递,然后扁平化列表作为Enum.map/2
函数的第一个参数传递给函数,该函数将列表中的每个元素加倍。
换句话说,上述表达式简单地转换为:
Enum.map(List.flatten([1, [2], 3]), fn x -> x * 2 end)
陷阱
使用管道操作员时有两个常见的缺陷。
第一个与运营商优先权有关。例如,下面的表达式:
String.graphemes "Hello" |> Enum.reverse
转换为:
String.graphemes("Hello" |> Enum.reverse)
这会导致错误,因为该Enumerable
协议未针对二进制文件定义。添加明确的括号可以消除模糊性:
String.graphemes("Hello") |> Enum.reverse
或者,甚至更好:
"Hello" |> String.graphemes |> Enum.reverse
第二个缺陷是|>
运营商致力于通话。例如,当你写:
"Hello" |> some_function()
Elixir看到右侧是一个函数调用并将其传递给它。这意味着,如果你想管道到一个匿名或捕获的函数,它也必须显式调用。
鉴于匿名功能:
fun = fn x -> IO.puts(x) end
fun.("Hello")
这不会起作用,因为它会尝试调用本地函数fun
:
"Hello" |> fun()
这工作:
"Hello" |> fun.()
正如你所看到的,|>
操作符保留了与不使用管道时相同的语义,因为两者都需要fun.(...)
符号。
left || right (macro)
提供一个短路运算符,仅当第一个表达式不计算为true
(即,它是nil
或false
)时才评估并返回第二个表达式。否则返回第一个表达式。
警卫条款不允许。
例子
iex> Enum.empty?([1]) || Enum.empty?([1])
false
iex> List.first([]) || true
true
iex> Enum.empty?([1]) || 1
1
iex> Enum.empty?([]) || throw(:bad)
true
请注意,与or/2
此不同,此运算符接受任何表达式作为第一个参数,而不仅仅是布尔值。
本文档系腾讯云开发者社区成员共同维护,如有问题请联系 cloudcommunity@tencent.com