さらに継続を渡して

find-foldからfind-fold2にすることで、procを継続渡し方式に変更できた。
だけど、find-fold2自身は(null? lis)が真となるときに、依然として「seed(=戻り値)を返却する」というCall-Return方式になっている。
これを継続渡し方式に修正してみよう、とのこと。

(define find-fold/cont
  (lambda (pred? proc/cont seed lis cont)
    (cond
     ((null? lis) (cont seed))
     ((pred? (car lis))
      (proc/cont (car lis)
                 seed
                 (cut find-fold/cont pred? proc/cont <> (cdr lis) cont)))
     (else
      (find-fold/cont pred? proc/cont seed (cdr lis) cont)))))

継続手続きのcontが増えた。結果をcontに渡しさえすれば良いので、結果であるseedをcontに渡している。
あと、わざとらしく部分適用のcutを使ってみた。cutに関してはid:yagiey:20090220:1235132459を参照のこと。


さて、このfind-fold/cont使ってみる。add/contの定義はid:yagiey:20100327:1269700449を参照のこと。

gosh> (find-fold/cont odd?
                      add/cont
                      0
                      '(1 2 3 4 5 6 7 8 9 10)
                      print)
25
#<undef>

25はprintによるもの。
contとして引数をそのまま返す手続きを与えてみると、

gosh> (find-fold/cont even?
                      add/cont
                      0
                      '(1 2 3 4 5 6 7 8 9 10)
                      (lambda (_) _))
30

contにはそれまでの計算結果が渡されてくるはずなので、結果的に計算結果が取得できるわけだな。なるほどー。
結果に2を掛けた値が欲しければ、contとして

(lambda (n) (* 2 n))

を渡せばよさそうだな。
何となく継続という概念が分かってきた気がする。


実は、find-fold/contはまだ完全な継続渡し方式になっていないそうだ。
それは、(null? lis)と(pre? (car lis))のところ。
それぞれ、戻り値を見て、どうするかを決めているから。
完全な継続渡し方式にするとこうなる、というのがコラムに書いてあるが、また今度自分で書いてみよう。

分からないこと1

戻り値を利用するのがCall-Return方式ならば、elseでの再帰はCall-Return方式なんじゃないのかな?
なんでそのことには触れないのだろう。

分からないこと2

関数型言語と呼ばれる言語では、入力から出力へのマッピング(つまり関数)でもってプログラミングしていくものだと思ってきた。
つまり、Call-Return方式が素直な考え方だと思うけど、

  • 戻り値にはあまり意味がない
  • 計算結果が帰ってこない

みたいな考え方の継続渡し方式は、Call-Return方式とは馴染まないような気がする。
Call-Return方式と継続渡し方式を混ぜて使うことはあるのかな?