Nim 程式設計教學:高階函式 (Higher Order Function)

PUBLISHED ON APR 11, 2018 — PROGRAMMING

    高階函式 (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)
    
    comments powered by Disqus