Scalaプログラミングスタイル集をSwiftで

  • 12
    Like
  • 0
    Comment
More than 1 year has passed since last update.

この記事はScalaプログラミングスタイル集をSwift2.0化するものです。詳しくはそちらをご覧ください。


手続きプログラミング

func fac(n: Int) -> Int {
    var result = 1
    for i in 1...n {
        result *= i
    }
    return result
}

普通の再帰

func fac(n: Int) -> Int {
    if n == 0 {
        return 1
    } else {
        return n * fac(n - 1)
    }
}

パターンマッチ

func fac(n: Int) -> Int {
    switch n {
    case 0:
        return 1
    default:    //case _:でも通る
        return n * fac(n - 1)
    }
}

Swiftのパターンマッチイコールただの(割と自由に書ける)switch文という認識が抜けない。これじゃあ普通の再帰と一緒なのだが。

末尾再帰

func facAcc(acc: Int, n: Int) -> Int {
    if n == 0 {
        return acc
    } else {
        return facAcc(n * acc, n: n - 1)
    }
}
func fac(n: Int) -> Int {
    return facAcc(1, n: n)
}

facAccはローカル関数化できる、が、Swift1.2でやるとLocal functions cannot reference themselvesとか言われる。

func fac(n: Int) -> Int {
    func facAcc(acc: Int, n: Int) -> Int {
        if n == 0 {
            return acc
        } else {
            return facAcc(n * acc, n: n - 1)
        }
    }
    return facAcc(1, n: n)
}

継続渡し

そもそもの合成関数をざっくり用意するところから始めないといけない。

infix operator  { }

func <T,U,V>(lhs: U -> V, rhs: T -> U) -> T -> V {
    return {x in lhs(rhs(x))}
}

↑を踏まえると

func facCps(k: Int -> Int, n: Int) -> Int {
    if n == 0 {
        return k(1)
    } else {
        return facCps(k  {n * $0}, n: n - 1)
    }
}
func fac(n: Int) -> Int {
    return facCps({$0}, n: n)
}

Yコンビネータ

func Y<T, U>(x: T, f: (T, (T -> U)) -> U) -> U {
    return f(x){y in Y(y, f: f)}
}

func fac(n: Int) -> Int {
    return Y(n){ n, f in
        if n == 0 {
            return 1
        } else {
            return n * f(n - 1)
        }
    }
}

trailing closureを意識して本家とは仮引数の位置を代わってもらってみた。

foldLeft

ScalaのfoldLeftはSwiftのreduceだしScalaのreduceは……あれ?

func fac(n: Int) -> Int {
    return (1...n).reduce(1, combine: *)
    //return (1...n).reduce(1){$0 * $1}
}

自力でfoldLeft(Swiftでいうところのreduce)を実装すると次のような感じ。reduceと同様SequenceTypeに対して実装してみる。

extension SequenceType {
    func foldLeft<U>(initial: U, combine: (U, Self.Generator.Element) -> U) -> U {
        var result = initial
        for element in self {
            result = combine(result, element)
        }
        return result
    }
}

func fac(n: Int) -> Int {
    return (1...n).foldLeft(1, combine: *)
}

foldRight

ダメなfoldRightの場合

extension SequenceType {
    func foldRight<U>(initial: U, combine: (Self.Generator.Element, U) -> U) -> U {
        var result = initial
        let array = self.reverse()
        for element in array {
            result = combine(element, result)
        }
        return result
    }
}
func fac(n: Int) -> Int {
    return (1...n).foldRight(1, combine: *)
}

無限Stream対応版は種々のエラーに阻まれてなかなか大変なので省略。

ポイントフリー

func fac(x: Int) -> Int {
    return ({(array: [Int]) in array.reduce(1, combine: *)}  {(x: Int) in Array(1...x)})(x)
}

unfold

func unfold<T, U>(p: T -> Bool, h: T -> U, t: T -> T, a: T) -> Array<U> {
    if p(a) {
        return [U]()
    } else {
        return [h(a)] + unfold(p, h: h, t: t, a: t(a))
    }
}

func fac(n: Int) -> Int {
    return ({(array: [Int])in array.foldRight(1, combine: *)}  {(n: Int) in unfold({$0 == 0}, h: {x in x}, t: {$0 - 1}, a: n)})(n)
}

Hylomorphism

func hylo<T,U,V>(v: V, f: (U, V) -> V, g: T -> (U, T), p: T -> Bool, t: T) -> V {
    if p(t) {
        return v
    } else {
        let (u, t_) = g(t)
        return f(u, hylo(v, f: f, g: g, p: p, t: t_))
    }
}

func fac(n: Int) -> Int {
    return hylo(1, f: *, g: {n in (n, n - 1)}, p: {$0 == 0}, t: n)
}

Paramorphism

func paraInt<T>(a: T, n: Int, f: (Int, T) -> T) -> T {
    if (n == 0) {
        return a
    } else {
        return f(n, paraInt(a, n: n - 1, f: f))
    }
}

func fac(n: Int) -> Int {
    return paraInt(1, n: n){n, m in n * m}
}

余談としてParamorphismの代表例、tailsが載っている。

func paraList<T, U>(b: U, array: [T], f: (T, [T], U) -> U) -> U {
    if array.isEmpty {
        return b
    } else {
        return f(array.first!, array, paraList(b, array: Array(array[1..<array.count]), f: f))
    }
}

func tails<T>(array: [T]) -> [[T]] {
    return paraList([[T]](), array: array){x, xs, tls in
        return [xs] + tls
    }
}

ただしこの実装だと出力がたとえば

[[1, 2], [2]]

となる。ScalaだとList(List(1, 2), List(2), List())。リスト構造じゃないのでしょうがない…?あと、部分配列の取り出しが気持ち悪い。Swiftzとか使うと綺麗になるのかもしれない。

始代数、終余代数

量が多すぎ。Scalaに譲る


ここまで理解できていれば、Swiftを実務で使うのに十分な実力があると思います。(?)