遞迴函式是一種會呼叫自己的函式。
一個_遞迴函式_至少需要一個_基本情況_,以及至少一個_遞迴情況_。
基本情況會回傳一個值,而不會再次呼叫函式。 遞迴情況會再次呼叫函式,並修改輸入,讓它在某個時間點符合基本情況。
以下是一個計算陣列元素數量的範例。
def count:
if length == 0 then
0 # base case
else
1 + (.[1:] | count) # recursive case
end;
([] | count), # => 0
([11, 22, 33] | count) # => 3
一個_遞迴函式_可以有很多個_基本情況_,也可以有很多個_遞迴情況_。 例如費波那契數列就是一個有兩個_基本情況_的遞迴數列。
def fibonacci:
if . == 0 then
0
elif . == 1 then
1
else
(. - 1 | fibonacci) + (. - 2 | fibonacci)
end;
10 | fibonacci # => 55
計算某個給定的值x在陣列中出現的次數,會有兩個_遞迴情況_。
def count_occurrences(x):
if length == 0 then
0
elif first == x then
1 + (.[1:] | count_occurrences(x))
else
(.[1:] | count_occurrences(x))
end;
[11, 22, 33, 22, 44] | count_occurrences(22) # => 2
實務上,走訪陣列和其他可列舉的資料結構時,最常使用內建函式,例如map和reduce,或是像[.[] | select(...)]這樣使用資料流。
在底層,有些內建函式則是用遞迴實作的。
你剛加入一個維護 jq 資料管線的團隊。
在第一次結對工作時,你的新同事好奇地問:
「你知道 add、reverse 和 map 在底層是怎麼運作的嗎?一起來探索吧,試著用遞迴自己實作它們,不要用內建功能。」
我們要重新實作內建的 add 篩選器,藉此練習遞迴。
基本情況 是空陣列的總和為 0。
請用遞迴函式自己實作,不要使用內建的 add。
[5, 4, 6, 10] | array_add # => 25
我們要重新實作內建的 reverse 篩選器。
基本情況 是空陣列反轉後仍是空陣列。
請用遞迴函式自己實作,不要使用內建的 reverse。
[5, 4, 6, 10] | array_reverse # => [10, 6, 4, 5]
我們要重新實作內建的 map 篩選器。
這個函式接受一個篩選器作為參數,對輸入陣列的每個元素執行該篩選器,並將輸出結果放在新的陣列中回傳。
基本情況 是空陣列映射後仍是空陣列。
請用遞迴函式自己實作,不要使用內建的 map。
[5, 4, 6, 10] | array_map(. + 10) # => [15, 14, 16, 20]