elixir 入门笔记
安装
MAC 平台用 brew 安装
brew update
brew install elixir
如果没有 erlang 环境,上面的命令会自定安装 erlang 的环境。
基本数据类型
iex> 1 # integer
iex> 0x1F # integer
iex> 1.0 # float
iex> true # boolean
iex> :atom # atom / symbol
iex> "elixir" # string
iex> [1, 2, 3] # list
iex> {1, 2, 3} # tuple
数学运算
iex> 1 / 2
1 / 2
0.5
/ 总是返回浮点数,如果需要整数运算,使用 div 和 rem 函数
iex> div(1, 2)
div(1, 2)
0
iex> rem(1, 2)
rem(1, 2)
1
二进制,八进制,十六进制表示方式
iex> 0b10000
0b10000
16
iex> 0o20
0o20
16
iex> 0x10
0x10
16
原子
原子是一种常量,变量名就是它的值。有2种写法:
- :原子名
- :"原子名"
列表
列表中可以包含任意数据类型
iex> [1, 1.2, true, "hello"]
[1, 1.2, true, "hello"]
[1, 1.2, true, "hello"]
列表可以通过 ++/– 来拼接
iex> [1, 2, true] ++ [1, 3, false]
[1, 2, true] ++ [1, 3, false]
[1, 2, true, 1, 3, false] iex> [1, 2, true, 2, false] -- [1, 3, false, 2]
[1, 2, true, 2, false] -- [1, 3, false, 2]
[true, 2]
可以通过 hd/1 tl/1 函数来获取头部和头部以外的部分
iex> hd([1, 2, true])
hd([1, 2, true])
1 iex> tl([1, 2, true])
tl([1, 2, true])
[2, true]
对一个空列表执行 hd/1 和 tl/1 会报错
iex> hd([])
hd([])
** (ArgumentError) argument error
:erlang.hd([]) iex> tl([])
tl([])
** (ArgumentError) argument error
:erlang.tl([])
列表和元组区别
- 列表是以链表形式在内存存储的,元组是在内存中连续存储的。
- 列表前置拼接操作很快,后置拼接操作慢
后置拼接时修改了原列表的最后一个元素,所以会重建整个列表 - 函数的返回值一般用元组来保存
字符串和字符列表
- 双引号包裹的是字符串: 字符串中存储的是 byte
- 单引号包裹的是字符列表: 字符列表中存储的是每个字符的 codepoint
基本运算符
算术运算 + - * / div/2 rem/2
列表拼接 ++ –
字符串拼接 <>
iex> "foo" <> "bar"
"foo" <> "bar"
"foobar"
布尔运算符
or and not 这3个运算符只接受布尔值作为第一个参数
iex> true or 1
true or 1
true iex> 1 or true
1 or true
** (ArgumentError) argument error: 1
|| && ! 这3个运算符可以接受非布尔值作为第一个参数
iex> 1 || true
1 || true
1 iex> true || 1
true || 1
true
比较运算符
= !
=
!== <= >= < >= !
和=
!== 相比,后者的检查更加严格iex> 1 == 1.0
1 == 1.0
true iex> 1 === 1.0
1 === 1.0
false iex> 1 != 1.0
1 != 1.0
false iex> 1 !== 1.0
1 !== 1.0
true
不同数据类型之间也可以比较大小
iex> 1 < "hello"
1 < "hello"
true iex> 1 > "hello"
1 > "hello"
false iex> 1 < [1, 2]
1 < [1, 2]
true iex> "hello" < [1, 2]
"hello" < [1, 2]
false
不同数据类型之间的默认的顺序如下:
number < atom < reference < functions < port < pid < tuple < maps < list < bitstring
模式匹配
elixir 中 = 是模式匹配运算符
可以给list 的 head 和 tail 赋值
iex> [h|t]=[1,2,3]
[1, 2, 3]
iex> h
1
iex> t
[2, 3]
控制语句
case
iex> case {1, 2, 3} do
...> {4, 5, 6} ->
...> "This clause won't match"
...> {1, x, 3} ->
...> "This clause will match and bind x to 2 in this clause"
...> _ ->
...> "This clause would match any value"
...> end
case的条件中可以加入判断的表达式,比如下面的 (when x > 0)
iex> case {1, 2, 3} do
...> {1, x, 3} when x > 0 ->
...> "Will match"
...> _ ->
...> "Won't match"
...> end
cond
iex> cond do
...> 2 + 2 == 5 ->
...> "This will not be true"
...> 2 * 2 == 3 ->
...> "Nor this"
...> 1 + 1 == 2 ->
...> "But this will"
...> 3 + 3 == 6 ->
...> "But this will too"
...> end
"But this will"
只会执行第一个匹配上的分支
if/unless
iex> if nil do
...> "This won't be seen"
...> else
...> "This will"
...> end
"This will"
unless 和 if 相反,条件为false时才执行
iex> unless true do
...> "This won't be seen"
...> else
...> "This will"
...> end
"This will"
do
do 语句快有2种写法:
iex> if true do
...> "this is true"
...> else
...> "this is false"
...> end
OR
iex> if true, do: ("this is true"), else: ("this is false")
键值列表-图-字典
键值列表
iex> l = [{:a, 1},{:b, 2}]
[a: 1, b: 2]
iex> l[:a]
1
iex> l[:b]
2
键值列表还有另一种定义方式:(注意 a: 和 1 之间必须有个空格)
iex> l = [a: 1, b: 2]
[a: 1, b: 2]
键值列表2个特点:
- 有序
- key 可以重复,重复时,优先取排在前面的key
iex> l = [a: 3] ++ l;
[a: 3, a: 1, b: 2]
iex> l
[a: 3, a: 1, b: 2]
iex> l[:a]
3
图
图的2个特点:
- 图中的key是无序的
- 图的key可以是任意类型
iex> map = %{:a => 1, 2 => :b}
%{2 => :b, :a => 1}
图匹配时,只要 = 右边包含左边的值就能匹配上
iex> %{} = %{:a => 1, 2 => :b}
%{2 => :b, :a => 1}
iex> %{:a => 1, 2 => :b} = %{}
** (MatchError) no match of right hand side value: %{}
iex> %{:a => 1} = %{:a => 1, 2 => :b}
%{2 => :b, :a => 1}
iex> %{:a => 1, :c => 2} = %{:a => 1, 2 => :b}
** (MatchError) no match of right hand side value: %{2 => :b, :a => 1}
修改图中的值可以用以下的方式:
iex> %{map | 2 => :c}
%{2 => :c, :a => 1}
字典
以上的 键值列表 和 图 都是 字典 ,它们都实现了 Dict 接口。
此模块现在已经 deprecated
模块和函数定义
模块和函数定义方式
defmodule Math do
def sum(a, b) do
do_sum(a, b)
end
defp do_sum(a, b) do
a + b
end
end
Math.sum(1, 2) #=> 3
Math.do_sum(1, 2) #=> ** (UndefinedFunctionError)
函数中的卫兵表达式
defmodule Math do
def zero?(0) do
true
end
def zero?(x) when is_number(x) do
false
end
end
Math.zero?(0) #=> true
Math.zero?(1) #=> false
Math.zero?([1,2,3])
#=> ** (FunctionClauseError)
默认参数
defmodule Concat do
def join(a, b, sep \\ " ") do
a <> sep <> b
end
end
IO.puts Concat.join("Hello", "world") #=> Hello world
IO.puts Concat.join("Hello", "world", "_") #=> Hello_world
枚举类型和流
枚举类型
枚举类型提供了大量函数来对列表进行操作
iex> Enum.sum([1,2,3])
6
iex> Enum.map(1..3, fn x -> x * 2 end)
[2, 4, 6]
iex> Enum.reduce(1..3, 0, &+/2)
6
iex> Enum.filter(1..3, &(rem(&1, 2) != 0))
[1, 3]
枚举操作都是积极的,比如如下的操作:
iex> odd? = &(rem(&1, 2) != 0)
#Function<6.54118792/1 in :erl_eval.expr/5>
iex> 1..100_000 |> Enum.map(&(&1 * 3)) |> Enum.filter(odd?) |> Enum.sum
7500000000
以上每步的操作(Enum.map, Enum.filter)都会产生一个新的列表,这就是 积极 的意思。
流
和上面的枚举类型对应,流的处理是 懒惰 的,比如:
iex> 1..100_000 |> Stream.map(&(&1 * 3)) |> Stream.filter(odd?) |> Enum.sum
7500000000
表面上看,和枚举类型的处理一样,而实际上,流先创建了一系列的计算操作。然后仅当我们把它传递给Enum模块,它才会被调用。
iex> stream = 1..100_000 |> Stream.map(&(&1 * 3)) |> Stream.filter(odd?)
#Stream<[enum: 1..100000,
funs: [#Function<23.27730995/1 in Stream.map/2>,
#Function<8.27730995/1 in Stream.filter/2>]]>
iex> Enum.sum(stream) <== 这里才开始执行
7500000000
进程
elixir中进程都是轻量级的,所以使用时不用太在意进程的数目。
派生的进程执行完自动结束自己
iex> pid = spawn fn -> 1 + 2 end
#PID<0.62.0>
iex> Process.alive?(pid)
false
发送和接收消息
下面示例中是给自己发送了一条消息,可以通过 flush/1 函数刷新消息,刷新一次之后就iex> send self(), {:hello, "world"}
{:hello, "world"}
iex> flush
{:hello, "world"}
:ok
iex> flush
:ok
也可以通过 receive/1 函数来接收
iex> send self(), {:hello, "world"}
{:hello, "world"}
iex> receive do
...> {:hi, msg} -> msg
...> {:hello, msg} -> msg
...> end
"world"
receive/1 函数收不到消息会阻塞,可以给它设置一个超时时间
iex> receive do
...> {:hello, msg} -> msg
...> after
...> 3000 -> "timeout"
...> end
"timeout"
进程间的连接
进程B连接进程A之后,进程A出现异常,进程B就能捕获,这样进程B就能处理进程A的异常
进程连接的方式很简单,就是 spawn_link/1 函数iex> spawn fn -> raise "oops" end
#PID<0.76.0> 15: 18:22.804 [error] Process #PID<0.76.0> raised an exception
** (RuntimeError) oops
:erlang.apply/2 iex> spawn_link fn -> raise "oops" end
** (EXIT from #PID<0.73.0>) an exception was raised:
** (RuntimeError) oops
:erlang.apply/2 15: 18:31.533 [error] Process #PID<0.78.0> raised an exception
** (RuntimeError) oops
:erlang.apply/2
关注 Process模块 模块,里面提供了进程操作的函数
进程中保存状态的方法:
defmodule KV do
def start do
{:ok, spawn_link(fn -> loop(%{}) end)}
end defp loop(map) do
receive do
{:get, key, caller} ->
send caller, Map.get(map, key)
loop(map)
{:put, key, value} ->
loop(Map.put(map, key, value))
end
end
end
iex> send pid, {:put, :hello, :world}
#PID<0.62.0>
iex> send pid, {:get, :hello, self()}
{:get, :hello, #PID<0.41.0>}
iex> flush
:world
实际使用时,可以用 Agent 模块 来简化上面的操作。
模块属性
elixir中模块的属性主要有3个作用:
- 作为一个模块的注释,通常附加上用户或虚拟机用到的信息
- 作为常量
- 在编译时作为一个临时的模块存储
注释
注释时,一些常用的模块属性如下:
名称 | 含义 |
---|---|
@moduledoc | 为当前模块提供文档 |
@doc | 为该属性后面的函数或宏提供文档 |
@behaviour | (注意这个单词是英式拼法)用来注明一个OTP或用户自定义行为 |
@before\_compile | 提供一个每当模块被编译之前执行的钩子。这使得我们可以在模块被编译之前往里面注入函数。 |
常量
作为常量:
defmodule MyServer do
@my_data 14
def first_data, do: @my_data
@my_data 13
def second_data, do: @my_data
end
测试方法:
iex> MyServer.first_data
14
iex> MyServer.second_data
13
临时存储
模块中的变量只在编译时存在,所以用做临时存储,存储一些只在编译时使用的变量。
示例:
defmodule MyServer do
@my_data 14
def first_data, do: @my_data
@my_data 13
def second_data, do: @my_data
end
iex> MyServer.first_data #=> 14
iex> MyServer.second_data #=> 13
结构体
定义
defmodule User do
defstruct name: "harry", age: 32
end
使用方式
iex> j = %User{}
%User{age: 32, name: "harry"} iex> j.name
"harry" iex> j[:name]
** (UndefinedFunctionError) undefined function User.fetch/2
User.fetch(%User{age: 32, name: "harry"}, :name)
(elixir) lib/access.ex:77: Access.get/3 iex> j.__struct__
User
协议
协议类似于其他语言中的接口,谁实现了协议,谁就可以使用协议,
比如下面的例子,Integer 和 User 结构体实现了协议,就可以使用协议中的方法。
defmodule User do
defstruct name: "harry", age: 32
end
defprotocol Enough do
def enough?(data)
end
defimpl Enough, for: Integer do
def enough?(data) do
if data > 0 do
true
else
false
end
end
end
defimpl Enough, for: User do
def enough?(data) do
if data.age > 18 do
true
else
false
end
end
end
使用示例:
iex> Enough.enough?(11)
true
iex> Enough.enough?(0)
false
iex> u = %User{}
%User{age: 32, name: "harry"}
iex> Enough.enough?(u)
true
iex> u = %{u|age: 10}
%User{age: 10, name: "harry"}
iex> Enough.enough?(u)
false
iex> Enough.enough?("string")
** (Protocol.UndefinedError) protocol Enough not implemented for "string"
iex:3: Enough.impl_for!/1
iex:4: Enough.enough?/1
上面的 string 类型没有实现协议,所以不能使用。
我们在实际使用中也不会对没种类型都实现协议,为了避免出现异常,可以设置协议对所有类型的默认实现
defprotocol Enough do
@fallback_to_any true
def enough?(data)
end
defimpl Enough, for: Any do
def enough?(_), do: false
end
这样以后,如下使用就不会报错了
iex> Enough.enough?("string")
false
异常处理
自定义异常
自定义异常使用 defexception/1 函数,
iex> h(defexception)
The most common way to raise an exception is via raise/2:
┃ defmodule MyAppError do
┃ defexception [:message]
┃ end
┃
┃ value = [:hello]
┃
┃ raise MyAppError,
┃ message: "did not get what was expected, got: #{inspect value}"
In many cases it is more convenient to pass the expected value to raise/2 and
generate the message in the exception/1 callback:
┃ 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
The example above shows the preferred strategy for customizing exception
messages.
异常的使用
elixir 虽然提供了 try/catch/rescue/after 的结构,但是尽量不要使用这种结构,使用这种异常处理方式,会影响现有程序的处理流程。
elixir 的很多函数都会返回错误信号,通过信号来处理错误是推荐的方式(类似golang的错误处理),比如如下示例:
iex> case File.read "hello" do
...> {:ok, body} -> IO.puts "got ok"
...> {:error, body} -> IO.puts "got error"
...> end
列表速构
速构的意思也就是从一个列表方便的生成另一个列表。
生成器
iex> l = for n <- [1, 2, 4], do: n*n
[1, 4, 16]
iex> l
[1, 4, 16]
过滤器
iex> require Integer iex> for n <- 1..4, Integer.is_odd(n), do: n*n
[1, 9]
二进制转化为元组
iex> pixels = <<213, 45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15>>
<<213, 45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15>> iex> for <<r::8, g::8, b::8 <- pixels>>, do: {r, g, b}
[{213, 45, 132}, {64, 76, 32}, {76, 0, 0}, {234, 32, 15}]
into
删除空格
iex> for <<c <- " hello world ">>, c != ?\s, into: "", do: <<c>>
"helloworld"
sigils(魔法印)
sigils 也就是对已有的变量或者常量做一些标记,使之变为其他的东西。
sigils 的目的就是提高 elixir 语言的扩展性。
正则表达式中的使用
iex> regex = ~r/foo|bar/
~r/foo|bar/ iex> "foo" =~ regex
true iex> "bat" =~ regex
false
表示字符串,字符以及列表的示例
iex> ~s(this is a string with "quotes")
"this is a string with \"quotes\""
iex> ~c(this is a string with "quotes")
'this is a string with "quotes"'
iex> ~w(foo bar bat)
["foo", "bar", "bat"]
~w 还可以加入其他的修饰符(比如:c, s, a 分别代表字符列表,字符串,原子)
iex> ~w(foo bar bat)a
[:foo, :bar, :bat]
自定义 sigils
iex> defmodule MySigils do
...> def sigil_i(string, []), do: string <> "add_sigil"
...> end iex> import MySigils
iex> ~i("123")
elixir 入门笔记的更多相关文章
- 每天成长一点---WEB前端学习入门笔记
WEB前端学习入门笔记 从今天开始,本人就要学习WEB前端了. 经过老师的建议,说到他每天都会记录下来新的知识点,每天都是在围绕着这些问题来度过,很有必要每天抽出半个小时来写一个知识总结,及时对一天工 ...
- ES6入门笔记
ES6入门笔记 02 Let&Const.md 增加了块级作用域. 常量 避免了变量提升 03 变量的解构赋值.md var [a, b, c] = [1, 2, 3]; var [[a,d] ...
- [Java入门笔记] 面向对象编程基础(二):方法详解
什么是方法? 简介 在上一篇的blog中,我们知道了方法是类中的一个组成部分,是类或对象的行为特征的抽象. 无论是从语法和功能上来看,方法都有点类似与函数.但是,方法与传统的函数还是有着不同之处: 在 ...
- React.js入门笔记
# React.js入门笔记 核心提示 这是本人学习react.js的第一篇入门笔记,估计也会是该系列涵盖内容最多的笔记,主要内容来自英文官方文档的快速上手部分和阮一峰博客教程.当然,还有我自己尝试的 ...
- redis入门笔记(2)
redis入门笔记(2) 上篇文章介绍了redis的基本情况和支持的数据类型,本篇文章将介绍redis持久化.主从复制.简单的事务支持及发布订阅功能. 持久化 •redis是一个支持持久化的内存数据库 ...
- redis入门笔记(1)
redis入门笔记(1) 1. Redis 简介 •Redis是一款开源的.高性能的键-值存储(key-value store).它常被称作是一款数据结构服务器(data structure serv ...
- OpenGLES入门笔记四
原文参考地址:http://www.cnblogs.com/zilongshanren/archive/2011/08/08/2131019.html 一.编译Vertex Shaders和Fragm ...
- OpenGLES入门笔记三
在入门笔记一中比较详细的介绍了顶点着色器和片面着色器. 在入门笔记二中讲解了简单的创建OpenGL场景流程的实现,但是如果在场景中渲染任何一种几何图形,还是需要入门笔记一中的知识:Vertex Sha ...
- unity入门笔记
我于2010年4月1日硕士毕业加入完美时空, 至今5年整.刚刚从一家公司的微端(就是端游技术+页游思想, 具体点就是c++开发, directX渲染, 资源采取所需才会下载)项目的前端主程职位离职, ...
随机推荐
- 第五百八十六天至第六百零五天 how ccan I 坚持
考研中,勿扰... 我是个逗比,哈哈. 时间不够用了呢,哎.
- 优雅的设计单线程范围内的数据共享(ThreadLocal)
单线程范围内数据共享使用ThreadLocal /** * @Description TODO * @author zhanghw@chinatelecom.cn * @since 2015年12月1 ...
- 2016.02.14 总结JS事件
今天主要总结JS事件的基本知识以及使用技巧,并作出相应的DEMO.
- 抽象数据类型ADT
ADT(Abstract Data Type) 类型由什么组成? 一个类型(type)指定两类信息,一个属性集和一个操作集. 假设要定义一个新的数据类型.首先,要提供存储数据的方式,可能是通过设计一个 ...
- swift 代码添加lable
let lable1 = UILabel(frame: CGRect(x: CGFloat(self.view.bounds.width/2-20), y: CGFloat(history.frame ...
- ES 聚合函数
https://segmentfault.com/a/1190000004466154#articleHeader0 http://www.cnblogs.com/didda/p/5485681.ht ...
- display 显示隐藏 ESAYuI
$("#rejectCauseDisplay").css('display',"block"); $("#rejectCauseDisplay ...
- ZT “樱花小萝莉”走红网络 网友:好想生个女儿
“樱花小萝莉”走红网络 网友:好想生个女儿 投递人 itwriter 发布于 2014-04-02 17:39 评论(3) 有717人阅读 原文链接 [收藏] « » 近日,一组被网友亲切地称呼 ...
- Windows下MongoDB环境搭建
MongoDB下载 登录MongoDB官网:www.mongodb.org:点击[Download MongoDB]按钮,进入如下所示界面 选择目标操作系统及其版本,比如这里选择的是64位的Windo ...
- Nginx-->进阶-->Module-->ngx_http_stub_status_module
一.模块介绍 The ngx_http_stub_status_module module provides access to basic status information. This modu ...