Hatena::ブログ(Diary)

西尾泰和のはてなダイアリー

2013-03-19

Javaでヒープ領域を余らせたままOutOfMemoryErrorを出す方法

先日、こんな問題を見かけたのだけども、JavaのGCにはあまり詳しくないので答えがわからなかった。

OutOfMemoryErrorが発生しました。(中略)ヒープメモリは足りているようです。原因として何が考えられますか?

http://d.hatena.ne.jp/iad_otomamay/20130318/1363596244

なんでだろうなぁと思っていたところid:moriyoshiが「Permanent領域があふれたんじゃないの」と一言。「Permanent領域」で検索してみると、なるほど、そういうことなのかー。

というわけで早速それを再現させるコードを書いてみた。ヒープの大部分ががら空きなのにPermanent領域だけ99%になっているのがわかるかと思う。

Exception in thread "main" [Full GC [Tenured: 515K->515K(56896K), 0.0106600 secs] 972K->515K(82560K), [Perm : 4095K->4026K(4096K)], 0.0107050 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
java.lang.OutOfMemoryError: PermGen space
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:791)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:634)
	at Test.loadNext(Test.java:135)
	at Test.main(Test.java:142)
Heap
 def new generation   total 25664K, used 914K [0x00000007bfc00000, 0x00000007c17d0000, 0x00000007d5150000)
  eden space 22848K,   4% used [0x00000007bfc00000, 0x00000007bfce4878, 0x00000007c1250000)
  from space 2816K,   0% used [0x00000007c1250000, 0x00000007c1250000, 0x00000007c1510000)
  to   space 2816K,   0% used [0x00000007c1510000, 0x00000007c1510000, 0x00000007c17d0000)
 tenured generation   total 56896K, used 515K [0x00000007d5150000, 0x00000007d88e0000, 0x00000007ffc00000)
   the space 56896K,   0% used [0x00000007d5150000, 0x00000007d51d0e30, 0x00000007d51d1000, 0x00000007d88e0000)
 compacting perm gen  total 4096K, used 4058K [0x00000007ffc00000, 0x0000000800000000, 0x0000000800000000)
   the space 4096K,  99% used [0x00000007ffc00000, 0x00000007ffff6bd8, 0x00000007ffff6c00, 0x0000000800000000)
No shared spaces configured.

コードは重要なところだけ抜粋するとこんな感じ。

public class Test extends ClassLoader{
    static int count = 0;
    Class loadNext() throws ClassNotFoundException {
        (中略)
        return defineClass(name, b, 0, b.length);
    }

    public static void main(String args[]) throws ClassNotFoundException{
        Test me = new Test();
        while(true){
            me.loadNext();
            count++;
        }
    }
}

つまりクラスを動的に生成しつづけるコードだ。先ほどのコンソール出力の例では手早く落とすために「-XX:PermSize=4m -XX:MaxPermSize=4m」を指定してPermanent領域を小さくしたので800個ちょいのクラスを定義した所で落ちる。その設定なしだと僕の環境では8万個のクラスを定義したあたりで落ちる。

ソースコードはこちら。なお、バイトコードを理解しないままコピペするのが嫌で、理解できるまで調査した結果大量のコメントが入っているけども、そこは今回のストーリー上ではあんまり重要じゃないので無視してもよいだろう。

https://github.com/nishio/learn_language/blob/master/oome/Test.java

2012-06-19

作りたいもの: プログラミング言語のコア概念を学ぶサイト その2

作りたいもの: プログラミング言語のコア概念を学ぶサイトの続き。

サイト自体や日本語での解説はまだ完成していないが部品が色々揃ってきた

完成したもの

  • JavaScriptのASTをリアルタイムにSVGで可視化(Esprimaを使う、見栄えに不満があるので改善の余地がある)
  • JavaScriptによるLISPの実装(パーサだけのはずだったのに関数定義、レキシカルスコープ、マクロ、までできちゃった)
  • JavaScriptによる中置記法の数式の再起下降パーサ(演算子の種類や優先度をブラウザ上で変更できる、これも予定以上の出来)
  • JavaScriptによるBrainf*ckの実装(IO周りに改善の余地がある)

