星田さんの記事に対するコメント。
ザーッと見たカンジ「手慣れてきてる」。スゴい進歩です。
ただ、多分本人も痛感してるだろうけど、「あと一歩」のトコがあるのね。「破壊的変更」使っちゃったりした辺りがそう。
ひょっとしたら余計なお世話で、言われたくねぇ、とか言う部分もあるだろうけど、「鉄は熱いウチに打て」とも言うんで、コードを書いてすぐさまテクニック的な話を書くのは悪い事じゃないと思って心を鬼にして敢えて書きます。
時間経つと自分で書いたロジックさえ「忘れちゃう」んでね(これがすげぇ良くある・笑)。
おおぅ・・入力が小数点ありなので返ってくるのも実数型じゃないか、見づらいw。そして破壊的操作にLoopの入れ子が面倒だということで並行して書いて最後にまとめて出力するという・・でも、こうやってかけるかな?ってのでちょっと実験的に書いてみたってのもあるので(^_^;) あ、出力自体はあってます、やったぜ!
関数内で名前付きletを2つ使う、ってのはすげぇ良いアイディア。
ただ、問題は「この2つを一気に返すにはどうすりゃエエんだ」って辺りで、それで破壊的変更に頼っちゃったのね。
ここでちと思い出して欲しいのは、名前付きletってのは事実上ローカル関数だ、と言う事。そしてそのローカル関数は最終的に何らかの値を返す、と言う事なの。
って事は、この例のように名前付きletが2つあっても、そいつらを纏めてリストで囲んでしまって良い、と言う事になる。
敢えてロジックは変えてないんだけど、例えば次のように書けば破壊的変更のお世話にならなくて済みます。
(define (dec2b target)
`(,(let loop1 ((int (truncate target)) (acc '()))
(if (zero? int)
acc
(loop1 (quotient int 2) (cons (remainder int 2) acc))))
,(let loop2 ((f (- target (truncate target))) (acc '()) (n 0))
(if (or (zero? f) (>= n 10))
(reverse acc)
(let ((m (truncate (* f 2))))
(loop2 (- (* f 2) m) (cons m acc) (+ n 1)))))))
逆に言うと、こう書いて許されるのがLisp系言語の「自由な」トコなんです。
なお、今までもチラチラ書いてはいたんだけど、例えばa、bが何かに束縛されている場合、
(list a b)
って書くのを短縮して
`(,a ,b)
って書くのが粋な書き方。こっちの方がタイピング量が少ない。
'(a b) って書くとaもbも評価されないんだけど、`(,a ,b) なら(list a b) と同様に、aとbが評価されてリストが生成されます。上のコードはそれを利用して、リストの要素である名前付きlet2つを「評価して」リスト化してるわけ。
`は「逆クオート」とか「逆引用符」と呼ばれる記号で、JISキーボードだとShift-@で出てきます(あまり普段は使わないんで、「どこにある?」ってなりやすい)。
あと、もう1つの手はもっとポピュラーな手で、名前付きletの代わりに実際にローカル関数を具体的に定義しちゃう事。こういう時がletrecの出番なんだけど、通常はinternal defineを使う事が好まれるかな。
(define (dec2b target)
(define (loop1 int (acc '()))
(if (zero? int)
acc
(loop1 (quotient int 2) (cons (remainder int 2) acc))))
(define (loop2 f (acc '()) (n 0))
(if (or (zero? f) (>= n 10))
(reverse acc)
(let ((m (truncate (* f 2))))
(loop2 (- (* f 2) m) (cons m acc) (+ n 1)))))
(let* ((int (truncate target)) (f (- target int)))
(values (loop1 int) (loop2 f))))
まぁでも、実のこと発想は両者同じなんです。見た目が違うだけ、でね。
一般的には後者の方が「プログラムらしいプログラムに見える」んで好まれるかな(笑)。
ちなみに、floatをintegerに変換するには、inexact->exactを使います。最後に得られるリストの各要素にマッピングしちまえば見た目はマシになるでしょう。
うーん・・どうせまた勘違いしてるなこれは。
再帰部分のfは一体何を参照してるでしょう?