iT邦幫忙

DAY 28
1

蠻可愛的 Erlang 與 Elixir系列 第 28

Elixir 的 Loops 與 iteration

Elixir的loops,嗯.....
遞迴!是的,我們的老朋友遞迴又出現了.

遞迴與尾遞迴

來看一個簡單的範例,產生自然數的.

defmodule NaturalNums do
  def print(1), do: IO.puts(1)

  def print(n) do
    print(n - 1)
    IO.puts(n)
  end
end

編譯及執行:

iex(1)> c "natural_nums.ex"
[NaturalNums]
iex(2)> NaturalNums.print(3)
1
2
3
:ok

再來看一個加總list的範例.

defmodule ListHelper do
  def sum([]), do: 0

  def sum([head | tail]) do
    head + sum(tail)
  end
end


尾遞迴的版本:

defmodule ListHelper do
  def sum(list) do
    do_sum(0, list)
  end

  defp do_sum(current_sum, []) do
    current_sum
  end

  defp do_sum(current_sum, [head | tail]) do
    new_sum = head + current_sum
    do_sum(new_sum, tail)
  end
end

相信大家對遞迴與尾遞迴應該很熟悉了.

Higher-order functions

這在前面Erlang的部份也有介紹過.直接來看範例.
凡是可以計數的,序列化的,(enumerable);都可以使用
Enum模組裡的函數,搭配Higer-order functions
的方法再加工.

iex(1)> Enum.each(
...(1)>   [1,2,3],
...(1)>   fn(x) -> IO.puts(x) end
...(1)> )
1
2
3
:ok

iex(2)> Enum.map(
...(2)>   [1,2,3],
...(2)>   &(&1 * 2)
...(2)> )
[2, 4, 6]

iex(3)> Enum.filter(
...(3)>   [1,2,3],
...(3)>   fn(x) -> rem(x, 2) == 1 end
...(3)> )
[1, 3]

可以改寫成

iex(4)> Enum.filter(
...(4)>   [1,2,3],
...(4)>   &(rem(&1,2) == 1)
...(4)> )
[1, 3]

Enum的 Enum.each, Enum.map, Enum.filter 三個函數,
相信大家都很熟悉了.
來介紹一下Enum.reduce/3
在介紹Elixir的Enum.reduce之前,我們先來看另一種程式
語言的片段:

var sum = 0;

[1,2,3].forEach(function(element) {
  sum += element;
})

這程式語言有受到Lisp影響.有看出來是哪個程式語言了嗎?

現在回到Elixir,介紹Enum.reduce的格式:

Enum.reduce(
    enumerable,
    initial_acc,
    fn(element, acc) ->
      ...
    end
)

對 list [1,2,3]進行加總,可以這樣寫

iex(5)> Enum.reduce(
...(5)>   [1,2,3],
...(5)>   0,
...(5)>   fn(element, sum) -> sum + element end
...(5)> )
6

上面的fn 可以改寫成lambda方式.

iex(6)> Enum.reduce([1,2,3], 0, &+/2)
6

結合上面介紹的Enum.reduce及lambda方式,
可以將加總寫成以下形式.

defmodule NumHelper do
  def sum_nums(enumberable) do
    Enum.reduce(enumberable, 0, &add_num/2)
  end

  defp add_num(num, sum) when is_number(num), do: sum + num
  defp add_num(_, sum), do: sum
  
end

實際應用:

iex(8)> NumHelper.sum_nums([1,"Hi", :xyz, 2, 3, "World"])
6

List Comprehension

這在前面Erlang的部份,也有介紹過.直接看範例.

iex(9)> for x <- [1,2,3] do
...(9)>   x * x
...(9)> end
[1, 4, 9]

你沒看錯,有for
Elixir有for,用在 List Comprehension,看上面的範例,就與一般程式語言
的方式差別不大了.
Erlang的List Comprehension格式偏向數學式,Elixir偏向一般方式.
例如在數學中,我們要表達正整數可以用以下方式

在Erlang 中

[ x || x <- ListofIntegers, x > 0]


來看應用範例,假設阿拉蕾,多拉A夢跟哥吉拉要一起聚餐,
但是飯店房門無法讓超過2m的通過,可以使用以下程式,
看誰到室內用餐.

customers = [
  %{ name: "arale",    height: 1.39},
  %{ name: "doraemon", height: 1.293},
  %{ name: "godzilla", height: 150}
]

for data = %{ height: howtall} <- customers
  howtall < 2
  do: IO.inspect data

結果是:

%{height: 1.39, name: "arale"}
%{height: 1.293, name: "doraemon"}

哥吉拉只能在室外用餐了.


上一篇
Elixir 的 Control flow
下一篇
Elixir 的 Concurrency
系列文
蠻可愛的 Erlang 與 Elixir30

尚未有邦友留言

立即登入留言