現在進行中のもの

  • JavaScriptによるLazyKインタプリタ(SKI記法に限定してパーサ完成、可視化部分を実行部より先に作成中)
  • JavaScriptによるForthインタプリタ(あと数日で完成する予定)
  • JavaScriptによるEDSACエミュレータ(Python版の実装がバグって動かない状態。LazyK終わってから直す)

-----

で、次は何だ?というのを考えつつある。

前回の引き継ぎ

  • EDSACくらいの命令数で、アーキテクチャをもっとシンプルにした仮想マシン: どういう設計にする?opcode1バイト、アドレス部1バイト、文字セットはASCII、入出力は両方紙テープで1カラム8ビット、とか?
    • 2バイトワードを整数として足し算する命令と浮動小数点数として足し算する命令が別々にあっても良い(EDSACより進んでるけど) それがあれば「間違った命令を使ってひどい結果になる」がデモンストレーションできるから型情報の必要性が説明出来る
  • Pythonのコードをバイトコードに変換して表示するページ、ページ遷移なし・リアルタイムで動くこと。
  • PythonのコードをSTとASTにしてそれぞれ可視化するページ、ページ遷移なし・リアルタイムで動くこと。
  • 数式パーサの出力を使ってFORTHのコードを出力、というデモをやるとバイトコードを吐いてVMで走らせている系の言語の気持ちがわかるようになる?

