高階函式 (higher-order function) 是指用函式為參數或回傳值的函式,像前面的閉包就是一種高階函式。在本文中,我們介紹一些常見的高階函式的模式。我們不依賴現有的套件,而會重新實作,讓各位讀者參考。
all
all
接受一個序列和一個程序,當序列中所有元素皆符合該程序的條件時,回傳 true
,反之,則回傳 false
。範例如下:
proc all[T](arr: seq[T], f: proc (n: T): bool): bool =
for e in arr:
if not f(e):
return false
true
let a1 = @[1, 2, 3, 4, 5, 6]
assert(a1.all(proc (n: int): bool = n > 0) == true)
let a2 = @[-2, -1, 0, 1, 2, 3]
assert(a2.all(proc (n: int): bool = n > 0) == false)
any
any
接受一個序列和一個程序,當序列中其中一個元素符合該程序的條件時,回傳 true
,反之,則回傳 false
。範例如下:
proc any[T](arr: seq[T], f: proc (n: T): bool): bool =
for e in arr:
if f(e):
return true
false
let a = @[-2, -1, 0, 1, 2, 3]
assert(a.any(proc (n: int): bool = n > 0) == true)
let b = @[1, 3, 5, 7, 9]
assert(b.any(proc (n: int): bool = n mod 2 == 0) == false)
filter
any
接受一個序列和一個程序,從原序列中過濾條件,將符合該程序的元素回傳至新序列。範例如下:
proc filter[T](arr: seq[T], f: proc (n: T): bool): seq[T] =
result = @[]
for e in arr:
if f(e):
result.add(e)
let arr = @[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let even = arr.filter(proc (n: int): bool = n mod 2 == 0)
assert(even == @[2, 4, 6, 8, 10])
map
map
接受一個序列和一個程序,根據該程序將元素轉換後回傳至新序列。範例如下:
proc map[T](arr: seq[T], f: proc (n: T): T): seq[T] =
result = @[]
for e in arr:
result.add(f(e))
let arr = @[1, 2, 3, 4, 5]
let sqr = arr.map(proc (n: int): int = n * n)
assert(sqr == @[1, 4, 9, 16, 25])
reduce
reduce
接受一個序列和一個程序,依照該程序將序列縮減為單一值。範例如下:
proc reduce[T](arr: seq[T], f: proc (a: T, b: T): T): T =
assert(arr.len > 0)
if arr.len == 1:
return arr[0]
result = arr[0]
for i in countup(1, arr.len - 1):
result = f(result, arr[i])
let arr = @[1, 2, 3, 4, 5]
let sum = arr.reduce(proc (a: int, b: int): int = a + b)
assert(sum == 15)
zip
zip
接收兩個等長的序列,將兩序列中同位置的元素合成一個元組,回傳一個以元組為元素的序列。範例如下:
proc zip[T, S](a: seq[T], b: seq[S]): seq[tuple[first: T, second: S]] =
assert(a.len == b.len)
result = @[]
for i in countup(0, a.len - 1):
result.add((a[i], b[i]))
let a = @[1, 2, 3]
let b = @["a", "b", "c"]
let zipped = zip(a, b)
assert(zipped == @[(first: 1, second: "a"), (first: 2, second: "b"), (first: 3, second: "c")])
partition
partition
接收一個序列和一個程序,根據該程序將原序列拆開,回傳兩個新序列。範例如下:
註:兩序列長度可能不相等。
proc partition[T](arr: seq[T], f: proc(n: T): bool): tuple[matched: seq[T], unmatched: seq[T]] =
result = (matched: @[], unmatched: @[])
for e in arr:
if f(e):
result.matched.add(e)
else:
result.unmatched.add(e)
let arr = @[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let (evens, odds) = arr.partition(proc (n: int): bool = n mod 2 == 0)
assert(evens == @[2, 4, 6, 8, 10])
assert(odds == @[1, 3, 5, 7, 9])
組合數個高階函式
高階函式間可相互組合,達到加乘的效果,範例如下:
# Declare filter as above.
# Declare map as above.
# Declare reduce as above.
let arr = @[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let o = arr
.filter(proc (n: int): bool = n mod 2 != 0)
.map(proc (n: int): int = n * n)
.reduce(proc (a: int, b: int): int = a + b)
assert(o == 1 * 1 + 3 * 3 + 5 * 5 + 7 * 7 + 9 * 9)