星田さんの記事に対するコメント。
2進数への変換コード。打ちはじめてびっくり、全然指が動かねぇ・・以前はPythonで学習をしてたのに全く分からなくなってるw
うん、無事Lispに脳が侵されてるね(笑)。
最初はPython <-> Lispの行き来に若干混乱するかもしんない。
でも慣れます(笑)。
っつーかね、だから「Pythonのコードらしいコード」じゃなくって「C言語脳が書いたPythonコード」に拒否感が生じるでしょう。
それは「正しい感覚」なんです。
自分を信頼しよう。
多分その本のコードがあんまり「美しくない」んだ。
今度は16進数変換コード。こういうアルゴリズムだったのか・・・これが分かってればX68000ももうちょっと楽しめた(かも知れない)のになぁ。
ちなみに、僕だったらそんな風には書かない。
そもそも、10 => 'A' なんつー変換をif ~ else で書くのはメンドイでしょ?
そういうときはハッシュテーブル使って「変換テーブル」を書いちゃうのが吉です。
そうすればコードが圧縮されて短くなります。
def dec2hex(target):
acc = []# ここで変換テーブルを作っちゃう
table = {10 : 'A', 11 : 'B', 12 : 'C', 13: 'D', 14 : 'E', 15 : 'F'}
while target != 0:
acc += [target % 16]
target //= 16# for でダラダラ書くならリスト内包表記を# Lispなら map を使う局面だ!
return list(reversed([table[i] if i > 9 else i for i in acc]))
せっかくなので16進数変換コードをRacketに移植してみる。上のLetはいらんかったな・・。とりあえずこれで動くので、ここから10〜15までの場合はABCDEFに変わるようにするには・・
いや、よく書けてますよ(オプショナル引数の導入、とか上手い)。
ちなみに、僕ならこう書く。っつーか発想はPython版と同じ。
(define (hex target)
(let ((table '((10 . A) (11 . B) (12 . C)
(13 . D) (14 . E) (15 . F))))
(let loop ((target target) (acc '()))
(if (zero? target)
(map (lambda (x)
(if (> x 9)
(cdr (assv x table))
x)) acc)
(loop (quotient target 16)
(cons (modulo target 16) acc))))))
直接末尾再帰せずに名前付きletを使った理由は、変換テーブルも再作成されるのを避ける為。再帰部分は再帰部分で独立しておけば、変換テーブル作成は巻き込まれない。
今話題のX68000にあやかって68000を変換してみる。これあってんのかなw。
合ってます。
Racketで16進数が正解かどうか調べるには、
> #x109A0
68000
>
と直接16進数を入力してみれば良い。
- #x -> 16進数接頭辞
- #o -> 8進数接頭辞
- #b -> 2進数接頭辞
で全部で3種類、すぐさま10進数に直して返してくれます。
あるいは、本当はRacketなら
(define (hex target)
(number->string target 16))
って書いても良くって、組み込み関数number->stringは数値を文字列に直すんだけど、その際に基数を指定できるのね。上の例だと16を与えて16進数表記なんだけど、例えば2を与えると2進数表記の文字列に変換してくれる。
従って、
> (string->number "109a0" 16)
68000
>
と元に戻れば計算が成功してる、と言うことになる。
ちなみにPythonでは
0x109a0
と書いて、
- 16進数は0xではじまる
- 8進数は0oではじまる
- 2進数は0bではじまる
とすれば全部10進数に変換して「答え合わせ」が可能です。
参考まで、に。