上节接触到了匿名函数,这次了解下命名函数。
Elixir
的命名函数必须写在模块里。
示例
定义模块
1 | defmodule Demo do |
通常单行使用do:语法,多行使用do…end语法。
命令提示符/终端时候用 iex
+ 文件
加载模块,iex
模式下用 c
+ 文件
1 | iex> c "E:\\Elixir_workspace\\hello.exs" |
函数的模式匹配
看示例:求 n
的阶乘 (递归)
1 | defmodule Demo do |
1 | iex> Demo.of 5 |
有一点要注意,子句顺序不同会产生不同的结果。Elixir
会自上而下依次尝试,执行最先匹配的一项。
所以如果上述 def
位置交换将会行不通;第一个定义总是匹配,而第二个永远不会被调用。
1 | #error |
哨兵子句
加入需要对类型进行判断,或者参数做些匹配怎么办?
我们用哨兵子句(guard clause
),一个或多个由 when
关键字接在函数定义之后的断言。
1 | Elixir |
1 | iex> c "E:\\Elixir_workspace\\hello.exs" |
[1, 2, 3]
分别打印的时候正好能匹配字符,Elixir
会自动以对应字符形式输出。
优化之前的递归,以免输入负数造成死循环。1
2
3
4defmodule Demo do
def of(0), do: 1
def of(n) when n > 0, do: n * of(n-1)
end
注意:哨兵子句不支持 ||
和 &&
默认参数
param \\
value 的语法可给param设置默认值。1
2
3
4
5defmodule Demo do
def func(p1, p2 \\ 2, p3 \\ 3, p4) do
IO.inspect [p1, p2, p3, p4]
end
end
体会下以下输出结果。
1 | iex> c "E:\\Elixir_workspace\\hello.exs" |
私有函数
defp
来定义私有函数,该函数仅能在生命它的模块内被调用。
1 | def fun(a) when is_list(a), do: true |
管道运算符
关键字: |>
类似 linux
的管道,前面的输出作为后面的输入。
举个例子:
1 | iex> (1..10) |> Enum.map(&(&1 * &1)) |> Enum.filter(&(&1 < 50)) |
第一段,一个区间
流向第二段,区间的每个值平方
新的列表流向第三段,筛选出 < 50
的值。
最后输出。(涉及一些自带函数就不多做解释。如:Enum.filter
)
内外模块
在外部访问嵌套模块内的函数,需要加上所有的模块名作为前缀。
(同一个模块内访问不需要)
1 | defmodule Outer do |
import 指令
import
指令将模块内的函数/宏引入到当前作用域。import
可以减少一遍遍重复模块名,保持源文件的整洁。
如下:
1 | defmodule Outer do |
完成语法:import Module [,only: |except: ]
后面跟随一组 name: arity
对。
尽可能的缩小对 import
的作用域。
alias 指令
alias
指令模块创建别名,显然,它的作用是减少输入。
1 | alias Mix.Tasks.Doctest, as Doctest |
模块属性
每个 Elixir
模块都有与之关联的元数据,元数据每一项成为模块的属性。模块内部可以通过名称前加 @
符号访问属性。
1 | @name value |
并不是传统意义上的变量,仅作为配置和元数据使用。
Elixir 函数库
可以快速浏览下官网上 Elixir
已有的模块,还有如 hex.pm
和 Github
。