読者です 読者をやめる 読者になる 読者になる

CL版ModanShogi

http://gihyo.jp/dev/column/01/prog/2010/aprilfool2010-01
Route 477 - プログラミング言語ModanShogiを公開しました

プロesolang作家yhara先生のファンの1人としてこれは取り組まねばなるまい、という事でCLに移植。
http://gist.github.com/351580
パーサが手抜きなので、ラベル定義(*n)の後ろにスペースや改行等がないとパースエラーになります。あと"△投 了"とかが最後にあってもエラーに。

sbclxyzzyで動作確認をしていますが、xyzzyでbyte-compileする場合はsjisで保存しておいてください。
sbclでは適切にexternal formatを設定してあればsjisでもutf8でも動くんじゃないでしょうか。

xyzzyで試す

適当に*load-path*(site-lispとか)にmodanshogi.lというファイル名で放り込んで、*scratch*上で

(require 'modanshogi)
(modanshogi:run
 "▲9九金 △8七金 ▲6四歩 △5五歩 ▲6六銀 △6五銀 ▲7七金 △7六金 *1
 ▲7二角 △7一歩 ▲8四王 △5八玉 ▲8八龍 △8九歩 ▲9二馬 △1一飛 *2")

と実行すればフィボナッチ数列が1000番目まで表示されると思います。

コンパイルして実行する

modanshogi:runは中間コードを生成してインタプリタ方式で実行しますが、コンパイラも用意してあります。(modanshogi::compile%)
上記のフィボナッチ数列を表示するコードをコンパイルすると

(LET ((#:GREG (MAKE-ARRAY 10 :INITIAL-CONTENTS '(0 1 2 3 4 5 6 7 8 9)))
      (#:GSTACK (LIST)))
  (DO ((#:GLABEL '#:GSTART
                 (BLOCK NIL
                   (CASE #:GLABEL
                     (#:GSTART
                      (DECF (AREF #:GREG 9) (AREF #:GREG 9))
                      (DECF (AREF #:GREG 8) (AREF #:GREG 7))
                      (INCF (AREF #:GREG 6) (AREF #:GREG 4))
                      (INCF (AREF #:GREG 5) (AREF #:GREG 5))
                      (SETF (AREF #:GREG 6)
                              (* (AREF #:GREG 6) (AREF #:GREG 6)))
                      (SETF (AREF #:GREG 6)
                              (* (AREF #:GREG 6) (AREF #:GREG 5)))
                      (DECF (AREF #:GREG 7) (AREF #:GREG 7))
                      (DECF (AREF #:GREG 7) (AREF #:GREG 6))
                      1)
                     (1
                      (WHEN (>= (AREF #:GREG 7) 0) (RETURN (AREF #:GREG 2)))
                      (INCF (AREF #:GREG 7) (AREF #:GREG 1))
                      (PRINC (AREF #:GREG 8))
                      (PRINC (CODE-CHAR (ROUND (AREF #:GREG 5))))
                      (PUSH (AREF #:GREG 8) #:GSTACK)
                      (INCF (AREF #:GREG 8) (AREF #:GREG 9))
                      (SETF (AREF #:GREG 9) (POP #:GSTACK))
                      (WHEN (/= (AREF #:GREG 1) 0) (RETURN (AREF #:GREG 1)))
                      2)
                     (2 NIL)))))
      ((NULL #:GLABEL))))

こんな感じに。
というわけでSBCL上でcompile-and-runすればネイティブコードにコンパイルされて実行されるのでとっても高速!

CL-USER> (time (modanshogi:compile-and-run modanshogi::*kifu-fib-10k*))
54438373113565281338734260993750380135389184554695967026247715841208582865622349017083051547938960541173822675978026317384359584751116241439174702642959169925586334117906063048089793531476108466259072759367899150677960088306597966641965824937721800381441158841042480997984696487375337180028163763317781927941101369262750979509800713596718023814710669912644214775254478587674568963808002962265133111359929762726679441400101575800043510777465935805362502461707918059226414679005690752321895868142367849593880756423483754386342639635970733756260098962462668746112041739819404875062443709868654315626847186195620146126642232711815040367018825205314845875817193533529827837800351902529239517836689467661917953884712441028463935449484614450778762529520961887597272889220768537396475869543159172434537193611263743926337313005896167248051737986306368115003088396749587102619524631352447499505204198305187168321623283859794627245919771454628218399695789223798912199431775469705216131081096559950638297261253848242007897109054754028438149611930465061866170122983288964352733750792786069444761853525144421077928045979904561298129423809156055033032338919609162236698759922782923191896688017718575555520994653320128446502371153715141749290913104897203455577507196645425232862022019506091483585223882711016708433051169942115775151255510251655931888164048344129557038825477521111577395780115868397072602565614824956460538700280331311861485399805397031555727529693399586079850381581446276433858828529535803424850845426446471681531001533180479567436396815653326152509571127480411928196022148849148284389124178520174507305538928717857923509417743383331506898239354421988805429332440371194867215543576548565499134519271098919802665184564927827827212957649240235507595558205647569365394873317659000206373126570643509709482649710038733517477713403319028105575667931789470024118803094604034362953471997461392274791549730356412633074230824051999996101549784667340458326852960388301120765629245998136251652347093963049734046445106365304163630823669242257761468288461791843224793434406079917883360676846711185597501
Evaluation took:
  0.063 seconds of real time
  0.062500 seconds of total run time (0.062500 user, 0.000000 system)
  98.41% CPU
  12 forms interpreted
  168 lambdas converted
  91,889,329 processor cycles
  6,051,656 bytes consed

…ただのループなんでxyzzy上で実行しても0.1秒で終わっちゃうんですけどねー。

コンパイラ書いててちょっと残念だったのは、飛角が第2引数のレジスタの中身見て飛ぶんじゃなくて第2引数のラベルに飛ぶっていう仕様だったらdo, caseを使わずtagbody, goですっきり書けたのになーというのがあります。(ラベルが1〜9に限定されてしまいますが)
まあ速度は大して変わらないですかね。

以下ソース埋め込み