因為在 Elixir 裡,所有的值都是不可變的 (immutable)
所以並沒有這種在其他語言常見的迴圈
# 在 elixir 裡沒有這種:
for(i = 0; i < sizeof(array); i++) {
array[i] = array[i] * 2;
}
所有像是迴圈的,例如上一篇的 for
, Enum.map
等,底下也是都是遞迴
要把一個 list 的所有項目都乘二
方便的 map 作法
list = [1,3,5,7,9]
Enum.map(list, fn x -> x * 2 end)
最有彈性的函式遞迴作法
def x2(list), do: x2(list, [])
def x2([], accu), do: Enum.reverse(accu)
def x2([x | tail], accu), do: x2(tail, [x * 2 | accu])
這種剛開始寫的時候會有點不自然
我們一步步的看每次呼叫的情況
剛開始我們呼叫 x2 函數並給參數 [1,2,3,5,7,9]
x2([1,3,5,7,9])
因為只有一個參數,所以會批配到第一個 def x2(list), do: x2(list, [])
這個函式會幫我們加上 accumulator (累積器)到第二個變數後再次呼叫
(在這個例子,accumulator 就是一個空 list,準備接收我們乘過的結果)
x2([1,3,5,7,9], [])
這次因為第一個參數不是空 list,
所以批配到第三個函式 def x2([x | tail], accu), do: x2(tail, [x * 2 | accu])
在這邊第一個變數的值 [1,3,5,7,9]
被 pattern matching 成 [1 | [3, 5, 7, 9]]
並準備呼叫 x2(tail, [x * 2 | accu])
帶入變數變成 x2([3, 5, 7, 9], [1 * 2 | []])
這時候的狀態是 x2([3, 5, 7, 9], [2])
可以看到,原本在前面的 1
已經乘過了,加到 accumulator 裡面
再次呼叫會變成 x2([5, 7, 9], [6, 2])
在一次會變成 x2([7, 9], [10, 6, 2])
直到第一個參數變成空 list x2([], [18, 14, 10, 6, 2])
這個時候會 pattern matching 的是第二個函式 def x2([], accu), do: Enum.reverse(accu)
這時候我們得到的 accu 其實是相反的,因為在 Elixir 裡面從 head 放新項目比較快
所以最後要幫 accu 轉正 Enum.reverse([18, 14, 10, 6, 2])
便得到結果 [2, 6, 10, 14, 18]