みねこあ

mineko. A! ―from mi-neko online.

2010-02-03

[]Logo基礎文法最速マスター :発動編 22:10 Logo基礎文法最速マスター :発動編を含むブックマーク

発動編は、実践編です。接触編がかなりマニアックだったので、発動編は 普通に最速マスターを目指しました。

他の言語をある程度知っている人はこれを読めば Logo の基礎をマスターして Logo を書くことができるようになります。弄りながら遊んでくださいまし。

本エントリーは MSW Logo で動作確認しています。Lisp同様 Logo は方言が激しいので、他の処理系では動かなかったりするかもです。そこらへんはご容赦。

コメント

コメントは Lisp同様 セミコロン「;」で一行コメントになります。

;この行はコメント
print "hello ;ここ以降も行末までコメント

語(= シンボル)

Logo のプログラムを構成する最小の要素は、 語(word) と リスト(list) です。 語は いわゆるシンボル(シンボルに馴染みのないかたは文字列リテラルみたいなものと思いねぇ)、リストは [] で囲まれた空白区切りの 配列です。

語は

  • 「語」をそのまま書いたときは そう言う名前の関数が実行される
  • 「"語」とクォートしたときは、シンボルそのものを差す(文字列リテラルのように扱う)
  • 「:語」と書いた場合は、そう言う名前の変数に束縛された値を取得する

と言う風に使います。以下は未定義の hoge に対してそれぞれをやってみたモノ

>>hoge
I don't know how  to hoge   ;hoge なんて関数しらないよ!

>>"hoge
You don't say what to do with hoge  ;この「hoge」って値、どうしたらいいの?

>>:hoge
hoge has no value   ; hoge は値を持ってません!

あと、数値語(数字文字列)だけはちょっと特殊で、「語」をそのまま書いたときも「クォートされている」という 「暗黙のクォート」という特別ルールがあります。

>>42
You don't say what to do with 42 

反応が "hoge の時と一緒でしょ?数字だけの関数名は無いので、クォートしなくても数値リテラルみたいに扱えるの特別ルールは便利です。

手続き(= 関数)

ここまで「関数」と言ってしまったモノだけれど、Logo ではホントは「手続き」と呼びます。正確には、戻り値のある関数を オペレーション(operation)、戻り値の無い関数を コマンド(command) 、コマンドとオペレーションを総称して「手続き」です。

評価結果が 他の手続きの引数となりうるのはオペレーションだけで、トップレベルに書けるのは 厳密には コマンドだけです(警告されるだけですけれど)。良くあるプログラミング言語の「式」と「文」の関係ですね。

