elixir 学习

elixir学习

使用 cmd 运行 elixir 语法:iex

在iex 中 使用 c “xxx.ex" 编译运行

编译运行:elixirc xxx.ex

mix new xxx 创建项目

iex -S mix 启动项目

使用 mix deps 查看依赖是否已添加

使用 mix deps.get 下载依赖

基础

原子:

原子类型是名字和代表的值相同的常量

to_charlist(atom): 将原子转换为charlist

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dZAggZHx-1627371357025)(在这里插入图片描述
)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-glT1EFrk-1627371357028)(在这里插入图片描述
)]

to_string(atom): 将原子转换为字符串

字符串:

  1. <> 字符串拼接
  2. #{ var } : 字符串插值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uYwZb0q3-1627371357029)(在这里插入图片描述
)]

iex(1)> string = <<104,101,108,108,111>>
"hello"
iex(2)> string
"hello"
iex(3)> string <> <<104>>
"helloh"

iex(6)> String.replace("Helloeee", "e", "a")
"Halloaaa"

列表:

列表是值的简单集合,可以包含不同的数据类型,而且也可能包含相同的值。

:列表实现是链表,所以最好使用头插法添加元素

  • ++:列表尾部拼接

  • – :列表减法:如果列表中有重复的值,对于右边列表中的出现的每一个值,只会从左边列表(被减列表)中移除一个与之相等的值,即第一个出现的值

  • | :添加元素是最好能用头插法

iex(17)> list = [1,2,3]
[1, 2, 3]
# 获取第一个元素
iex(18)> [head | list] = list
[1, 2, 3]
iex(19)> head
1
iex(20)> list
[2, 3]

iex(30)> list
[4, 2, 3]
iex(31)> [x,y,z | list] = list
[4, 2, 3]
iex(32)> z
3
iex(33)> list
[]

# 添加,删除
iex(21)> list ++ [4,5]
[2, 3, 4, 5]
iex(23)> list ++ 3
[2, 3 | 3]
iex(24)> list -- [2]
[3]
iex(35)> list = [3,2,1]
[3, 2, 1]
iex(36)> list -- [4]
[3, 2, 1]

# 判断是否在列表中
iex(25)> 3 in list
true
iex(26)> 2 in list
true
iex(27)> 1 in list
false

iex(28)> list = [4 | list]
[4, 2, 3]
  • 列表打印
iex(1)> list = [1,2,3]
[1, 2, 3]
iex(2)> IO.puts list   
     
:ok     
iex(3)> IO.puts "#{inspect(list)}"
[1, 2, 3]
:ok      
iex(4)>

元组:

元组和列表很像,但是元组在内存中是连续存放的。 这样的话,获取元组的长度很快,但是修改元组的操作很昂贵:新的元组必须重新在内存中拷贝一份。 定义元组要用花括号

iex(14)> {x,y} = {1,2}
{1, 2}
iex(15)> {z,t} = {1,2}
{1, 2}
iex(16)> {x,y} == {z,t}
true

:一般使用元组进行成功匹配

