Archive for 5月, 2008

パーサのバグを修正

金曜日, 5月 30th, 2008

* *scrap*でニコ動Lispのバグを指摘されました。

(defun if0 (n a b) (if (eq 0 n) a b))
(if0 0 1 2) ;=> 2
((lambda (n) (if (eq 0 n) 1 2) 0) ;=> 2
((lambda (n) (if (eq n 0) 1 2) 0) ;=> 1

(下2行は括弧が足りませんが、ただの誤字でしょう。)
試してみると、たしかにその通り。
youzさん、報告ありがとうございます。
ソースを読んでみると、
数字→スペース→文字(数字以外)
の順番で文字を読み込んだときの処理というのがまるまる抜けていました。
(eq 0 n)がリードされると(eq 0)になっていた。これは酷い。
ついでにevalもつくっておきました。評価にはevalを呼んだときの環境が使われます。
面倒だったので、長時間版の方しか変更していませんが、いいですよね。

ニコ動Lispを公開してから、いくつかバグを見つけては修正しているんですが、
そのほとんどの原因がパーサだったりします。
パーサは最初に書き始めた箇所で、あまり何も考えずに
「動けばいいや」という精神で書いたんですが、
それが後になって苦労の種となってしまいました。
ソースが手抜きなのはともかく、せめて設計だけでも先に真面目にやってればよかった……

ニコ動Lisp 2.0[長時間版]

金曜日, 5月 30th, 2008

3分では短いとのことなので、9分6秒のものを作りました。

動画を作った後で気づいたんですが、
「ニコスクリプトは1000行まで」(ただし1行に複数命令を書ける)
という制約があり、インタプリタ本体で150行使用し、
「○分○秒に処理をする」というのには1行を使用するため、
0.5秒に1回処理をするように作っていたら、
7分5秒(+初期化分)しか処理が出来ないことになります。
処理を1秒に2回(同じ時刻に行うので1行で書ける)にすると、
倍の14分10秒まで処理を続けることが出来るので、この方法を採用しました。
しかし、同じ時刻に処理を行うと、表示が後からの処理の方しか画面に反映されず、
少々かっこ悪くなりました(特にパース時が顕著です)。
また、処理自体も重たくなった気がします(これは推測)。
そんな訳で、長時間処理を続けるインタプリタを
ニコ動上で動作させるのは色々と問題がありそうです。

ニコ動Lisp 2.0[lambda始動]

水曜日, 5月 28th, 2008

ニコニコ動画のメンテナンスが終わったので、ニコ動Lisp2.0(仮称)が始動しました。

「もっと評価されるべき」タグが付いていたので、文字アトムも評価することに。
もっと評価されるべきの意味が違うという突っ込みもありました。
期待通りの突っ込みをありがとうございます。
それはともかくスクリプトを更新して1分もしないうちに
「ラムダきたー」というコメントが書き込まれたんですよ。
どれだけlambdaが欲しかったんだwwww
まあ、なにはともあれ、lambdaが無事動いてよかった。
普段から「lambdaかわいいよlambda」とお思いの諸兄も安心してニコニコできますね。

prognとかdefunとかLisp的(NOT Scheme的)なスペシャルフォームを持っていますが、
lambdaに関してはScheme的な評価規則となっています。
CommonLispでは次のような式は動作しません。

(((lambda (x)
(lambda (y)
(+ x y)))
3)
4)

というのも、関数適用の第一要素はシンボルか(lambda …)に限られているためです。
一方、Schemeではこの式は正しく動作します。
Schemeでは手続き呼び出しの第一要素を評価したものを手続きとして扱い、その形は問わないからです。
ニコ動LispもこのSchemeの規則に従っています。
そのため、上記式は問題なく動きます。

ニコ動Lispはセル、スタックを自分で管理しているので、
本当はGCを書かないといけないんですけど、サボって書いていません。
評価しすぎると大変なことになるのでご注意を(笑)
……いま気づいたんですが、スタックを自分で管理してるってことは、
call/ccも結構簡単に書けますね。頑張れば。
でも、あまりにソースが汚いので頑張る気は起きません(笑)

あの巫女の絵は誰が描いたのかという質問があったので回答。
あれは私の兄が書いたものを無断で使用しました。
以下私と兄のメールでのやり取り。
zick:
前の一恋とクルルの絵を無断で使わせてもらうよ!
(↑こういった時点で無断じゃなくなったやったね(笑))
兄:
お前・・・・頭、いいな・・・

ニコ動Lisp 2.0[予告および仕様]

水曜日, 5月 28th, 2008

ニコニコ動画で動作するLispインタプリタですが、
ついにlambdaとdefunが完成しました。
もちろん再帰呼び出しも可能です。

(defun f (x) (if (eq x 0) 0(+ x (f (- x 1))))) (f 3)

こんなコードが動きます(ただし、2回に分けて入力してください)。
で、ちゃんとテストもしたし、これで完成だと思い、
確定ボタンを押したんですが、なぜかニコニコ動画から反応が返ってこない。
何回試しても反応が返ってこない。
一旦ログインからやり直してみるかと思い、ログアウトしようとしたら、

メンテナンスのお知らせ
現在ニコニコ動画はメンテナンス中です。
5月28日(水) 13時00分~16時00分(予定)
※都合により変更となる可能性がございます。

ぎゃああああああああああああああああ
ちなみに、スクリプトの確定ボタンを押したのは13時を5分ほど回ったときでした(泣)

**ニコ動Lisp 2.0 仕様**
サポートする関数:
car
cdr
cons
eq (数の比較もこれで行う)
atom
+ (引数は任意個)
- (引数は2つ)
サポートするスペシャルフォーム:
quote
if
progn
lambda
defun
データ:
文字アトム (日本語なども使用可能)
数アトム (入力時は非負整数のみ)
コンス (今のところはimmutable)
注意点:
ドット記法による入力は受け付けない。すなわち、 ‘(a . b) は正しい式として扱われない
セルの数は512個固定であるが、GCは行われない。
0.5秒に1回の処理を行うため、再帰呼び出しを繰り返し使うような式は動画の時間内で評価が終わらない可能性がある。
バグと思われるものがあったら報告してもらえると嬉しいです。
ただし、修正されるまではそのバグは仕様です(笑)

続・ニコ動でLisp

火曜日, 5月 27th, 2008

昨日の動画に書き込まれたコメントを見てみたら、
(+ 1 2) とか (print “hoge”) といった書き込みが多数ありました。
pure lispっていったのに……
特に前者のような数値演算をする式があまりにも多かったので、
急遽、数アトムと+をつくり、ついでにifやprognも作っておきました。
少し気になったので、コメントを
・リスト操作
・数値演算
・printを使用
の3つに分類してみたところ、以下のような結果になりました。
(複数に分類されたり、どれにも分類されなかったものもあります。)
・リスト操作: 32%
・数値演算: 47%
・printを使用: 20%
ちなみに、この集計を取ったのは、数アトムと+を作った後ですが、
半数のコメントはこの改造前に書かれていたものでした。
ちゃんと評価できた式は50%前後といったところでしょうか。
数値計算はともかく、20%もの人がprintを使ったというのが驚きです。
(まあ、よく分からない人が他の人のまねをして、
その結果printが量産されたとも考えられますが。)

話は変わりますが……
ジュンク堂に行ったら、オライリーフェアとかなんとかで、
コースターがもらえました。ヒトデのやつ。
ヒトデのコースターといえば、以前CLANNADのDVDの初回特典に
ついてきたものがあったので、せっかくなので並べてみました。
ヒトデ
今日はヒトデ祭りだぞ!

ニコ動で動作するLispインタプリタ書いたよ

月曜日, 5月 26th, 2008

どうもzickです。
2/1に見た夢をきっかけに触り始めたニコスクリプト(正確にはニワン語というらしい?)ですが、
しばらく触っていないうちに色々と機能が追加されており、なんと手続きが作れるようになっていました。
知らない機能を色々触っていたら、いつの間にかLispインタプリタが出来上がっていました。

2/1に見た夢が4ヶ月ほどを経て正夢になってしまいました。
関係ありませんが、少し前に単位が足りなくて4年生になれない夢を見ました。
こちらの夢は正夢にならないことを祈るばかりです。

*ニワン語メモ*
大体のことはニコニコ動画まとめwikiを見れば分かります。
(Lispインタプリタを半分くらい作ってからここの存在に気づきました。もっと速くググればよかったorz)
とりあえず、ここに載っていないような細かい事項を書いておきます。
[1行の制限]
スクリプトは1行に1024文字まで書くことができます(以前はもっと短かった気がします)。
手続きを作って処理を分割できるので、この制限はあまり問題ないでしょう。
[文字列]
文字列に str[n] のように添え字をつけるとn文字目(0から始まる)の文字が得られます。
一見C言語のようですが、正確にはn文字目の文字のみから成る文字列が得られます。
その文字列自身ではなく恐らくコピーが得られるので文字列はimmutableです。
また、文字列のメソッドindexOfは以前は1引数の関数でしたが、
いつのまにか2引数の関数になってました。詳しくはまとめwiki参照。
[配列]
arr=[1,2,3,4,5] のように大括弧で囲むと配列を作ることが出来ます。
大きな配列を作りたかったら要素を沢山書いたらいいのですが、
要素数を100個ほどにすると動作しません(50個では動作しました)。
配列の要素に配列を入れることができるので、大きな配列が必要な場合はこれを使うのも手です。
(push, shiftを使って動的に配列を大きくした場合どうなるかは未確認です)
[手続き]
再帰呼び出しが可能です。その場合も引数$nが壊れることはありません。
これでループが書けます。恐ろしく便利です。

続・Amazon…

水曜日, 5月 21st, 2008

本日は皆様ご存知の通りTVアニメ版CLANNADのDVD第6巻の発売日ですね。
なんかCLANNADのDVDが出るたびに、もう一ヶ月がたったのかと実感してしまいます。
それはともかくおよそ一ヶ月前、4/19の日記にて、

ご注文時に予定して おりました発送予定日となっておりますが、まだ下記の商品の調達ができておりませ ん。

というメールがAmazonから届いたと書きましたが、
次はこんなメールが届きました。

誠に申し訳ございませんが、大変残念なご報告があります。お客様のご注文内容のうち、
以下の商品については入手できないことが判明いたしました。
(中略)
勝手ながらお客様の注文からこの商品をキャンセルさせていただきました。

\(^o^)/

プログラミングの難易度は質によりけり

月曜日, 5月 19th, 2008

今更ですが、プログラミングのジャンルと難易度(および Web プログラミング批判) – 黎明日記に反応してみる。
*コンパイラって難しい?*
コンパイラは最も簡単なものでもある程度難しいという意見が出てますが、
実際のところ、そうでもないかと思います。
まともな言語のまともなコンパイラを作るとしたら非常に難しいかともいますが、
ふざけた言語のふざけたコンパイラならそこまで難しいこともないでしょう。
(例えば、アセンブラに毛が生えたようなものを作るとか。)
それに、講義で実際にコンパイラを作る大学も結構あるのではないでしょうか(*1)。
そう考えると毎年、結構の数の学生がコンパイラを作っているはずかと思います。
ただ、そういった学生が自分のブログなどで
「俺はコンパイラを作ったZE☆」
と自慢している姿を見かけることはあまりありません。
また、Webで「コンパイラ作りましたよー」と発表しているものは大抵質がいいものだったりします。
質が良い => ネットで公開される
質が悪い => ひっそりと作者の記憶の中に埋もれていく。
このような図式が成立しているため、
「コンパイラは最も簡単なものでもある程度難しい」
という気がしてしまうのではないでしょうか。
(というか、質が悪かったら「そんなものコンパイラとは言えんわ!」で済まされるのかも。)
*Webプログラミングの程度*
Webプログラミングはコンパイラの話と逆で、
どんな簡単なものでも、自分のブログで
「こんなもの作ったZE☆ 俺スゲー!」
と書いている人が非常に多いようです。
そのため、
1. 質の悪いものがどんどん発信されていく
2. 全体として質の悪いもの割合が非常に大きくなる
3. あれ、Webプログラミングって程度低くない?
こうなるのではないでしょうか。
まあ、WebプログラミングだからWebに公開するもんなんでしょうか。
なによりも問題は、
金をもらってコンパイラを作ってる人はほぼ確実に凄いものを作り上げるでしょうが、
金をもらってWebアプリケーションを作っている人の中には、
タダだとしても使いたくないようなものを作る人がいることかもしれません(*2)。
*個人的なWebプログラミングへの思い*
私としてはWebプログラミングというのはあまり好きな分野ではありません。
理由は何故だか良く分かりません。好きじゃないものは好きじゃないからしょうがない。
さっとモノがつくれてすぐ動くのは楽しかったりします。
高校のときにPerlで書いた、
『全自動掲示板』(ランダムに生成された文章が勝手に書き込まれ、その書き込みにレスまで勝手に付くすぐれもの)
といったものは、友人の間で非常に受けがよく、大学でも非常に好評でした。
大学に入ってからも『Web CLANNAD』(ブラウザでCLANNADをプレイするためのもの。文字しか表示されないが、選択肢とジャンプは作った)
というふざけたものをPHPで書いたりもしました。
どちらも実用性の全くないものですが、
作っていて非常に楽しかったですし、またすぐに完成するのも気持ちよかったです。
楽しかったんですが、こればっかしやろうという気にはとてもなれませんでした。
また、まともなWebアプリケーションを作ろうという気にはもっとなれませんでした。
不思議。
*結論*
無駄に長くなったけど、
「質がいいものを作るのは難しいよね。」
これで十分じゃないでしょうか。
ちなみに私は質が悪いものを次々作っていくのが好きです(笑)
*どうでもいいこと*

Web アプリケーションを構文解析に劣る程度の文字列処理、あるいは GUI プログラムの焼き直しで終わらせちゃいけない。違いませんか?

構文解析で文字列処理はあんまりしないでしょ。
別に構文解析が文字列処理やっちゃいけないことはないけど、
個人的には面倒な文字列処理は字句解析で済ませて楽したいです。

(*1)
少なくても私の大学ではやっています。おまけに必修です。
(*2)
私の大学の履修登録が今年からようやくWebで登録できるようになったんですが、
そのシステムの使いにくいこと。使いにくいこと。
どこの誰がどれだけお金をもらって作ったのかは知りませんが、
いくら安くてもお金がMOTTAINAIと感じるような酷いシステムでした。
講義一つを登録するたびにページ遷移するな。
講義名で登録させてくれ。登録コードしか受け付けないってなんだよ。
講義の登録コードがPDFで配布ってなんだ。しかも全角文字で書くな。
正直紙に書いて登録していたときの方が楽だった気がしてきました……

(追記)
最初タイトルが「プログラミングは難易度は質によりけり」になってたwwww
テラハズカシスwwww

NScLisperでSHIORI

火曜日, 5月 13th, 2008

先に結論を書いておきます。
以下のものは全く役に立ちません(笑)

SWI-Prologのcoroutining

土曜日, 5月 10th, 2008

SWI-Prologのマニュアルを読んでいたら、面白そうな述語を見つけました。

6.2 Coroutining
Coroutining deals with having Prolog goals scheduled for execution as soon as some conditions is fulfilled. In Prolog the most commonly used conditions is the instantiation (binding) of a variable. Scheduling a goal to execute immediately after a variable is bound can be used to avoid instantiation errors for some built-in predicates (e.g. arithmetic), do work lazy, prevent the binding of a variable to a particular value, etc. Using freeze/2 for example we can define a variable can only be assigned an even number:

?- freeze(X, X mod 2 =:= 0), X = 3
No

( http://gollem.science.uva.nl/SWI-Prolog/Manual/coroutining.html )


freezeは第一引数に未束縛の変数を指定し、
第二引数に『第一引数の変数が束縛されたときに実行されるゴール』を指定します。
上の例ではXが3に束縛された時点で X mod 2 =:= 0 が実行されます。
とりあえず、使ってみました。

?- [user].
test(Y):- write(1), freeze(X, (write(a),Y is X + 1,write(b))), write(2), X = 3, write(3).
?- test(Y).
12ab3
Y = 4;
no

実行順序はこんな感じ。
1) write(1) が実行 (画面に1が出力)
2) freeze(X, (write(a),Y is X + 1,write(b))) が実行 (引数は評価されない)
3) write(2) が実行 (画面に2が出力)
4) X = 3 が実行
5) Xが束縛されるのをきっかけにfreezeの第二引数が実行される
5a) write(a) が実行 (画面にaが出力)
5b) Y is X + 1 が実行
5c) write(b) が実行 (画面にbが出力)
6) write(3) が実行 (画面に3が出力)
私自身はコルーチンについてはあまり知らないんですが、
これはむしろ変数の束縛をトリガとした遅延評価みたいに感じました。
Schemeのdelayがfreezeに対応して、forceが変数の束縛に対応する感じです。
他にもfrozenという述語があり、これは指定された変数をトリガとしている
freezeオブジェクト(?)を取得できます。

?- freeze(X, write(a)), frozen(X, Y).
X = _G397{freeze = user:write(a)},
Y = user:write(a) ;
No

使い道は現在はいまいち分かってません。
他にもcoroutining関連の述語はいくつかありますが、
それらはまたの機会にでも。
(条件を満たしたときに引数の実行を始めるwhenという述語があるんですが、
なぜか上手くいかなかったorz)