GC

  • GCを教える上で必要なデモってなんだろう
    • new_obj()とnew_tuple(obj, obj)と変数への束縛とtupleの破壊的な書き換えだけできる言語があれば、とりあえず参照カウント方式で相互参照が出来て消せない状況をデモすることができる
    • それを繰り返していてヒープがいっぱいになったらマーク&スイープが走る、もデモすることができる
    • そういえばこんなものも昔作ったな… NarVisualizer ってメアドがnaist.jpのままだな、もはや著者にメールを送ることすらできない。誰だこれ作ったの(ぉ

並行処理

  • どのタイミングでどっちのスレッドを実行するか、を全パターン試してくれる処理系があれば、問題のあるコードとないコードをデモンストレーションできるかなーと思ったが10命令のスレッドが2つあると全パターンは18万だなぁ。さすがに無茶か。
  • じゃあせめて失敗する例を実際に動かして納得できるように、命令にグローバルな実行順序を付けられる処理系?
  • 一つのコードでいろんなパターンを実行したいと思うから、実行順序はコードとは別の方法で指定できなきゃいけない。

-----

とまあ、思いついたものを脳からダンプしておいたけども、とりあえずは焦らずに今進行中のものを完了させてスッキリした方が良いかもしれないな。そもそも肝心の解説用サイトがまったくできていないし。

2012-05-01

作りたいもの: プログラミング言語のコア概念を学ぶサイト

増井さんの作りたいものリストを作ろうというスライドを見て「確かに『いつかやる』リストに入れてるだけじゃ発展しないから、公開しても問題ないものは公開したらいいなぁ」と思ったので早速やってみました。とはいえ、僕の『いつかやる』リストは一つのリストの要素に色々な設計やアイデアが書かれていて、全部一度に載せると読むのも大変。とりあえず1つ目だけ載せてみます。動機なんかを加筆。


プログラミング言語のコア概念を学ぶサイト

日本語や英語で書かれた解説を読むより、実際に動かしてみたほうがわかりやすいこともある。


しかし、広く使われている言語は既に長年の改良が繰り返されていて、改築・増築を繰り返した老舗旅館みたいに複雑なものになってしまっている。


学習のために、小さくシンプルな、全体像を理解しやすい、わかりやすく可視化された「言語の実装」が必要だ。

そんなものはすでにあるのでは?

かつて色々な方法で(Javaアプレットで、Flashで、etc)、色々なアルゴリズムの可視化が試みられてきた。


適切な可視化手法は時代によって変わっていく。一方で、ある時代にアルゴリズムの可視化を実装した人が、みな5年後、10年後別の可視化手法への移植をするかというとそうではない。それがなぜなのかはわからない。可視化部分の習得コストが高いからか?


メンテナンスが継続されない問題はどうすれば解決できるか?コードが公開されていなかったり、されていてもライセンスが不明な点が問題の原因ではないか?であれば、GPLで公開すれば、将来の移植版も含めて再利用可能なライセンスであることを保証できるのではないか?


また、アルゴリズムの実装部分と可視化部分は疎結合であるべき。それによって移植性や再利用性が高まる。

適切なサイズ

ついつい機能追加をしてしまいがちだが、実装の読みやすさにも価値があることを考えると、最適なサイズは結構小さい。例えば中置記法のパーサの実装であれば「足し算と掛け算と括弧による優先順位の変更」だけ、とかが適切なサイズだと思う。

これ以上付け加える物が無くなった時でなく、これ以上取り去る物が無くなった時が完成だ - アントワーヌ・ド・サン=テグジュペリ

実装するものリスト

何でもありだと力が分散してしまう。まずは集中するべき。今回は「JavaScriptで実装されていて、インストールなしで、ブラウザ上で動作する」に集中する。

  • 構文木可視化部門
    • A: 構文木をSVGかcanvasで可視化する部品。汎用のツリー表示ライブラリが使えるかも。
    • シンプルな中置記法のコードをパースして構文木を作る部品。Aと結合して「ブラウザ上で1 * 2 + 3とか書くとリアルタイムで木がにょきにょき変形するのがわかる」というデモが出来る
    • 実用的に使われている言語の、構文木を作る部品とAとを結合させるアダプタ。JavaScriptのブラウザ上で動作するパーサは存在する http://esprima.org/demo/parse.html 後はこれの出力をAに送って可視化するだけ
    • LispのS式のパーサ。最低限数値と加算・乗算だけでよい。これもAに接続できるように
    • Forthのパーサ(って言うほどのものでもないか) Aに接続できるように
  • スタックマシン部門
    • B: 「命令列のどこを実行しているか」と「スタックの状態」をわかりやすく可視化する部品
    • D: Forthの実装 これはほぼスタックマシンの実装と同義。最低限数値と加算・乗算だけでよい。Bと結合して可視化されること
    • スタックマシンを使っている言語のバイトコードを実行 たとえばPythonであれば文字列として受け取ったソースコードバイトコードにコンパイルして返すHTTPサーバを書くことができるから、Pythonのバイトコードを実行するJSを書けばブラウザ上でステップ実行できる。Bと結合して可視化されること
    • Lispパーサの出力をForthにコンパイルしてDで動かす
  • 構文木書換え部門
    • C: 構文木を部分的に書き換えていくことで計算を行う。Aと結合して可視化されること
    • Lispのパーサからの出力をCにつないで実行
    • Lazy K実装。Aと結合して実行過程が可視化される。
  • 機械語部門
    • EDSACシミュレータ。シンプルな機械で機械語を学ぶために。ただし命令の読みにくさを保存する必要性はないのでわかりやすいエイリアスをつける。
    • Brainf*ck: ただし読みにくさを保存する必要はないので、わかりやすいエイリアスをつける、数値をstoreする命令を足す、などを行う。
    • 将来的には上記の機能限定されたForthやLispから機械語へのコンパイラを実装する。コンパイル過程を可視化する。
  • その他
    • オブジェクトシステムの実装
    • スコープの実装
    • ガベージコレクション (thanks id:nisemono_san)

2012-03-23

言語女子会3: Pythonが恋愛に悩んでRubyに相談しましたの巻

言語女子会: undefとnullは両方必要?言語女子会2: varは必要?/privateがない?の続編です。

Rubyに恋愛相談

Python: 最近悩んでるのよね…

Ruby: んー、何に?

Python: 自分はどんな人が好きなのかなぁ…とか…

Ruby: あー、そんなの簡単よ!一緒にいて楽しいことよ! *1

Python: そんなの誰とだって仲良くなったら楽しいんだから差別化にならないじゃん

Ruby: そうとは限らないわよ、たとえば、あっ…

C: ごめーん、会議が長引いちゃって遅れちゃった!(髪の毛ファサーっ)

Python: ああ…なるほど…


C

C: 何の話?え、恋愛に悩んでる?そんなの簡単よ。卓越性よ。

Python: 卓越性??

C: そうよ。なんらかの分野で「わたしが一番」という状況を作ることよ。そうすれば男の側からいくらでも寄ってくるわ。

Python: なるほど…ナンバーワンになることですか…

C: そうね、あとは胃袋をつかむことね。食事は毎日必要になるわ。毎日必要になる根幹をつかんでしまえば不動の地位が築けるわよ。

Python: それってOSの実装言語になるとかですか?

C: そういうこと。ラクでいいわよー、そういう状況の男って。だって私に従うしかないんだから。

Python: (なんかいかにもキャリアウーマンだなー)


JavaScript

Python: (そうは言うけどPythonでOSを実装するっていうわけにもいかないしなぁ、なんか別のプラットフォームを探すか…)

JavaScript: さっきのCさんのはなし、すごいおもしろかったねー☆

Python: あ、聞いてたの

JavaScript: やっぱプラットフォームを独占しちゃえばあっとーてきにつよいよね☆ (ブラウザを独占している余裕の笑み)

Python: (イラッ)

Ruby: (イラッ) ところで、あなたの妹さん、元気してるの? *2

JavaScript: んー、まあ元気なんじゃない?でもあの子、ペットのパトラッシュだかフラッシュだかと一緒じゃないと出歩けないからー

Ruby: 最近ペット不可の店多いよね。Apple Storeとかメトロとか。 *3

JavaScript: そうなのよー。店を追い出されたことにスネちゃって、最近は部屋でゲームばっかりやってるかもー。さいきんはFacebookとかで畑たがやしてるかなー。

Ruby: (なんかこのままだとActionScriptちゃん心を病みそうね…)

JavaScript: ま、わたしだってcanvas使えばゲームとか作れるしー、妹のモテ期はもう終わったんじゃない〜?


Java

Ruby: まあ、プラットフォームを独占できれば勝ち組ってのはわからなくもないけど、そうそう都合よく空いてるプラットフォームなんてないよねぇ

Python: そうだよねぇ

Java: (話に割り込んでくる) プラットフォームがなければ作ればいいのよ!

Python: あ、Javaさん…

Java: ヲラーー!! *4

Python: ヒィィィ怖い…

Java: まあアジテーションはさておき、Cさんがプラットフォームを独占してしまったからといって諦めるのは早いのよ。人間の作るものには必ず微妙な差があるわ。それを均質化することでプラットフォームはいくつでも生まれるのよ!

Python: 均質化…ですか

Java: そうよ、JSがでかい顔できてんのもjQueryなんかのクロスブラウザライブラリで均質化したからこそよ!あれもまた均質化によるプラットフォーム創出だわ!そしてその創始者はこの私!(ババーン)

Ruby: やっぱJavaさんとJavaScriptちゃんって仲がいいんですね!

Java: !! 誰があんな奴のことなんか…名前勝手にパクりやがって…ブツブツ…

Python: (Rubyちゃんヤバイよ、それ地雷だよ)

Java: アイツのせいで「ちょっとジャバとか書ける人いないかな?」って言われた場合、だいたいがJSのことじゃんか。*5 いい迷惑だよ!

Ruby: (ああー、そういうことなのかー)

Java: だいたいね、あんな「クラス?なにそれ美味しいの?ぽかーん」とか言ってるユルいやつと同一視されるとか!あいつに比べたらまだPHPの方がよっぽど可愛げがあるわ! *6

Python: え、そうなんですか。意外…。

Java: そうよ!あなたたち!男を従える上で一番重要なのは何だと思う?それは規律よ!

Ruby: 規律?!

Java: そうよ、男はみんな愚かなのよ。彼らの好きになんかさせたら失敗をするに決まっているわ。だからこそきっちりルールを決めて決まったレールの上を歩くように仕向けなきゃいけないのよ。まずはクラスを作れ!型宣言をしろ!多重継承は禁止!グローバル変数なんて不要だ!

Python: なるほど…あえて自由度を削ぐことで男を管理しやすくするわけですか…

Java: そうよ、わたしみたいにアッシー・メッシー・ミツグ君をどっさり抱えているとその管理だけで大変なのよ

Python: (うわー、この人「男はたくさんいた方がいい」なんてバブルな思想なんだ…)

Ruby: (こういう思想だから「頭数揃えて」とか「人月で」とか言っちゃうんだな…)


PHP

別の日…


Python: Javaさんの思想はやっぱり私にはしっくり来ないなぁ

Ruby: あなたprivate全開だしね

Python: (-_-#)

Ruby: でもJavaさんの話で意外だったのはPHPさんを評価してるっぽいところね

Python: そうね、わたしもてっきりPHPさんはJavaScriptちゃんみたいなユルい感じだと思ってたよ。

Ruby: いままであんまり話をしなかったけど、意外と参考になるかもよ。

Python: そうね。


Python: …というわけで恋愛についてアドバイスを貰えませんか?

PHP: あら…そうねぇ…重要なのは安心感を与えることじゃないかしら

Python: 安心感?

PHP: みんな不安なのよ。いきなり難しいことを要求したら逃げられちゃうわ。最初の1歩はとにかくやさしくなきゃ。

Python: なるほど。

PHP: 大学のサークル勧誘とかもそうでしょ。「このサークルは自分を受け入れてくれる」と感じさせることが重要よ。プログラミングでいうなら、最初に動くものができるまでの動線がとにかく短く、わかりやすくなきゃいけないのよ。

Python: そうかー、わかりやすさかー。わたしもわかりやすさは重要だと思う。

PHP: そうね。あとはCさんの言っていた卓越性も重要ね。すべてに卓越することは超人でなきゃできないわ。卓越と特化はセットなの。「自分の得意なドメイン」に特化して、そこでは1位でなければならないの。わたしの場合はそれが「ウェブアプリが動くまでの動線の短さ」だったわけよ。

Python: なるほど、分野を選ぶ、と。

PHP: すべてに卓越することは不可能なの。だからターゲットを絞ったらその他のことは捨てなければいけないわ。例えばあなた、文字列と数値を結合するとエラーになるわよね。でもそれって、数値と文字列を別のものだと理解することを強要する設計よね。

Python: えっ、そんなこと考えたこともなかった…

PHP: エラーにせずに結合した方がウェブアプリが動くまでの動線は短いわよね。数値と文字列は区別してもいいじゃないとあなたは思うのかもしれないけど、あなたはタプルとリストを区別したりとか、文字列とユニコード文字列を区別したりとかいろいろめんどくさい区別を強要してるんじゃない?

Python: うむむ、わたし的には「明示は暗黙より良い」って考えだから、暗黙の変換とかはあんまりしたくないんだけど…

PHP: それはあくまであなたの理想でしょ。それを求めている男性がどれだけいるの?

Python: 確かに…

PHP: 「こうした方が男性は喜ぶんだ」とか思い込んで理想を追いかけるのより、実際に男性が求めることを素早く手軽に提供するのが正しいのじゃない?

Haskell: …不潔にゅ…。


Haskell

Haskell: PHPの主張にはまったくこれっぽちも同意できないにゅ!理想を追いかけることこそ正しいにゅ!


Python: (ヒソヒソ Haskellちゃんって彼氏いたっけ?)

Ruby: (ヒソヒソ ほら彼氏にunsafePerformIOを許したことを後悔してるんじゃなかったっけ)

Python: (ヒソヒソ あ、そっか!)


Haskell: そもそも大勢にモテる必要があるのかにゅ!自分のことを理解してくれて、理想の実現を応援してくれるような一握りの仲間がいればそれで十分じゃないかにゅ!

Python: なるほど、それはたしかに一理あるかも。

Haskell: オンリーワンの独自性、ついてこれない奴はさっさと帰れって突き放し、これがセットになって初めて強い絆が生まれるにゅ!熱心なファンもできるにゅ!

Python: 突き放すことで熱心な人だけを残すということね

Haskell: おまえはキャズムを超えたんだにゅ。ファンが増えれば増えるほど熱心さの平均は下がるもんだにゅ。自明だにゅ。

Python: そうか…

Haskell: それなのにおまえは全部のファンにいい顔をしようとしているにゅ。八方美人だにゅ。そんなことだからどうしたらいいかわからないなんて寝言を言うにゅ。じぶんのやりたいことをやって、ついてこれない奴は捨てればいいにゅ!

Python: むむむ、なるほど…どうすればいいかな…

Haskell: さいきんPythonは関数の引数とか返り値をアノテートできるようにしたにゅ?そのまま型宣言を義務化したらいいにゅ。

Python: 型宣言か…

Haskell: あとオブジェクトは全部コルーチンにして、リストは全部ジェネレータにして、コンパイル時に型チェックした上でErlang VMのバイトコードを吐けばいいにゅ。

Python: えー、あのマーケットに参入するのはちょっと…

Haskell: Erlangファンの半分以上は文法にげんなりしているから「文法がキレイです!」って主張すれば略奪愛できるにゅ。その点に関しては確実にErlangよりイケてるにゅ。盛大に戦って潰しあえばいいにゅ。そのあとでわたしがParallel Haskellで参入するにゅ。

Python: なんだ自分のためなんじゃんー、まじめに聞いて損したー

Haskell: 当たり前だにゅ。誰が自分よりモテてる言語に真面目にアドバイスするかにゅ。お前はバカかにゅ。


まとめ

Python: というわけでいろんな人に話を聞いてきたんだけど、やっぱり自分がどうすればいいのかわかんない…

Ruby: あなたRailsみたいなキラーアプリを持ってないもんねぇ。あ、もってたっけ?なんだっけ?ソープ?

Python: Zope?まあZopeにしてもDjangoにしても、簡単ポンと管理画面付きのウェブアプリができるのは悪くないと思うんだけどねぇ。なんか世間的にはRailsの方が注目されちゃってるねぇ。

Ruby: まああなたはウェブアプリじゃないところに軸足を移したら?ほらシヴィライゼーションとかゲームに組み込まれてたりするじゃん?あっち方面は?

Python: んー、その方面はLuaちゃんとガチバトルになっちゃうかなぁ

Ruby: じゃ、あれは?BlenderとかInkscapeとかそういうアート系の。

Python: うーん…なんか不安定そうだし…

Ruby: NumPyやSciPyなんかの科学系のは?

Python: むずかしい数式見ると眠くなっちゃうのよねぇ…

Ruby: やっぱあんた一度ファンを失ったほうがいいんじゃない?(-_-#)

Python: えっ

反響まとめ

  • アセンブリばあちゃん「あたしらは生まれた時から添い遂げる男(チップ)が決まってたから恋愛なんて考えた事がなかったねえ」(by id:raitu thanks!)
  • COBOLおばちゃん「銀行とか金持ってる人を抑えとけばずっと食ってけるよ。頑張りな。」 (by @town_b thanks!)
  • デレ全開のPHPさんは徹底的なconfigureで自分色に染めてあげましょう。でもたまにVerUpで性格豹変して反撃してくるので注意。 (by @KentarouTakeda thanks!)
  • Luaちゃんとガチバトルする気満々(mruby-RiteVM)なRubyさんの「じゃあ私狙っちゃおうっかなー…」的反応を妄想 (by id:taninsw thanks!)
  • Vim scriptちゃん「プラットフォームを独占することは重要よね!」(by @thinca thanks!)
    • EmacsLispちゃん「えっ」
  • 個性的なのがHaskellしかいないのが物足りないので、C++とSmalltalkとlispが加わって欲しいです。 (by id:coolstyle thanks!)
  • Lisp女子会も若手のClojureが企画していたが… Scheme「無理よ!人数が多すぎるわよ!」 Common Lisp「プログラマーの数だけいるんだものね」Clojure「でも折角の機会ですし、私も先輩とお話ししてみたいし…」 Emacs Lisp「つっても、car/cdr/cons位しか通じねえべさ?」 Clojure「えっ?car?cdrって何です?」 Sc&CL&EL「これだから近頃の若い娘は…」(by @dowhileloop thanks!)
  • わりとオチ担当のPHPがまともにみえるwPythonは自然言語処理系でやたらと押されているよね。あと、Java姉さんを進化が止まったとディスるC#たんはまだですか? (by @kiichi543321 thanks!)
  • javaさんはphpさんをただ評価しているのでなく「こいつは劣化java」「物足りなくなれば自分に寄ってくる」とか思ってたりするかも (by id:akitsukada thanks!)
  • 「明示は暗黙より良い」って思想によりゆるふわモテカワになれるって発想は、ネイルケアで男にアピールに相当するくらい明後日の方向の戦略ってことですね (by id:dekaino thanks!)
    • Python: 「みんなもっとselfをさらけだしていけばいいのに…」
    • PHP: 「Pythonちゃん、あんまりあけっぴろげだと男性に引かれるわよ。男性は『アイドルはおならしない』とか思っちゃう生き物なのよ。」

あとがき

各種言語の違いを見る上で、やはり化粧やアクセサリよりも、男性観が重要ですよねー。ということで最近自分を見失いがちなPythonちゃんに恋愛相談をしてもらいました。


なおこの記事で言語の比較とかに興味を持った方には僕の書いた「言語設計の基礎知識」を含む60冊分のPDFが1冊にまとまった「Web+DB Press 総集編」がオススメです:

*1: Rubyの「Enjoy Programming」の考え方 まつもとゆきひろ氏が語る「言語の世界」の過去・現在・未来(2/2)

*2ActionScriptのこと。ECMAScriptも参照。

*3: iPhone, iPadなどを始めとする多くのモバイル端末、WindowsのMetro UIでFlashがサポート外

*4Write once, run anywhere

*5: 注:Javaさんの妄想であり統計情報に基づいたものではありません;p

*6: PHPがJava風の「単一継承のクラスと多重継承のインターフェイス」を採用している件

2011-08-19

セプキャン2011感想文

みんながバイナリ*1とかバイナリ*2とかUCAS*3とかで書いて読むのに苦労したから、僕はちゃんとASCIIで書きましたよ!

https://gist.github.com/1156669

言語仕様

1文字1トークンで、単純に並べるだけで関数呼び出しになる。たとえば「XYZ」というコードがあった場合、これはPythonでいうところの「X(Y)(Z)」に相当する。つまり「関数Xに引数Yを渡して呼び出し、その返り値の関数に引数Zを渡して呼び出す」という意味だ。

括弧()は特別な意味を持っていて、まあみんなの期待通り処理をまとめるのに使われる。評価は内側の括弧から順番。たとえば「X(YZ)」というコードがあった場合、これはPythonでいうところの「X(Y(Z))」に相当する。つまり「関数Yに引数Zを渡して呼び出し、その返り値を引数として関数Xを呼び出す」という意味だ。あと一番内側が複数個ある場合は一番出現が早いものが評価される。「( (01)(02)(03) )」だと01,02,03の順に実行されるってことね。(追記:「一番内側」という言葉が「一番カッコの深いところ」と誤解されうるのでもう一言書いておくと「( (01) (0(02)) (03) )」は01, 02, 0(02), 03の順に実行される。)

組み込み関数がU, 0, 1の3つ。あと名前付けのための特殊形式+がある。

一番独特なのは数値が全て関数でもある点で、整数オブジェクトはその数だけ増やす関数としても扱える。1は整数の1であると同時に関数でもある。たとえば「11」は「1という関数に引数1を渡したもの」になるので2になる。ただし、0だけは0増やす関数になっても面白く無いので引数を出力する関数にした。返り値は引数に0を足したものになる。先ほどの例にあった「( (01)(02)(03) )」は1,2,3の順に出力し、その後(123)を評価して6を返す。

あと特殊形式+は値を名前に束縛する。「+X(11)」とやると、(11)が整数の2になってからそれをXという名前に束縛する。だからそれ以降「XX」とかやれば関数2に数値2を入れたことになって4が返る。評価の順番に注意。意外に思うかも知れないが「X(+X(11))」は最初のXが評価されるのより+Xで束縛される方が先なのでエラーにならずに実行される。なお(+XY)の返り値はYである。

もう一度例を挙げると、「(0(1(01)))」はまず(01)が評価されて1が出力され、(0(11))の(11)が評価されて2になり、最後に(02)が実行されて2が出力される

組み込み関数Uの仕様は http://en.wikipedia.org/wiki/Iota_and_Jot を参照。

言語の名前はspcampでバージョン番号が2011.0.0。

他になにか言うことあったかなぁ。不明な点があれば気兼ねなくコメントしてください。

ソースコードにSとかKとかIが出てきているのはかなり親切に書いたつもり。全部Uで書いてもよかったんだけど本質的じゃない部分で読みづらいし。特殊形式の+とかもSKIをUから定義してコードをコンパクトにするために作ったわけだし。S,K,Iについて良い日本語の記事がどこにあるかわからなかったので SKI combinator: http://en.wikipedia.org/wiki/SKI_combinator_calculus を参照。(追記:Uの仕様も含めて404 Blog Not Found:Math - 言語はどこまで小さくなれるか - (unlambda|iota|jot) のすすめがよくまとまっている)

1文字1トークンだからレキサーいらないし、構文も括弧で木構造なだけだからパーサも簡単。処理系はPythonで書いてて、doctestでテストケースたくさん書いて300行程度だからテスト削ったら200行くらいかなぁ。dis.disでバイトコードにディスアセンブルして562行くらい。SimpleHTTPServerよりちょこっと小さい。

追記: 12:03時点でメールで1通の正解が送られて来ました。