末尾再帰
なんか関数の最後で再帰呼び出しすると勝手にループになるらしいですよ。まぁ適当にデクリメントしながら画面に表示する関数を書く。
let rec print_decrement x = if x = 0 then () else ( print_int x; print_decrement (x-1); ) let _ = print_decrement 100000; print_newline ();
ここでポイントは else の後に ( を書くと手続き脳の世界に行けるというこないだ教えてもらったことなのですがそれはともかく。
080497b0 <camlTail__print_decrement_57>: 80497b0: 83 ec 04 sub $0x4,%esp 80497b3: 83 f8 01 cmp $0x1,%eax 80497b6: 75 18 jne 80497d0 <camlTail__print_decremen t_57+0x20> 80497b8: b8 01 00 00 00 mov $0x1,%eax 80497bd: 83 c4 04 add $0x4,%esp 80497c0: c3 ret 80497c1: eb 0d jmp 80497d0 <camlTail__print_decremen t_57+0x20> 80497c3: 90 nop 80497c4: 90 nop 80497c5: 90 nop 80497c6: 90 nop 80497c7: 90 nop 80497c8: 90 nop 80497c9: 90 nop 80497ca: 90 nop 80497cb: 90 nop 80497cc: 90 nop 80497cd: 90 nop 80497ce: 90 nop 80497cf: 90 nop 80497d0: 89 04 24 mov %eax,(%esp) 80497d3: e8 28 07 00 00 call 8049f00 <camlPervasives__string_o f_int_153> 80497d8: 89 c3 mov %eax,%ebx 80497da: a1 18 a6 05 08 mov 0x805a618,%eax 80497df: e8 9c 08 00 00 call 804a080 <camlPervasives__output_s tring_214> 80497e4: 8b 04 24 mov (%esp),%eax 80497e7: 83 c0 fe add $0xfffffffe,%eax 80497ea: eb c7 jmp 80497b3 <camlTail__print_decremen t_57+0x3> 80497ec: 8d 74 26 00 lea 0x0(%esi),%esi
んーなんでそんな nop はさまってるねんと思いつつも、まぁ call print_decrement みたいなのが無いのは確認できますねーよかたですねー。んで print_decrement => print_int と呼び出し順を変えてやると、
80497d6: e8 d5 ff ff ff call 80497b0 <camlTail__print_decrement_57>
とかが現れやがりまして、ああなるほど再帰しちゃってるのうと。実際実行してやるとスタックオーバーフローするわけですよ。
でもですね。ぶっちゃけ末尾かどうかなんて気にするのめんどいのでなんかどうでもいいというかですね。私はシグナルハンドラの中でなんでもやっちゃいたい人なんですよとかそういう話。
そんなこと気にしてたら LL の舞台には上がれませんよ。ていうか OCaml はどう見ても LL じゃないと思います。だって print_string とか print_int とか + とか +. とか .() とか .[] とかなんか気軽感がどこにもないですよ。あえて言うなら型書かなくていいという話ですけど、それなら C も LL ですな。
あ、ちなみに 新しい言語を学ぶときはアセンブリを出力しなさいというお告げ があったのでなんとなく objdump したとかそういう複雑な経緯がありまして。