Swift
FunctionalProgramming
関数型プログラミング
クロージャ
高階関数

1記事目。

ふだんiOSアプリ開発をしています。

最近すごいH本を読み、Swiftでも関数で遊んでみました。

(楽しい以外の何でもないので、有益な実装を求めていた方は、ブラウザバック推奨です)

興味がある方はplaygroundなどで試してみてください。


本題

hoge1 ~ 4は、返却値がクロージャのため、さらに引数を渡すことができる

/// Intをとり、Intを返す

func hoge1(_ value: Int) -> Int {
return value
}

hoge1(1) // return 1

/// Intをとり、IntをとりIntを返すクロージャを返す

func hoge2(_ value: Int) -> ((Int) -> Int) {
return { a in
return value + a
}
}

hoge2(1)(2) // return 3

/// Intをとり、IntをとりIntをとりIntを返すクロージャを返す

func hoge3(_ value: Int) -> ((Int) -> ((Int) -> Int)) {
return { a in
return { b in
return value + a + b
}
}
}

hoge3(1)(2)(4) // return 7

/// Intをとり、IntをとりIntをとりIntをとりIntを返すクロージャを返す

func hoge4(_ value: Int) -> ((Int) -> ((Int) -> ((Int) -> Int))) {
return { a in
return { b in
return { c in
return value + a + b + c
}
}
}
}

hoge4(1)(2)(3)(8) // return 14


hoge5 ~ 7 は、引数がクロージャのため、事前に処理を渡しておく必要がある

/// IntをとりIntを返すクロージャをとり、IntをとりIntをとりIntをとりIntを返すクロージャを返す

func hoge5(_ closure: @escaping (Int) -> Int) -> ((Int) -> ((Int) -> ((Int) -> Int))) {
return { a in
return { b in
return { c in
return closure(a + b + c)
}
}
}
}

hoge5({ a in return a })(1)(2)(4) // return 7

/// IntをとりIntをとりIntを返すクロージャをとり、IntをとりIntをとりIntをとりIntを返すクロージャを返す

func hoge6(_ closure: @escaping (Int) -> ((Int) -> Int)) -> ((Int) -> ((Int) -> ((Int) -> Int))) {
return { a in
return { b in
return { c in
return closure(a)(b + c)
}
}
}
}

hoge6({ a in return { b in return a + b }})(1)(2)(4) // return 7

/// IntをとりIntをとりIntをとりIntを返すクロージャをとり、IntをとりIntをとりIntをとりIntを返すクロージャを返す

func hoge7(_ closure: @escaping (Int) -> ((Int) -> ((Int) -> Int))) -> ((Int) -> ((Int) -> ((Int) -> Int))) {
return { a in
return { b in
return { c in
return closure(a)(b)(c)
}
}
}
}

hoge7({ a in return { b in return { c in return a + b + c }}})(1)(2)(4) // return 7


hoge5 ~ 7 は、引数がクロージャのため、hoge1 ~ 4 の返却値を使うことができる

hoge5(hoge1)(1)(2)(4)    // return 7

hoge5(hoge2(1))(2)(4)(8) // return 15
hoge5(hoge3(1)(2))(4)(8)(16) // return 31
hoge5(hoge4(1)(2)(4))(8)(16)(32) // return 63

hoge6(hoge2)(1)(2)(4) // return 7
hoge6(hoge3(1))(2)(4)(8) // return 15
hoge6(hoge4(1)(2))(4)(8)(16) // return 31

hoge7(hoge3)(1)(2)(4) // return 7
hoge7(hoge4(1))(2)(4)(8) // return 15


返却値であるクロージャをそのまま持つことももちろんできる

数値を渡しているが、実際に計算は行われていない

hoge7(hoge3)    // return (Function)

hoge7(hoge3)(1) // return (Function)
hoge7(hoge3)(1)(2) // return (Function)


ループを回して、全部足す

わかりやすいようにループ外は0としている

var loop1 = hoge7(hoge4(0))

(1...100).forEach { loop1 = hoge6(loop1($0)) }
loop1(0)(0)(0) // return 5050


TとTをとりTを返すクロージャをとり、TをとりTをとりTを返すクロージャを返す

演算子渡したいので、Numericに適合した型を引数とする

func hoge8<T>(_ process: @escaping (_ lhs: T, _ rhs: T) -> T) -> ((T) -> ((T) -> T)) where T: Numeric {

return { a in
return { b in
return process(a, b)
}
}
}


処理内容に演算子をわたせる(2値をとり1値を返すならなんでもよい)

hoge8(+)(5)(9)    // return 14

hoge8(-)(5)(9) // return -4
hoge8(*)(5)(9) // return 45
hoge8(/)(5)(9) // return 0
hoge8(max)(5)(9) // return 9
hoge8(min)(5)(9) // return 5


ループを回して全部足す

わかりやすいようにループ外は0としている

var loop2 = hoge8(+)(0)

(1...100).forEach { loop2 = hoge8(+)(loop2($0)) }
loop2(0) // return 5050


ループを回して最大値を得る

わかりやすいようにループ外は0としている

var loop3 = hoge8(max)(0)

(1...100).forEach { loop3 = hoge8(max)(loop3($0)) }
loop3(0) // return 100


ループを回して意味不明なことをする

わかりやすいようにループ外は0としている

var loop4 = hoge8(+)(0)

(1...100).forEach {
let temp: (Int) -> Int
switch $0 % 6 {
case 0:
temp = hoge8(+)(loop4($0))
case 1:
temp = hoge8(-)(loop4($0))
case 2:
temp = hoge8(*)(loop4($0))
case 3:
temp = hoge8(/)(loop4($0))
case 4:
temp = hoge8(max)(loop4($0))
default:
temp = hoge8(min)(loop4($0))
}

loop4 = temp
}
loop4(0) // return 93



最後に

すべてきちんと読んだ方、お疲れ様でした。

実際のプロダクトには上記のような他人が読みづらいクソコードは埋め込まないように注意しましょう。

より深遠な関数型プログラミングが知りたい方、上記のコードでは全然足りなかった方は、下記のリンクを踏んでみると楽しいと思います。

https://github.com/typelift/Swiftz