iex(3)> {:ok,file} = File.open("demo_3.ex")
{:ok, #PID<0.116.0>}

关键字列表:

关键字列表本身是一个列表!只不过里面是k-v形式而已。

关键字列表里的内容是二元元组,并且二元组的第一个元素必须是原子!

iex(4)> list = [{:name, "Quin"},{:city, "Chendu"}] 
[name: "Quin", city: "Chendu"]

iex(1)> aa = [name: "Quin", city: "Chendu"]
[name: "Quin", city: "Chendu"]
iex(2)> list = [{:name, "Quin"},{:city, "Chendu"}]
[name: "Quin", city: "Chendu"]
iex(3)> list == aa                                
true
iex(4)> list === aa 
true

映射(散列表):

  1. 如果重复的键添加到映射中,后面的值会覆盖之前的值

  2. 存储键只有原子的映射,可以不用 =>,直接使用关键字列表的语法

  3. 映射另一个有趣的特性是:它们提供了自己更新和获取原子键(key)的语法

iex(4)> m1 = %{"Quin" => "hello", :zzz => 123}
%{:zzz => 123, "Quin" => "hello"}
# 除原子以外,其他所有都只能 map[] 这种形式访问到值
iex(5)> m1["Quin"]
"hello"
iex(6)> m1.Quin
** (CompileError) iex:6: invalid alias: "m1.Quin". If you wanted to define an alias, an alias must expand to an atom at compile time but it did not, you may use Module.concat/2 to build it at runtime. If instead you wanted to invoke a function or access a field, wrap the function or field name in double quotes
# 原子可以使用 map. 进行访问
iex(6)> m1.zzz
123
iex(7)> m1[:zzz]
123
iex(8)> m1[zzz]
** (CompileError) iex:8: undefined function zzz/0
    (stdlib 3.12) lists.erl:1354: :lists.mapfoldl/3
    (stdlib 3.12) lists.erl:1355: :lists.mapfoldl/3

# 当全为原子的时候 可以简写
iex(12)> m2 = %{a: 1,b: 2,c: 3}
%{a: 1, b: 2, c: 3}
iex(13)> m2.a
1

# 更新散列表
iex(2)> list = [a: 1,b: 2]
iex(6)> map = Enum.into(list,%{c: 3})
%{a: 1, b: 2, c: 3}
iex(7)> map = %{map | c: 4 }
%{a: 1, b: 2, c: 4}
iex(8)> map
%{a: 1, b: 2, c: 4}

注意: 这种语法只在更新一个已经存在于映射的键才有效!如果键不存在,则会抛出 KeyError 错误。

  • 匿名函数

iex(53)> func = fn a -> a + 3 end
#Function<7.126501267/1 in :erl_eval.expr/5>
iex(54)> func.(1)
4
iex(55)> func_2 = &(&1 + &2 * &3 - &4)
#Function<5.126501267/4 in :erl_eval.expr/5>
iex(56)> func_2.(1,2,3,4)
3

Eenu 模块

  • all?

使用 all? 以及大部分 Enum 函数的时候,我们要提供一个函数来作用到要操作的集合上。只有当函数在所有的元素上都返回 true 的时候,all? 才会返回 true,否则结果就是 false

iex(1)> list = ["foo", "bar", "hello"]
["foo", "bar", "hello"]

iex(5)> Enum.all?(list, fn (s) -> String.length(s) > 1 end)
true

iex(2)> Enum.all?(list, &(String.length(&1) > 1))
true

iex(3)> func = &(String.length(&1) > 1)
#Function<7.126501267/1 in :erl_eval.expr/5>
iex(4)> Enum.all?(list, func)
true
  • each

有时候需要遍历某个集合进行操作,但是不想产生新的值(不把函数的遍历调用结果返回),这种情况下,可以使用 each

iex(6)> Enum.each(list , &( IO.puts(&1) ))
foo
bar
hello

注意each 函数会返回原子 :ok

  • map

如果需要把执行结果做为一个新集合返回的话,可以使用map 函数

iex(7)> new_list = Enum.map(list , &( &1 <> " ,Quin" ))
["foo ,Quin", "bar ,Quin", "hello ,Quin"]

:使用 Eenu 实现混合排序

  • into

转换集合类型,也可以合并两个集合

@spec into(Enumerable.t(), Collectable.t(), (term() -> term())) :: Collectable.t()

iex(2)> list = [a: 1,b: 2]
[a: 1, b: 2]
iex(3)> Enum.into(list,%{})
%{a: 1, b: 2}

iex(4)> Enum.into(list,%{c: 3})
%{a: 1, b: 2, c: 3}

iex(5)> Enum.into(list,%{c: 3},fn {k, v} -> {k,v * 2} end)
%{a: 2, b: 4, c: 3}

  • at

根据位置查询列表

@spec at(t(), index(), default()) :: element() | default()

iex(17)> list = [1,3,5,'a',:t]
[1, 3, 5, 'a', :t]
iex(18)> Enum.at list,3
'a'
  • join

将列表转字符串

@spec join(t(), String.t()) :: String.t()

# 只能是列表
iex(13)> list
[1, 2, 3, 4]
iex(14)> Enum.join(list)
"1234"
iex(15)> Enum.join(list,",")
"1,2,3,4"
  • 匿名函数使用
defmodule Test do
  def format(x) do
    x + 10
  end
end

iex(3)> rows = [1,2,3,4,5]
[1, 2, 3, 4, 5]

iex(4)> Enum.map(rows,&Test.format/1)
[11, 12, 13, 14, 15]

iex(5)> Enum.map(rows,&Test.format(&1))
[11, 12, 13, 14, 15]

iex(6)> Enum.map(rows,&(Test.format(&1)))
[11, 12, 13, 14, 15]

iex(7)> Enum.map(rows,fn x -> Test.format(x) end)  
[11, 12, 13, 14, 15]

模式匹配

在 Elixir 当中,用 = 来使得左右两边的值相等。如果匹配成功,即左右值相等后,返回这个等式的值。如果两边的值无法匹配,Elixir 则会抛出错误。

iex(30)> x = 1
1
iex(31)> 1 = x
1
iex(32)> 2 = x
** (MatchError) no match of right hand side value: 1

iex(32)> list = [1,2,3]
[1, 2, 3]
iex(33)> [x,y,z] = list
[1, 2, 3]
iex(34)> x
1
iex(35)> y
2

iex(40)> [1 | tail] = list
[1, 2, 3]
iex(41)> tail
[2, 3]
iex(42)>
  • _ 通配符
iex(37)> [_,x,_] = [1,2,3]
[1, 2, 3]
iex(38)> x
2
  • Pin 操作符:

使用 pin 操作符,我们就是用已经绑定的值去匹配,而不是重新绑定一个新值。

iex(42)> x = 1
1
iex(43)> {x,^x} = {2,1}
{2, 1}
iex(44)> x
2
iex(1)>  greeting = "Hello"
"Hello"
iex(2)> greet = fn
...(2)> (^greeting, name) -> "Hi #{name}"
...(2)> (greeting, name) -> "#{greeting}, #{name}"
...(2)> end
#Function<13.126501267/2 in :erl_eval.expr/5>
iex(3)> greet.("hi","Quin")
"hi, Quin"

iex(5)> greeting = "hi"
"hi"
iex(6)> greet.("hi","Quin")
"hi, Quin"

iex(9)> greet.("Hello","Quin")
"Hi Quin"

iex(13)> greet.(greeting,"Quin")
"aaaa, Quin"
iex(14)> greet.("Hello","Quin")
"Hi Quin"

:这里面^greeting 为 greeting 值

x = 3.14

IO.puts(x)
y = 10
x = y
IO.puts(x)

case y do
  ^x -> IO.puts("Not so tasty");x = 3.14;IO.puts(x)
  x -> IO.puts("I bet #{x} is tasty")
end

iex(36)> c "demo_1.ex"
3.14
10
Not so tasty
3.14
[]

控制语句

Elixir 中唯一为假的值是 nil 和 布尔值 false。其余都为真。

  • if 和 unless
iex(15)> if :a do
...(15)>  IO.puts("123")
...(15)> else
...(15)>  IO.puts("456")
...(15)> end
123
:ok

iex(16)> unless is_integer("aaa") do
...(16)>  "not an int"
...(16)> end
"not an int"
iex(17)> is_integer("aaa")
false
  • case
case {:ok,"Hello Quin"} do
  {:ok,res} -> IO.puts(res)
  {:error} -> IO.puts("123456")
  _ -> IO.puts("catch all")
end

iex(16)> c "demo_1.ex"
Hello Quin
[]

case {:ok} do
  {:ok,res} -> IO.puts(res)
  {:error} -> IO.puts("123456")
  _ -> IO.puts("catch all")
end

iex(17)> c "demo_1.ex"
catch all
[]

case {"aaa"} do
  {:ok,res} -> IO.puts(res)
  {:error} -> IO.puts("123456")
  {"aaa"} -> IO.puts("456789")
  _ -> IO.puts("catch all")
end

iex(18)> c "demo_1.ex"
456789
[]

case {true} do
  {:ok,res} -> IO.puts(res)
  {:error} -> IO.puts("123456")
  {"aaa"} -> IO.puts("456789")
  {true} -> IO.puts("asd456789")
  _ -> IO.puts("catch all")
end

iex(20)> c "demo_1.ex"
asd456789
[]

:case 中的匹配参数必须相同,否则会匹配失败

  • cond

当我们需要匹配条件而不是值的时候,可以使用 cond/1,这和其他语言的 else if 或者 elsif 相似。

iex> cond do
...>   2 + 2 == 5 ->
...>     "This will not be true"
...>   2 * 2 == 3 ->
...>     "Nor this"
...>   1 + 1 == 2 ->
...>     "But this will"
...> end
"But this will"

iex> cond do
...>   7 + 1 == 0 -> "Incorrect"
...>   true -> "Catch all"
...> end
"Catch all"

函数

  • 匿名函数

要定义匿名函数,我们需要 fnend 关键字,在这两者之间,我们可以定义任意数量的参数和函数体,它们用 -> 分隔开。

iex(32)> sum = fn (a,b) -> a + b end
#Function<13.126501267/2 in :erl_eval.expr/5>
iex(33)> sum.(5,6)
11

iex(36)> sum = &(&1 + &2 + 1)
#Function<13.126501267/2 in :erl_eval.expr/5>
iex(37)> sum.(2,3)
6
  • 命名函数
defmodule Zzz do
  def func(n) do
    "Hi," <> n
  end
end

n = "Quin"
IO.puts(Zzz.func(n))

defmodule Length do
  def of([]), do: 0
  def of([_ | tail]), do: 1 + of(tail)
end

:模块命必须为大写字母开头

  • 函数与匹配模式
defmodule Greeter1 do
  def hello(%{name: person_name}) do
    IO.puts "Hello, " <> person_name
  end
end

fred = %{ name: "Fred",age: "95",favorite_color: "Taupe" }

Greeter1.hello(fred)

iex(4)> c "demo_1.ex"
Hello, Fred
[Greeter1]
defmodule Greeter1 do
  def hello(%{name: person_name}) do
    IO.puts "Hello, " <> person_name
  end
end

fred = %{ name1: "Fred",age: "95",favorite_color: "Taupe" }

Greeter1.hello(fred)

iex(5)> c "demo_1.ex"
warning: redefining module Greeter1 (current version defined in memory)
  demo_1.ex:1


== Compilation error in file demo_1.ex ==
** (FunctionClauseError) no function clause matching in Greeter1.hello/1

    The following arguments were given to Greeter1.hello/1:

        # 1
        %{age: "95", favorite_color: "Taupe", name1: "Fred"}

    demo_1.ex:2: Greeter1.hello/1
    (elixir 1.11.4) lib/kernel/parallel_compiler.ex:315: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/7
** (CompileError)  compile error
    (iex 1.11.4) lib/iex/helpers.ex:200: IEx.Helpers.c/2

:这里的匹配必须是已有的 key 才行

  • 私有函数
defmodule Greeter do
  def hello(name), do: phrase <> name
  defp phrase, do: "Hello, "
end

iex> Greeter.hello("Sean")
"Hello, Sean"

  • 哨兵子句
defmodule Greeter do
  def hello(names) when is_list(names) do
    names
    |> Enum.join(", ")
    |> hello

  end

  def hello(name) when is_binary(name) do
    IO.puts(name)
    phrase() <> name
  end

  defp phrase, do: "Hello, "
end

# IO.puts(Greeter.hello("123"))
IO.puts(Greeter.hello(["123","456","789"]))

  • 默认参数

如果想给参数设置默认值,我们可以用 argument \\ value 语法。

defmodule Greeter do
  def hello(name, language_code \\ "en") do
    phrase(language_code) <> name
  end

  defp phrase("en"), do: "Hello, "
  defp phrase("es"), do: "Hola, "
end

iex> Greeter.hello("Sean", "en")
"Hello, Sean"

iex> Greeter.hello("Sean")
"Hello, Sean"

iex> Greeter.hello("Sean", "es")
"Hola, Sean"

defmodule Greeter do
  def hello(names, language_code \\ "en")

  def hello(names, language_code) when is_list(names) do
    names
    |> Enum.join(", ")
    |> hello(language_code)
  end

  def hello(name, language_code) when is_binary(name) do
    phrase(language_code) <> name
  end

  defp phrase("en"), do: "Hello, "
  defp phrase("es"), do: "Hola, "
end

iex> Greeter.hello ["Sean", "Steve"]
"Hello, Sean, Steve"

iex> Greeter.hello ["Sean", "Steve"], "es"
"Hola, Sean, Steve"

管道

Elixir 给我们提供了管道操作符来解决这个语法上的混乱。管道操作符 |> 获取一个表达式的结果,并把它往后传递。

other_function() |> new_function() |> baz() |> bar() |> foo()

管道获取左边的值,并把它传递给右边。

模块

  • 模块属性

:模块属性通常是常量不能改变

defmodule Example do
  @greeting "Hello"

  def greeting(name) do
    ~s(#{@greeting} #{name}.)
  end
end

若要实现全局变量可用 GenServer 的状态实现

defmodule Registry do
  use GenServer
  # 外部API
  def start_link() do
    GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
  end
  def lookup() do
    GenServer.call(__MODULE__, :lookup)
  end

  @impl true
  def init(:ok) do
    count = %{lookup: 0}
    {:ok, count}
  end

  @impl true
  def handle_call(:lookup, _from, count) do
    l = count.lookup + 1
    count = %{count | lookup: l}
    {:reply, l, count}
  end
end

iex(2)> Registry.start_link
{:ok, #PID<0.112.0>}
iex(3)> Registry.lookup     
1
iex(4)> Registry.lookup
2

组合

  • alias
defmodule Sayings.Greetings do
  def basic(name), do: "Hi, #{name}"
end

defmodule Example do
  alias Sayings.Greetings

  def greeting(name), do: Greetings.basic(name)
end

# Without alias

defmodule Example do
  def greeting(name), do: Sayings.Greetings.basic(name)
end

# 有冲突时

defmodule Example do
  alias Sayings.Greetings, as: Hi

  def print_message(name), do: Hi.basic(name)
end

推导

# 关键字列表
iex> for {_key, val} <- [one: 1, two: 2, three: 3], do: val
[1, 2, 3]

# 映射
iex> for {k, v} <- %{"a" => "A", "b" => "B"}, do: {k, v}
[{"a", "A"}, {"b", "B"}]

# 二进制
iex> for <<c <- "hello">>, do: <<c>>
["h", "e", "l", "l", "o"]

iex> for {:ok, val} <- [ok: "Hello", error: "Unknown", ok: "World"], do: val
["Hello", "World"]
  • 过滤器
defmodule A do
  def func do
    import Integer

    for x <- 1..100,is_even(x),rem(x,3) == 0,do: IO.puts(x)
  end
end

A.func()

iex(1)> c "demo_1.ex"
6
12
18
24
...
90
96
[A]

创建项目

  • 使用 mix new 创建项目
mix new example
  • 交互 iex -S mix

这样 iex 启动的时候会把你的应用和依赖都加载到当前环境(这样你可以直接在 iex 中导入和运行编译好的代码)。

错误处理

  • try/rescue/after
try do
  raise "Oh no!"
rescue
  e in RuntimeError -> IO.puts("An error occurred: " <> e.message)
after
  IO.puts("The end!")
end

iex(11)> c "demo_1.ex"
An error occurred: Oh no!
The end!
[]

并发

  • 进程 spawn

@spec spawn(module(), atom(), list()) :: pid()

实现异步操作,返回 PID

defmodule Example do
  def add(a, b) do
    IO.puts(a + b)
  end
end

spawn(Example, :add, [2, 3])
  • 消息传递 send 和 receive

@spec send(dest :: Process.dest(), message) :: message when message: any()

defmodule Spawn do
  def greet do
    # receive 每次只能接受一条消息,若要处理多条数据需要递归操作
    # receive 类似 case 语句
    receive do
      # receive 监听 与 send 一起使用
      {sender, msg} ->
        send(sender,{:ok,"hello1, #{msg}"})
        send(sender,{:ok,"hello2, #{msg}"})
        send(sender,{:ok,"hello3, #{msg}"})
        # 客服端也只能每次接收一条消息
      {:err} ->
        IO.puts "err!"
    end
  end
end

iex(2)> pid = spawn(Spawn,:greet,[])
#PID<0.112.0>
iex(3)> send pid,{self,"Quin"}
{#PID<0.103.0>, "Quin"}
iex(4)> receive do
...(4)> {:ok,msg}->
...(4)> IO.puts msg
...(4)> end
hello1, Quin
:ok

iex(5)> send pid,{self,"Quin"}
{#PID<0.103.0>, "Quin"}
iex(6)> receive do                   
...(6)> {:ok,msg}->
...(6)> IO.puts msg
...(6)> end
hello2, Quin
:ok

iex(7)> receive do
...(7)> {:ok,msg}->
...(7)> IO.puts msg
...(7)> end
hello3, Quin
:ok
defmodule Spawn do
  def greet do
    # 循环接收多条消息
    receive do
      {:ok, msg} ->
        IO.puts "get success! #{msg}"
      {:err} ->
        IO.puts "err!"
      {sender,msg} ->
        send sender,{:ok,"hello,#{msg}"}
      _ ->
        IO.puts "lalala~"
      # 递归
    end
    
    greet()
  end
end

iex(2)> pid = spawn(Spawn,:greet,[])
#PID<0.112.0>
iex(3)> send pid,{:ok,"aaa"}
get success! aaa
{:ok, "aaa"}
iex(4)> send pid,{:ok}       
lalala~
{:ok}
iex(5)> send pid,{self,"Quin"}
{#PID<0.103.0>, "Quin"}
iex(6)> receive do           
...(6)> {:ok,msg} ->         
...(6)> IO.puts msg          
...(6)> end        
hello,Quin
:ok
iex(7)> receive do   
...(7)> {:ok,msg} ->
...(7)> IO.puts msg  
...(7)> after 500 ->
...(7)> IO.puts "the greeter has gone away"
...(7)> end
the greeter has gone away
:ok

:若500ms 未接收到消息则退出接收状态,可使用 after

  • elixir 递归:尾递归
defmodule A do
  def func(n), do: _fact(n,1)
  defp _fact(0,acc), do: acc
  defp _fact(n,acc), do: _fact(n-1,acc*n)
end
defmodule B do
  def func(0), do: 1
  def func(n), do: n * func(n-1)
end
  • 线程关联

spawn_link 把进程链接起来。两个链接起来的进程能收到相互的退出通知。

defmodule Example do
  def explode, do: exit(:kaboom)
end

iex(3)> spawn Example,:explode,[]
#PID<0.141.0>
iex(4)> spawn_link Example,:explode,[]
** (EXIT from #PID<0.103.0>) shell process exited with reason: :kaboom

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gj1MBp2l-1627371357031)(在这里插入图片描述
)]

使用 spawn_link 时,当一个线程死亡时,另一个线程也会同时被杀死。

  • 监控线程

使用 spawn_method 单向监控线程

节点

  • 命名节点
  • 查询节点名称
iex(2)> Node.self
:nonode@nohost
  • 重命名节点名称
C:\xxx\Quin>iex --sname qiuran_node_one
Interactive Elixir (1.11.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(qiuran_node_one@Qiuran)1> Node.self
:qiuran_node_one@Qiuran
iex(qiuran_node_one@Qiuran)2>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R4yzdQ2O-1627371357032)(在这里插入图片描述
)]

  • 节点添加

使用 Node.connect 连接节点

@spec connect(t()) :: boolean() | :ignored

# node_two
iex(node_two@Qiuran)1> Node.list
[]
iex(node_two@Qiuran)2> Node.connect :"node_one@Qiuran"
true
iex(node_two@Qiuran)3> Node.list
[:node_one@Qiuran]

# node_one
# 此时 node_one 也已经连接到 node_two 节点了
iex(node_one@Qiuran)1> Node.list
[:node_two@Qiuran]


  • 使用 cookie 确保连接安全
iex --sname node_one -- cookie cookie-chip
iex --sname node_two -- cookie cookie-chip

# Node.get_cookie 查询 cookie 信息
iex(node_two@Qiuran)2> Node.get_cookie
:ERTBJEXYOWUNUKBQYGHC
iex(node_one@Qiuran)8> Node.get_cookie
:ERTBJEXYOWUNUKBQYGHC
# 若 cookie 信息不同,则无法连接成功

:cookie 是明文传输的!

  • Node.spawn 和 Noed.spawn_link

节点间的互相通信

Agent

iex(1)> {:ok, agent} = Agent.start_link fn -> [] end
{:ok, #PID<0.137.0>}
iex(2)> agent
#PID<0.137.0>
iex(3)> Agent.update(agent,fn list -> ["eggs" | list]end)
:ok
iex(4)> Agent.get(agent,fn list-> list end)
["eggs"]
iex(5)> Agent.update(agent,fn list -> ["aa" | list]end)
:ok
iex(6)> Agent.get(agent,fn list-> list end)
["aa", "eggs"]
iex(7)> Agent.stop(agent)
:ok

GenServer

defmodule Quin.Stack do
  use GenServer

  # 外部 API
  # 连接
  def start_link(:ok) do
    GenServer.start_link(__MODULE__,:ok,name: __MODULE__)
  end

  def pop do
    GenServer.call(__MODULE__,:pop)
  end

  def size do
    GenServer.call(__MODULE__,:size)
  end
  def push(head) do
    GenServer.call(__MODULE__,{:push,head})
  end
  def peek do
    GenServer.call(__MODULE__,:peek)
  end

  # 初始化
  @impl true
  def init(:ok) do
    {:ok, []}
  end

  # pop
  # 获取当前栈第一个元素,并移除
  @impl true
  def handle_call(:pop, _from, state) do
    # IO.puts
    if is_list(state) and state === [] do
      {:reply, {:ok,nil}, state}
    else
      [head | state] = state
      {:reply, {:ok,head}, state}
    end
  end

  # :size
  # 获取当前栈大小
  @impl true
  def handle_call(:size, _from, state) do
    {:reply, {:ok, length(state)}, state}
  end

  # :push
  # 添加元素到栈中
  @impl true
  def handle_call({:push, head}, _from, state) do
    state = [head | state]
    {:reply, {:ok, true}, state}
  end

  # :peek
  # 读取栈顶元素,但不移除
  def handle_call(:peek, _from, state) do
    head = hd(state)
    {:reply, {:ok,head}, state}
  end
end

mysql数据库 操作

添加 MyXQL 依赖

  1. 在 mix,exs 中添加 MyXQL 依赖
defp deps do
    [
      {:myxql, "~> 0.5.0"}
      # {:dep_from_hexpm, "~> 0.3.0"},
      # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
    ]
  end
  1. 使用 mix deps 查看依赖是否已添加

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bz0tEeeX-1627371357034)(在这里插入图片描述
)]

  1. 使用 mix deps.get 下载依赖

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2vakB6fr-1627371357035)(在这里插入图片描述
)]

连接数据库

iex(2)> {:ok, pid} = MyXQL.start_link(hostname: "localhost",database: "test",username: "root",password: "root") 
{:ok, #PID<0.214.0>}

:myxql 中有很多参数需要是可进去查阅

数据库查询

  • 查询
iex(3)> MyXQL.query(pid, "SELECT * FROM demo")                                                                 
{:ok,
 %MyXQL.Result{
   columns: ["time", "content"],
   connection_id: 4,
   last_insert_id: nil,
   num_rows: 3,
   num_warnings: 0,
   rows: [
     [~N[2021-07-22 15:51:08],
      "{ \"people\": [{ \"firstName\": \"Brett\", \"lastName\":\"McLaughlin\", \"email\": \"aaaa\" },{ \"firstName\": \"Jason\", \"lastName\":\"Hunter\", \"email\": \"bbbb\"},{ \"firstName\": \"Elliotte\", \"lastName\":\"Harold\", \"email\": \"cccc\" }]}"],
     [~N[2021-07-09 15:52:11],
      "{ \"people\": [{ \"firstName\": \"Brett\", \"lastName\":\"McLaughlin\", \"email\": \"aaaa\" },{ \"firstName\": \"Jason\", \"lastName\":\"Hunter\", \"email\": \"bbbb\"},{ \"firstName\": \"Elliotte\", \"lastName\":\"Harold\", \"email\": \"cccc\" }]}"],
     [~N[2021-07-01 15:52:23],
      "{ \"people\": [{ \"firstName\": \"Brett\", \"lastName\":\"McLaughlin\", \"email\": \"aaaa\" },{ \"firstName\": \"Jason\", \"lastName\":\"Hunter\", \"email\": \"bbbb\"},{ \"firstName\": \"Elliotte\", \"lastName\":\"Harold\", \"email\": \"cccc\" }]}"]
   ]
 }}
  • 插入
iex(10)> MyXQL.query(pid, "INSERT INTO demo (time,content) VALUES (?,?)",[~N[2021-07-22 15:51:08],"{ \"people\": [{ \"firstName\": \"Brett\", \"lastName\":\"McLaughlin\", \"email\": \"aaaa\" },{ \"firstName\": \"Jason\", \"lastName\":\"Hunter\", \"email\": \"bbbb\"},{ \"firstName\": \"Elliotte\", \"lastName\":\"Harold\", \"email\": \"cccc\" }]}"])
{:ok,
 %MyXQL.Result{
   columns: nil,
   connection_id: 4,
   last_insert_id: 0,
   num_rows: 1,
   num_warnings: 0,
   rows: nil
 }}

:~N[2021-07-22 15:51:08]

~N:魔符 时间格式

综合项目:

本地 mysql 建一张表, 其中一个字段使用 datetime 类型其中一个字段使用 text 类型, 保存 json 格式的数据;
搭建一个 supervisor 项目, 利用这两个第三方库: myxql, jason 完成如下功能:
1.往数据库表中插入一行数据;
2.读取数据库数据并解析( json 字段) 返回.

  1. 创建 supervisor 项目 mix new --sup SqlDemo

  2. 配置 SqlDemo.Application

def start(_type, _args) do
    children = [
      %{
        id: SqlDemo,
        start: {SqlDemo, :start_link, [:ok]}
      }
      # Starts a worker by calling: SqlDemo.Worker.start_link(arg)
      # {SqlDemo.Worker, arg}
    ]

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: SqlDemo.Supervisor]
    Supervisor.start_link(children, opts)
  end
  1. 实现数据库操作
defmodule SqlDemo do
  use GenServer

  # 外部API
  def start_link(:ok) do
    GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
  end

  def select_json do
    GenServer.call(__MODULE__, :select_json)
  end

  def select_all do
    GenServer.call(__MODULE__, :select_all)
  end

  def insert(x) do
    GenServer.call(__MODULE__, {:insert, x})
  end

  def count do
    GenServer.call(__MODULE__, :count)
  end

  # 内部API
  @impl true
  def init(:ok) do
    MyXQL.start_link(
      hostname: "localhost",
      database: "test",
      username: "root",
      password: "root",
      name: :sql
    )
    count = %{all: 0, json: 0, insert: 0}
    {:ok, count}
  end

  @doc """
  获取所有访问次数
  """
  @impl true
  def handle_call(:count, _from, count) do
    {:reply, count, count}
  end

  @doc """
  查询所有
  """
  @impl true
  def handle_call(:select_all, _from, count) do
    all_count = count.all + 1
    count = %{count | all: all_count}
    {:ok, res} = MyXQL.query(:sql, "SELECT * FROM demo")
    {:ok, rows} = Map.fetch(res, :rows)
    {:reply, rows, count}
  end

  @doc """
  查询 json 格式数据
  """
  @impl true
  def handle_call(:select_json, _from, count) do
    json_count = count.json + 1
    count = %{count | json: json_count}
    {:ok, res} = MyXQL.query(:sql, "SELECT * FROM demo")
    {:ok, rows} = Map.fetch(res, :rows)
    r = Enum.map(rows, &format/1)
    {:reply, r, count}
  end

  @doc """
  格式 json
  """
  defp format(row) do
    {:ok, res} = Jason.decode(tl(row))
    res
  end

  @doc """
  插入时间和json数据
  """
  @impl true
  def handle_call({:insert, x}, _from, count) do
    insert_count = count.insert + 1
    count = %{count | insert: insert_count}
    str = x
    {:ok, str} = Jason.encode(str)
    day = DateTime.utc_now()
    {:ok, res} = MyXQL.query(:sql, "INSERT INTO demo (time,content) VALUES (?,?)", [day, str])
    {:reply, res, count}
  end
end

Mix:命令

  • mix deps: 查看依赖

  • mix deps.get 下载依赖

  • mix hex.config mirror_url https://hexpm.upyun.com 更新镜像下载地址

  • mix deps.compile xxx 编译(xxx)依赖

  • mix deps.clean xxx 清空(xxx)依赖
    l(:count, _from, count) do
    {:reply, count, count}
    end

    @doc “”"
    查询所有
    “”"
    @impl true
    def handle_call(:select_all, _from, count) do
    all_count = count.all + 1
    count = %{count | all: all_count}
    {:ok, res} = MyXQL.query(:sql, “SELECT * FROM demo”)
    {:ok, rows} = Map.fetch(res, :rows)
    {:reply, rows, count}
    end

    @doc “”"
    查询 json 格式数据
    “”"
    @impl true
    def handle_call(:select_json, _from, count) do
    json_count = count.json + 1
    count = %{count | json: json_count}
    {:ok, res} = MyXQL.query(:sql, “SELECT * FROM demo”)
    {:ok, rows} = Map.fetch(res, :rows)
    r = Enum.map(rows, &format/1)
    {:reply, r, count}
    end

    @doc “”"
    格式 json
    “”"
    defp format(row) do
    {:ok, res} = Jason.decode(tl(row))
    res
    end

    @doc “”"
    插入时间和json数据
    “”"
    @impl true
    def handle_call({:insert, x}, _from, count) do
    insert_count = count.insert + 1
    count = %{count | insert: insert_count}
    str = x
    {:ok, str} = Jason.encode(str)
    day = DateTime.utc_now()
    {:ok, res} = MyXQL.query(:sql, “INSERT INTO demo (time,content) VALUES (?,?)”, [day, str])
    {:reply, res, count}
    end
    end


## Mix:命令

- mix deps:  查看依赖
- mix deps.get  下载依赖
- mix hex.config mirror_url https://hexpm.upyun.com   更新镜像下载地址
- mix deps.compile xxx  编译(xxx)依赖
- mix deps.clean xxx  清空(xxx)依赖
- mix deps.update xxx  更新(xxx)依赖
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值