実はLogoのプログラムでトップレベルに書けるのはコマンド(つまり「文」)だけなので、オペレーションや値(=「式」)を書いちゃうと 怒られます(さっきから出てくる警告「You don't say what to do with 〜」 は、式をトップレベルで実行しちゃったときに出る警告文です)。

Logo は、原則的に全てが 手続き として実装されています。普通のプログラミング言語では「構文」で実現するよなもの――変数の生成や、条件分岐構文、反復構文 みたいなのものまで 手続き なんです。すごいでしょう。

Logo の手続き呼出は、引数リストの扱いがちょっと独特で、

>>sum 1 2
3

みたいにカッコで括ったりしません。これは sum という手続きの呼出ですが、 sum は 2つの引数を取る という定義から、どこまでが引数なのかをLogo処理系が推論しています。だから、

>>print sum 1 2     ; print(sum(1, 2)); と解釈
3

>>print 42 sum 1 2  ; print(42); sum(1,2); と解釈
42
You don't say what to do with 3

みたいに動きます。

リスト

リストは、データの配列を表現したり、空白混じり文字列を表現したり、プログラムを表現したりに使います。Lispのリスト由来なのでそちらをご存じならば理解は早いです(Lisp の リスト操作との対応は 接触編をご覧ください)。

>>[1 2 3 4]
You don't say what to do with [1 2 3 4]

リストをプログラムとして実行するには、run手続き を使います。

>>run [print [hello world]]
hello world

標準入出力

標準出力には print手続き を使います。クォート語をそのまま文字列として出力します。

>>print "hogehoge
hogehoge

ただ、クォート語は空白で切れてしまうので、空白を含む文字列の場合は リストを使います(Logo には C系言語の"hoge piyo" に相当する 文字列リテラル は 残念ながらありません)。

>>print [hoge hoge piyo piyo]
hoge hoge piyo piyo

標準入力は readchar または readlist を使います。readchar は一文字、readlist は複数文字の入力を受け付けます。

>>readlist

MSW Log の場合、このようなインプットボックスが開き、

f:id:minekoa:20100203211740p:image

You don't say waht to do with [abc def gh i]

のように入力されます。


手続きの定義

手続きの定義は to 〜 end を使います。

to increment :arg
    output :arg + 1
end

「output」 は Smalltalk でいうところの「^」、C系言語での「return」です。

>>print increment 42
43

余談ですが、to 〜 end は、Logo の構文からするとかなりイレギュラーで、文法?と首をかしげるところもあります。to手続き(最初の一行)を実行すると、以降を 対話型プログラミングで聞いてくる形で実装されていたり(まるで VisualStudio とかのウィザードのような扱いです)など、「ちょっと浮いた」感触の実装が多いです。


変数の定義・代入

変数に値を代入する場合は make 手続き を使います。

>>make "hoge 42
>>print :hoge
42

hoge をクォートして書かなくちゃいけないのは、これが手続き(関数)だから。引数で「こういう名前の」と渡すので、手続きや変数として評価されちゃまずいというわけ。

makeの実行時に hoge という変数がまだ存在しなければ、make はグローバルスコープに変数を作ります。

to fooVarMake
  make "foo 42
end
>>print :foo
foo has no value

>>fooVarMake
>>print :foo
42

こんな感じ。ローカル変数を作るには localmake 手続きを使います。

to barVarLMake
  localmake "bar 50
end
>>print :bar
bar has no value

>>barVarLMake
>>print :bar
bar has no value

ただし、Logo の変数は ダイナミックスコープであることに注意してください。ダイナミックスコープとは、ソースコードの構文のネストではなく、関数の呼出スタックのネストがスコープとなります。

to printBuz
   print :buz
end

to executePrintBuz :msg
    localmake "buz :msg
    printBuz
end
>>print :buz
buz has no value

>>printBuz
buz has no value  in printBuz
[print :buz]

>>executePrintBuz "hello
hello

>>print :buz
buz has no value

printBuz を executePrintBuz からコールしたときだけ、executePrintBuz のローカル変数 buz が見えているわけですね。

条件分岐・反復

Logo は Lisp なので、制御構造だって 手続きとして好きに作ることが出来ます。ここで紹介する制御構造も Logo の手続きの一種に過ぎません。

まずは、if と ifelse 手続き。

>>make "hoge 3
>> if :hoge > 1 [rint "true]
true

>> ifelse :hoge < 1 [print "true] [print "false]
false

反復手続き repeat と while。

>>repeat 3 ["hello]
hello
hello
hello

>>make :x = 5
>>while [x > 0] [print "hello make "x :x - 1]
hello
hello
hello
hello
hello

タートルグラフィックス

見てきたとおり、Logo は 汎用スクリプト言語なのですが、そうは思われなくなってしまったのは「タートルグラフィックス」があまりに印象深いからです。

「言語」という目で見ると「基礎文法?」と思うのですが、タートルグラフィックスがないLogoは、まるで Tk のない Tcl のようなもの、これが無くっちゃ始まりません・・・ということで。


* * *


タートルグラフィックは、ペンを持ったドーム状のロボット「タートル」をコンピュータから制御する「物体タートル(床タートル)」と、それを ブラウン管上にシミュレートする 「画像(光線のタートル)」の二つがあるのですが、後者ばっかりが有名になって、なんだかお絵かきソフトのように誤解されている感じもあります。

パパート先生ンち(?) にあった 物体の「タートル」は、複数台制御することもでき、物体だけにブルドーザーのような遊び方も出来たそうで、これは凄く愉しそうです。走らせて遊んでも良し、床にお絵かきさせても良し。あぁ、羨ましいな、こんな素敵体験がしてみたいものです。

f:id:minekoa:20100203212642j:image


f:id:minekoa:20100203212704j:image

LOGO 1968 -BBN Timeline

タートルは、基本的に forward(前進)、back(後退)、left(左旋回)、right(右旋回)と pendown(ペンを下ろす)、penup(ペンを上げる)という動作しかできません。画面系のコマンドとして、clean(線を消す)、clearscreen(線を消してタートルを初期位置(画面中央)に戻す があります。

これらコマンドは短縮名があって、移動系が fd、bk、lt、rt、ペン操作系が pd、pu、clearscrean が cs になります。

>>fd 100 rt 120 fd 100 rt 120 fd 100
>>pu fd 100 pd
>>fd 100 rt 120 fd 100 rt 120 fd 100
>>pu fd 100 pd
>>fd 100 rt 120 fd 100 rt 120 fd 100

f:id:minekoa:20100203213316p:image


left(lt) right(rt) の引数は 角度です。forward(fd), back(bk) の引数は 移動単位で、普通はスクリーンのドットに対応します。超簡単です。

さて、タートルは前進後退と旋回のみが出来ますが、このような自分の姿勢を中心とした 幾何学を、 パパート先生は「タートル幾何学」と呼びました。

座標系で考える「普通(に教えられている)」のやりかたに比べ、タートル幾何学は自身に置き換えて考えられる(=イメージできる)ことで、容易に学習できます。普通の幾何学では 図形を書く方程式に誤りがあった場合、どこが悪かったのかを検証するのは大変敷居が高いのですが、タートル幾何学の場合、「タートルになったつもりで」コマンドをなぞる「タートルごっこ」をすれば簡単にバグが特定できます。良く例にあがるのが、

fd 100 rt 60 fd 100 rt 60 fd 100

が三角形にならない、というバグ。

f:id:minekoa:20100203213347p:image


「三角形の内積の和は180°」という知識につられて、つい、こう書いてしまいがちです。

ここら辺の話は 目から鱗なエピソードが満載の マインドストーム が大変おすすめ。このエントリーを読んでしまうような コンピュータとプログラミングが好きな方なら 是非是非 読んでみてください。絶対損はしませんですよ!

タートル幾何学といえば、ネタですが、先日発売されたレンタルマギカ の第三部初巻で、二部の最後で力を大幅に失ったいつき君が、自己の負荷を軽減する新しい方法として、戦闘指示を 自分を中心とした座標に切り替える描写がありましたが、「タートルだぁ」と思ってしまいました。

それにしてもレンタルマギカ、第二部のアニメ化はしないのでしょうか。オルトくんとかツェ(ryさん とかの新キャラや、影崎さんの驚天動地の魔法を、是非是非 動画で見たいです。閑話休題。

さて、そうはいいつつタートルに任意の座標に移動させたり、現在の座標を取得したりするコマンドもあります。*1

>>setpos [20 30]  ;タートルの位置を (20, 30) にセット
>>setx 50         ;タートルのX座標を 50 にセット
>>sety 60         ;タートルのX座標を 60 にセット
>>pos             ;タートルの座標を取得
You don't say what to do with [50 60]

>>setheading 90   ;タートルの向きを角度で設定
>>heading         ;タートルの向きを取得(角度)
You don't say what to do with 90

>>towards [10 20] ;タートルが引数座標に向いたときの角度
You don't say what to do with 233.130102354156

これらは、タートルグラフィック的にはちょっと邪道です。


* * *


以上でみなさんも、


f:id:minekoa:20100204022737p:image


みたいなのが 書けるようになっているハズです、・・・たぶん。

*1:タートルグラフィックスの場合、座標の原点が画面中央(タートルの初期位置)であることに注意してください

バッフクランめバッフクランめ 2010/02/05 00:01 発動篇 (Be Invoked)はメソッド呼び出しで、無限の可能性が開けるんですね、分かります。
ちなみに名前の由来はこの画像が「I,D,E,O,Nを重ねたように見えるから」だそうな。

minekoaminekoa 2010/02/05 21:05 惑星だって真っ二つです。最後はみんなガベージコレクトしちゃいます。

あぁっ、今頃「篇」の字が間違いなのにが気がつきました。もう手遅れ...orz

イデの論理イデの論理 2010/02/11 06:07 // すごくいい加減な擬似コード
while(1){
temp1 = new 知的生物();// 例:地球人
temp2 = new 知的生物();// 例:バッフクラン

if( temp1.equals(成功例) && temp2.equals(成功例) ){
ide.invoke(); // 良き生命体によってのみ発動する。
}
else{ // 汚物は消毒だ〜〜!
temp1.kill();
temp2.kill();
}
}

ただし、一回 newするのに数十億年かかります。w

minekoaminekoa 2010/02/12 01:27 例外でも起きないとループから脱出できないのが、また...(^^;