Elixir Part I - Playbook

November 30, 2017 by Byron ZHU


https://joearms.github.io/published/2013-05-31-a-week-with-elixir.html

Pattern Matching

equal sign = is not an assignment, is a match operator

^ pin operator forces using existing values

Immutable

iex(13)> l1 = [1,2,3]
[1, 2, 3]
iex(14)> l2 = [ l1 | 'a']
[[1, 2, 3], 97]
iex(16)> l2 = [ 'a'| l1]
['a', 1, 2, 3]

Basics

Value Types

iex(23)> 0o3407
1799
iex(24)> 0b111100
60
iex(25)> 0x33ff
13311
iex(28)> :var@2
:var@2
iex(29)> 1..100
1..100
iex> Regex.run ~r{[aeiou]}, "caterpillar"
["a"]
iex> Regex.scan ~r{[aeiou]}, "caterpillar"
[["a"], ["e"], ["i"], ["a"]]

System Types

iex(31)> self
#PID<0.84.0>
iex(32)> make_ref
#Reference<0.2792161163.4208984065.108413>

Collection Types

#tuple
iex(34)> {3,3,44,'adada',"adada", :adad, 33.33, 1..20}
{3, 3, 44, 'adada', "adada", :adad, 33.33, 1..20}
# typical funcition call
iex(38)> {status, file} = File.open('load.jmx')
{:ok, #PID<0.142.0>}
# list (lisp-like)

iex(40)> [1,2]++[3,4]
[1, 2, 3, 4]
iex(41)> [1,3,4]--[1,]
[3, 4]
iex(43)> 2 in [3,4]
false

# keyword list
iex(46)> [1,name: "Dave", city: "Dallas", likes: "Programming"]
[1, {:name, "Dave"}, {:city, "Dallas"}, {:likes, "Programming"}]
iex(47)> {1, fred: 1, dave: 2}
{1, [fred: 1, dave: 2]}

Maps

iex(48)> states = %{ "AL" => "Alabama", "WI" => "Wisconsin" }
%{"AL" => "Alabama", "WI" => "Wisconsin"}

iex(50)> colors = %{ red: 0xff0000, green: 0x00ff00, blue: 0x0000ff }
%{blue: 255, green: 65280, red: 16711680}
iex(51)> colors['red']
nil
iex(52)> colors["red"]
nil
iex(53)> colors[:red]
16711680
iex(54)> colors.red
16711680
iex(55)> colors.black
** (KeyError) key :black not found in: %{blue: 255, green: 65280, red: 16711680}

Binaries

iex(64)> bin = <<3 :: size(2), 5 :: size(4), 1 :: size(2)>>
<<213>>
iex(58)> :io.format("~-8.2b~n", :binary.bin_to_list bin)
11010101
:ok
iex(63)> 0b11010101
213

Dates & Times

iex(65)> d1 = Date.new(2016, 12, 25)
{:ok, ~D[2016-12-25]}
iex(76)> t1 = Time.new(12, 34, 56)
{:ok, ~T[12:34:56]}

Operators

iex(100)> div(3,1)
3
iex(101)> 3/1
3.0

iex(108)> [1,3,4] -- [2,1]
[3, 4]
iex(109)> [1,3,4] ++ [2,1]
[1, 3, 4, 2, 1]

Anonymous Functions

iex(1)> sum = fn (a,b) -> a+b end
#Function<12.99386804/2 in :erl_eval.expr/5>
iex(2)> sum.(1,2)
3

iex(3)> o99 = fn -> 99 end
#Function<20.99386804/0 in :erl_eval.expr/5>
iex(4)> o99.()
99

iex(6)> greet = fn msg -> IO.puts msg end
#Function<6.99386804/1 in :erl_eval.expr/5>
iex(10)> greet.([80, 77])
PM
:ok

function is first class

& notation

iex(32)> add_one = &(&1+1)
#Function<6.99386804/1 in :erl_eval.expr/5>
iex(33)> add_one.(2)
3

iex(36)> speak = &(IO.puts &1)
&IO.puts/1
# elixir figures out it's a direct wrapper of built in call, so it associate the anonymous func directly

iex(40)> Enum.map [1,2,3,4], &(&1 + 1)
[2, 3, 4, 5]

iex(44)> Enum.each [1,3,4,5], & IO.inspect &1
1
3
4
5
:ok
iex(45)> Enum.each [1,3,4,5], &IO.inspect/1
1
3
4
5
:ok

Modules and Functions

do…end is syntactic sugar for do: ()

# it's common pattern in elixir

defmodule Maths do
  def sum(0), do: 0
  def sum(n), do: n + sum(n-1)

  def gcd(x, 0), do: x
  def gcd(x, y), do: gcd(y, rem(x, y))

  def fact(0), do: 1
  def fact(n) when n > 0, do: n * fact(n-1)
end


default parameter param \\ val

defp is private function

Pipe operator |>

iex(10)> (1..25) |> Enum.map(&(&1*&1)) |> Enum.filter(&(rem(&1, 2) == 0))
[4, 16, 36, 64, 100, 144, 196, 256, 324, 400, 484, 576]

Module Directives

  • import Module [, only: | except: ]
  • alias My.Import.Module.{Parser, Runner}
  • require for marcros

Module Attributes

@author "dave"
def get_author do
  @author
end

Operators

  • list ++ and --
  • string <>
  • boolean and or not

List and Recursion

# head and tail with recursion
iex(26)> [1 | [2 | [3 | []]]]
[1, 2, 3]

# pattern match
iex(29)> [head | [2,3]] = [1,2,3]
[1, 2, 3]
iex(30)> [head | [3]] = [1,2,3]
** (MatchError) no match of right hand side value: [1, 2, 3]

# count lenght
defmodule Listxx do
  def len([]), do: 0
  def len([_ | tail]), do: 1 + len(tail)
end

Map, Keyword lists, Sets and Structs

Keyword

  • keys must be atoms
  • keys are ordered
  • keys can be redundant
iex(63)> if 0, do: :this, else: :that
:this
iex(64)> if false, do: :this, else: :that
:that
# this can be considered the `if` function using keyword list as parameters

Map


iex(88)> map = %{:a => 1, 2 => :b}
%{2 => :b, :a => 1}
# map update using pipe
iex(89)> %{map | 2 => "two"}
%{2 => "two", :a => 1}

Struct

defmodule Ski do
  defstruct name: "", distance: 0, eclaire: false, vertical: 0
end

iex(97)> bromont = %Ski{name: "Ski Bromont", distance: 100}
%Ski{distance: 100, eclaire: false, name: "Ski Bromont", vertical: 0}
iex(98)> bromont.name
"Ski Bromont"
iex(99)> bromont.distance
100
iex(100)> orford = %{bromont | name: "Orford",  distance: 110, eclaire: true}
%Ski{distance: 110, eclaire: true, name: "Orford", vertical: 0}

© 2018 | 朱曉清 | powered by Hugo