- 代码的基本单元 --- 模块
- 定义公开和私有命名函数
- 哨兵子句
- 模块指令和属性
- 调用Erlang模块里的函数
建立times.exs文件
defmodule Times do
def double(n) do
n * 2
end
end
- 编译文件
方法一: 在进入iex之前
iex 源文件名
$ iex times.exs
Erlang/OTP 21 [erts-10.1.1] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] [dtrace]
Interactive Elixir (1.7.3) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Times.double 4
8
iex(2)> Times.double 44
88
方法二:已经在iex下面 通过
c "源文件名称"
编译
iex(3)> c "times.exs"
warning: redefining module Times (current version defined in memory)
times.exs:1
[Times]
iex(4)> Times.double 56
Elixir里标识函数的名字为
名字加上参数的个数
,形如:Times.double/1
1代表一个参数
函数体是代码块
代码块用
do ... end
来组织,它被用在模块和命名函数的定义、控制结构
等需要将代码作为整体对待的地方。
do .... end 只是语法糖 真实的语法类似于:
def double(n), do: n * 2
多条语句需要用来圆括号
()
iex(7)> def greet(greeting,name),do: (
...(7)> IO.puts greeting
...(7)> IO.puts "how're you doing,#{name}"
...(7)> )
- 单行代码使用
do : 语法
defmodule Times do
def double(n) ,do: n * 2
def triple(n) ,do: n * n
end
- 多行代码用
do ... end 语法
defmodule Times do
def double(n) do
n * 2
end
end
函数调用与模式匹配
- n 的阶乘实例
阶乘表示
n!
约定0!
是1
iex(4)> defmodule Factorial do
...(4)> def of(0),do: 1
...(4)> def of(n) when n > 0 do n * of(n-1) end
...(4)> end
{:module, Factorial,
<<70, 79, 82, 49, 0, 0, 4, 88, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 130,
0, 0, 0, 15, 16, 69, 108, 105, 120, 105, 114, 46, 70, 97, 99, 116, 111, 114,
105, 97, 108, 8, 95, 95, 105, 110, 102, ...>>, {:of, 1}}
iex(5)> Factorial.of
of/1
iex(5)> Factorial.of(2)
2
iex(6)>
Factorial.of(2)
在调用时如下匹配执行: 匹配of(2)
->of(1)
->of(0)
,其中of(2)和of(1)匹配的是of(n)
小结:这种情况,首先考察最简单的情况,某个确定的值如: 0的阶乘是1,以它为
初始条件
,之后寻找一个递归方案
,其最终会调用初始的条件
模式匹配让Elixir按传入的参数来确定哪个函数被调用
类似的例子:
求前n项的和
- 第一项0的和是0,
- 前n项的和等于n加上前n-1项
列表的长度:
- 空列表的长度是0
- 任意列表的长度等于 1加上该列表尾部的长度
哨兵子句
使用场景:对参数进行类型判断,如:
is_number(x)
,is_atom(x)
是否是原子,is_list(x)
是否是列表;对参数值进行测试,如:n>0
(参数大于0)
- 它由一个或多个
when
字键字跟在函数定义之后
实例:
defmodule Guard do
def what_is(x) when is_number(x) do
IO.puts "#{x} is a number"
end
def what_is(x) when is_list(x) do
IO.puts "#{inspect(x)} is a list"
end
def what_is(x) when is_atom(x) do
IO.puts "#{x} is a atom"
end
end
- 比较运算符
==, !=, ===, !==, >, <, <=, >=
- 布尔和求逆运算符
or, and , not, !
不支持 && 和 ||
- 算术运算符
+ , - , * , /
- 连接运算符
<>
和++
,只要运算符的左边是字面量(字符串)便能使用
例如:"foo"<> x = "foobar"
正确 ,x<>"bar" = "footbar"
会出现异常
- 类型检查函数
is_atom , is_binary,is_bitstring , is_boolean,........
- 还有其它会返回(
非 true 或者 false
)的内置函数
默认参数
- 语法为
形参 \\ 值
实例1:
defmodule Example do
def func(p1, p2 \\ 2, p3 \\ 3, p4) do
IO.inspect [p1, p2, p3, p4] //省略了()所以inspect 后要有空格
end
end
运行结果
iex(1)> c "default_params.exs"
[Example]
iex(2)> Ex
Example Exception
iex(2)> Example.func("a","b")
["a", 2, 3, "b"]
["a", 2, 3, "b"]
iex(3)> Example.func("a","b","c")
["a", "b", 3, "c"]
["a", "b", 3, "c"]
iex(4)> Example.func("a","b","c","d")
["a", "b", "c", "d"]
["a", "b", "c", "d"]
iex(5)>
实例2
defmodule Params do
def func(p1, p2 \\ 123)
def func(p1, p2) when is_list(p1) do
"You said #{p2} with a list"
end
def func(p1, p2) do
"You passed in #{p1} and #{p2}"
end
end
运行结果
iex(1)> c "default_params2.exs"
[Params]
iex(2)> Params.func(99)
"You passed in 99 and 123"
iex(3)> Params.func(99,"cat")
"You passed in 99 and cat"
iex(4)> Params.func([99])
"You said 123 with a list"
iex(5)> Params.func([99],"dog")
"You said dog with a list"
iex(6)>
私有函数
关键字
defp
用来定义私有函数,只能在函数的内部调用
- 公有函数头部和私有函数头部不参重名
|> 管道运算符
作用:
把左边函数调用的结果做为右边函数的第一个参数
filing = DB.find_customers
|> Orders.for_customers
|> sales_tax(2013)
|> prepare_filing
val |> f(a,b)
等价于f(val,a,b)
list
|> sales_tax(2013)
|> prepare_filing
等同于prepare_filing(sales_tax(list,2013))
在管道中要用()把函数参数列表包含起来
iex > (1...10) |> Enum.map(&(&1*&1)) |> Enum.filter(&(&1 < 40))
模块
模块可以用来封装
命合函数 ,宏,结构体,协议,和其他模块
引用:模块的外部引用:
模块名.函数名
;代码内引用同一模块的东西,不用加模块名前缀;
- 嵌套模块
在外部访问
嵌套模块
的函数:需要加上所有的模块名作为前缀。在模块内部调用时,要么使用完全的限名,要么使用内部的
defmodule Outer do
defmodule Inner do
def inner_func do
....
end
end
def outer_func do
Inner.inner_func
end
end
引用
Outer.outer_func
和Outer.Inner.inner_func
- 模块嵌套的另一种自定义
当我们在某个模块内部定义模块时,Elixir仅仅在内部模块名前加上外部模块的名,用点号来连接。
defmodule Mix.Tasks.Doctest do
def run do
...
end
end
模块指令
- import 指令
defmodule Example do
def func1 do
List.flatten([1, [2, 3], 4])
end
def func2 do
import List, only: [flatten: 1] // :(冒号)后面要包含空格
flatten([5, [6, 7], 8])
end
end
完整语法是
import Module [, only:|except: ]
第二个可选参数决定导入哪个函数和宏:可用用only
和except
- alias指令
作用是为模块指定别名
defmodule Example do
def func do
alias Mix.Tasks.Doctest, as: Doctest
doc = Doctest.setup()
doc.run(Doctest.defaults())
end
end
alias
指令可以简写alias Mix.Tasks.Doctest
,因为as:的参数默认是最后一个模块名
- require指令
用来引用宏
模块属性
Elixir 模块相关联的元数据被称为模块的属性
用过@名称
来调用
-
@name value
用来定义属性:只能在模块的顶层定义,在函数内部用@名称
来引用
defmodule Example do
@author "Steven Qin"
def get_author do
@author
end
end
调用:
iex(2)> c "attributes.exs"
[Example]
iex(3)> IO.puts "Example was written by #{Example.get_author}"
Example was written by Steven Qin
:ok
iex(4)>
- 可以为属性多次赋值
defmodule Example do
@attr "one"
def first, do: @attr
@attr "two"
def second, do: @attr
end
IO.puts "#{Example.first} #{Example.second}"
模块名: Elixir Erlang 和原子类型
- 模块名其它是原子类型,比如:
IO
,Elixir内部将其转换成Elixir.IO
下面的调用是等价的
iex(1)> IO.puts 123
123
:ok
iex(2)> :"Elixir.IO".puts 123
123
:ok
iex(3)>
调用Erlang的库函数
Erlang的命名约定:
变量以大写字母开头,原子的名字都是小写字母
调用Erlang的模块
timer
在Elixir中要写作:timer
,调用它的tc函数写作::timer.tc
(注意要用:开头)