Elixir-模块与命名函数

上节接触到了匿名函数,这次了解下命名函数。

Elixir 的命名函数必须写在模块里。

示例

定义模块

1
2
3
4
5
defmodule Demo do
def double(n) do
n * 2
end
end

通常单行使用do:语法,多行使用do…end语法。

命令提示符/终端时候用 iex + 文件
加载模块,iex模式下用 c + 文件

1
2
3
iex> c "E:\\Elixir_workspace\\hello.exs"
iex> Demo.double 5
10

函数的模式匹配

看示例:求 n 的阶乘 (递归)

1
2
3
4
defmodule Demo do
def of(0), do: 1
def of(n), do: n * of(n-1)
end
1
2
iex> Demo.of 5
120

有一点要注意,子句顺序不同会产生不同的结果。Elixir会自上而下依次尝试,执行最先匹配的一项。

所以如果上述 def 位置交换将会行不通;第一个定义总是匹配,而第二个永远不会被调用。

1
2
3
4
5
#error
defmodule Demo do
def of(n), do: n * of(n-1)
def of(0), do: 1
end

哨兵子句

加入需要对类型进行判断,或者参数做些匹配怎么办?

我们用哨兵子句(guard clause),一个或多个由 when 关键字接在函数定义之后的断言。

1
2
3
4
5
6
7
Elixir
defmodule Demo do
def what_is(x) when is_number(x), do: IO.puts "#{x} is a number"
def what_is(x) when is_list(x), do: IO.puts "#{x} is a list"
def what_is(x) when is_atom(x), do: IO.puts "#{x} is a atom"
def what_is(x), do: IO.puts "#{x} is a unkown type"
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
iex> c "E:\\Elixir_workspace\\hello.exs"
[Demo]
iex> Demo.what_is 3
3 is a number
:ok
iex> Demo.what_is [1,2,3]
^A^B^C is a list
:ok
iex> Demo.what_is :red
red is a atom
:ok
iex> Demo.what_is "333"
333 is a unkown type
:ok

[1, 2, 3]分别打印的时候正好能匹配字符,Elixir会自动以对应字符形式输出。

优化之前的递归,以免输入负数造成死循环。

1
2
3
4
defmodule 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
5
defmodule Demo do
def func(p1, p2 \\ 2, p3 \\ 3, p4) do
IO.inspect [p1, p2, p3, p4]
end
end

体会下以下输出结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
iex> c "E:\\Elixir_workspace\\hello.exs"
[Demo]
iex> Demo.func 1,4
[1, 2, 3, 4]
[1, 2, 3, 4]
iex> Demo.func 2
** (UndefinedFunctionError) undefined function Demo.func/1
Demo.func(2)
iex> Demo.func 1,3,4,5
[1, 3, 4, 5]
[1, 3, 4, 5]
iex> Demo.func 1,3,4
[1, 3, 3, 4]
[1, 3, 3, 4]

私有函数

defp 来定义私有函数,该函数仅能在生命它的模块内被调用。

1
2
def fun(a) when is_list(a), do: true
defp fun(a), do: false

管道运算符

关键字: |>

类似 linux 的管道,前面的输出作为后面的输入。

举个例子:

1
2
3
iex> (1..10) |> Enum.map(&(&1 * &1)) |> Enum.filter(&(&1 < 50))
[1, 4, 9, 16, 25, 36, 49]
iex>

第一段,一个区间
流向第二段,区间的每个值平方
新的列表流向第三段,筛选出 < 50 的值。
最后输出。(涉及一些自带函数就不多做解释。如:Enum.filter

内外模块

在外部访问嵌套模块内的函数,需要加上所有的模块名作为前缀。
(同一个模块内访问不需要)

1
2
3
4
5
6
7
8
9
10
defmodule Outer do 
defmodule Inner do
def inner_func do
end
end

def outer_func do
Inner.inner_func
end
end

import 指令

import 指令将模块内的函数/宏引入到当前作用域。
import 可以减少一遍遍重复模块名,保持源文件的整洁。

如下:

1
2
3
4
5
6
7
8
9
10
defmodule Outer do 
defmodule func1 do
List.flatten [1, [2, 3], 4]
end

def func2 do
import List, only: [flatten: 1]
flatten [1, [2, 3], 4]
end
end

完成语法:
import Module [,only: |except: ]

后面跟随一组 name: arity 对。
尽可能的缩小对 import 的作用域。

alias 指令

alias 指令模块创建别名,显然,它的作用是减少输入。

1
alias Mix.Tasks.Doctest, as Doctest

模块属性

每个 Elixir 模块都有与之关联的元数据,元数据每一项成为模块的属性。模块内部可以通过名称前加 @ 符号访问属性。

1
@name value

并不是传统意义上的变量,仅作为配置和元数据使用。

Elixir 函数库

可以快速浏览下官网上 Elixir 已有的模块,还有如 hex.pmGithub

看官赏点饭钱可好~