命令型プログラミングに経験がある関数型プログラミング (FP) の初心者は、FP において for ループや while ループを作る方法にしばしば疑問を持ちます。 一般的な答は、それらは高階関数で置き換えられるので FP ではループを生成する必要はない、というものです。 はじめに、この答においていくつか困難な問題があることを見てみましょう。
次のC言語コードは、自然数 n に適用されると最初の n 個の自然数の合計を返す関数を実装しています:
この関数 tally と等価な ATS の関数は次のように与えられます:fun tally ( n: int ) : int = loop (0, 0) where { fun loop (i: int, res: int): int = if i < n then loop (i + 1, res + i) else res }
ループ生成を高階関数で置換できると誰かが指摘するとき、彼等はおそらく次のような関数でループを生成しようとするでしょう:
fun for_loop ( count: int, limit: int, fwork: (int) -<cloref1> void ) : void = ( if count < limit then (fwork(count); for_loop(count+1, limit, fwork)) else () // end of [if] ) (* end of [for_loop] *)
fun tally2 ( n: int ) : int = let val res = ref<int> (0) in for_loop (0, n, lam (i) => !res := !res + i); !res end // end of [tally2]
tally2 における参照の必要性を除去するために、for_loop を次のような関数テンレプレート for_loop2 に変換しましょう:
// fun{ env:t@ype } for_loop2 ( count: int, limit: int , env: &env >> _, fwork: (int, &env >> _) -<cloref1> void ) : void = ( // if count < limit then ( fwork(count, env); for_loop2<env> (count+1, limit, env, fwork) ) else () // end of [if] // ) (* end of [for_loop2] *)
fun tally3 ( n: int ) : int = let var res: int = 0 in for_loop2<int> (0, n, res, lam (i, res) => res := res + i); res end // end of [tally3]
fun{ env:t@ype } for_loop3 ( count: int, limit: int, env: &env >> _ ) : void = ( if count < limit then ( for_loop3$fwork<env>(count, env); for_loop3<env>(count+1, limit, env) ) else () // end of [if] ) (* end of [for_loop3] *)
fun tally4 ( n: int ) : int = let // var res: int = 0 // implement for_loop3$fwork<int> (i, res) = res := res + i // in for_loop3<int> (0, n, res); res end // end of [tally4]
ここまで読んだ読者は、FP におけるループ生成は高階関数で容易に置き換えられるという主張が著しく不正確であることに同意できるでしょう。 この章で紹介したコード全体とテストコードを含む loopcons.dats ファイルはオンラインから入手できます。