Elixir程序设计笔记(6模块与命名函数)

  • 代码的基本单元 --- 模块
  • 定义公开和私有命名函数
  • 哨兵子句
  • 模块指令和属性
  • 调用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_funcOuter.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: ] 第二个可选参数决定导入哪个函数和宏:可用用onlyexcept

  • 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 (注意要用:开头)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,884评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,212评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,351评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,412评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,438评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,127评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,714评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,636评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,173评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,264评论 3 339
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,402评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,073评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,763评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,253评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,382评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,749评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,403评论 2 358

推荐阅读更多精彩内容

  • 一、温故而知新 1. 内存不够怎么办 内存简单分配策略的问题地址空间不隔离内存使用效率低程序运行的地址不确定 关于...
    SeanCST阅读 7,818评论 0 27
  • 写在前面的话 代码中的# > 表示的是输出结果 输入 使用input()函数 用法 注意input函数输出的均是字...
    FlyingLittlePG阅读 2,764评论 0 8
  • 一、标准流和浮动 1.标准流 标准流布局:在标准流中,块级标签是一个占一行,默认宽度是父标签的宽度,默认高度是内容...
    Voyaserfuerte阅读 81评论 0 0
  • 记忆中从未给父亲撒过娇,也不记得母亲的样子,一直追求独特,渴望出类拔萃,23年来背负着沉重的压力,保持着斯...
    丫头Alisa阅读 152评论 0 0
  • 应是一番情未了 夜露缠绵 雪被依到金阳照 虽知明夜又相逢 亦难别离 冷面寒霜不解强颜笑 佳期短期 应是别离正惨凄 ...