5.

ルールを作るものと夢を生きるもの

[o]

 

Through space and time... in his bell jar... on a mission to find himself...

彼は自らの手になる釣鐘型の船に乗り、時空を旅している。自己を見出すために。真実を見出すために。

その男の名は、ドクター・N・ハロルド・チャム

はっきり言って、ドクター・チャムが狂人だという話を聞くのにはうんざりしている。確かに彼は自分で生き埋めになろうとした。確かに彼は姪を感電死させた。確かに、 そう、彼は養老院を爆破した。しかしそれにはすべてしかるべき理由があったのだ。私は彼が正しい選択をしたのだと信じている。

あなたは世間の人と同じ考えだろうと思うが、もし彼にRubyのクラス定義のすべてについて教わる機会があったなら、彼に対してちょっと尊敬の念を抱くようになるだろう。ミックスインについて学んだならなおのことだ。そしておそらくこの章 が終わる頃には、私たちは皆、ドクターの痛ましい過去にとらわれずに彼を見ることができるようになり、もう狂人と呼ぶのはやめるだろう。

もしそれでも彼を狂人と呼ぶ必要があるというなら、私なら線路まで下りて行ってどこかの長い蛍光灯を砕く。すぐ憂さを晴らすといい。私たちが取りかかる前に。

1. これは権利を奪われた人のため

Some people still can't get past what he did.

「私の祖母がいた療養施設は1940年代に ドクター・チャムに吹き飛ばされたんだ。なんて惨劇だろう」

「みんなあいつをグラニーボマーと呼んでたよ。まったく恐ろしい男だ。あいつが私たちを爆破しやしないかと思うと、今でも死ぬほど恐ろしい」

「だけど今度はこっちだって準備してるよ。うちの療養施設にはちょっとした防衛予算があるんだ。毎日午後4時の塹壕掘りだって手伝ってる! それに私たちにゃハーコート将軍っ人がついてるんだ!

ドクター・チャムの人生の中の年を1つあげてもらえたら、その時期における彼の概要を教えてあげるよ。私はそれをRubyのメソッドを使ってやる。これは独立した一片の孤立したコードの塊で、ロボット火山の声にだってつなぐことができる。そんななものが権威ある声優に最高の仕事になるときには。


OK、ここではdefcasewhenに注意してもらう必要がある。範囲は3章で見ているよね。1895..1913という閉じたアコーディオンだ。これは両方の端とその間の値を含んでいる。それから行の終わりのバックスラッシュは行末のEnterキーを無視し、この行にはまだ続きがあるのだととRubyに請け合っている。

ではdefcasewhenに気をつけて。

 def dr_chams_timeline( year )
   case year
   when 1894
     "生まれる。"
   when 1895..1913
     "ミシシッピ州ウィンストン郡のルースビルで子供時代を過ごす。" 
   when 1914..1919
     "ペカンの園芸店で働く。クエーカー教徒を殴る。" 
   when 1920..1928
     "ミシシッピ川を旅しながら考え深い自己改革に従事するウィズダミン川協会に入る。その\
      ウニバーシティにおいて140単位を修了する。" 
   when 1929
     "時間旅行するキジ狩人の小説を書くため、ルースビルに戻る。" 
   when 1930..1933
     "ペカン園芸の保険の仕事で相当なキャリアを積む。経済的に安定し、ブラジルとニューメ\
      キシコで過ごし、珍しい薄い殻のペカンの木を買い占める。彼の名声は高まるばかりだっ\
      た。それから、なんと彼は自分を生き埋めにする。" 
   when 1934
     "小説執筆に戻る。狩人を保険業界の大物に、キジをクエーカー教徒に変える。" 
   when 1935..1940
     "ウィズダミン川協会会長のアーサー・コーンを客として家に招く。5年間一緒に工学と投資\
      を行う。" 
   when 1941
     "ことが面白くなるのはここからだ。" 
   end
 end

まずdef キーワード。これは私たちがする最初のメソッド定義だ。def はただのカーネルメソッドで、Rubyのどこでも使うことができる。さて、これはどうやって実行すればいいのか?

 puts dr_chams_timeline( 1941 )

これは"ことが面白くなるのはここからだ。"という答えを返す。毎回同じ話だ。退屈なら自分の答えを使うといい。上ではcase の答えが必ず文字列になるようにしている。そしてcase 文はメソッドの最後の(そして唯一の)文になっているので、メソッドはその文字列を返すことになる。流れる水は、岩棚から岩棚へとこぼれ落ちる。

case 文について説明しよう。実際私はこれをcase..when文と呼ぶべきだろう。casewhenを別々に使うことはできないからだ。caseキーワードには値がくっついていて、それがwhenキーワードのそれぞれにくっついている値と比較される。最初にマッチした値がcaseに使われ、残りは無視される。同じことをたくさんのif..elsifを使ってやることもできるが、そうすると長くなる。

 case year
 when 1894
   "生まれる。"
 when 1895..1913
   "ミシシッピ州ウィンストン郡のルースビルで子供時代を過ごす。" 
 else
   "この年については記録がない。" 
 end

これは次のように書いても同じだ。

 if 1894 === year
   "生まれる。"
 elsif 1895..1913 === year
   "ミシシッピ州ウィンストン郡のルースビルで子供時代を過ごす。" 
 else
   "この年については記録がない。" 
 end

三重の等号は長く伸びたベルベットのロープで、二重の等号と同様に値をチェックしている。ただ三重の等号の方がロープが長く、真ん中の辺でたるんでいる。あまりきっちりとはしておらず、少しばかり柔軟性がある。

上の例の範囲を見てみよう。(1895..1913)というのは1905と全然等しくはない。範囲(1895..1913)が本当に等しくなる相手は範囲(1895..1913)だけだ。三重の等号は、範囲に対しては隙間を作って整数1905 を入れてくれる。この整数は範囲と等しくはないが、範囲が表す整数の集合に含まれている。場合によってはそれで十分なこともあり、たとえば上で作った年表がそうだ。

これは実際年表のように見えるよね? もちろんdr_chams_timelineメソッドはコードなわけだけど、年表みたいに読むことができ、すっきりしていて愛らしい。

What research revealed.

ドクター・チャムに虐殺された高齢者の中に私の祖母のスーザン・レイ・マッグウィンもいました。私はそれ以来入念な調査をし、私の祖母が死に値する憎むべき女性であったことを知るにいたりました。

私のノートには、祖母の姿を変えるものへの悪魔崇拝、潜水艦のハイジャック、骸骨の誘惑、子どもたちの上に基礎を作って家を建てること・・・まったく病的なことが次から次へと並んでいます。

私はドクター・チャムが彼らを殺してくれたことに本当に感謝しています。あのホームでは巨大なロボットアライグマを建造していたのです。アライグマは人に噛みつくんです!

しかし彼は病気だったのか??

彼はタイミングが悪かったのだ。小説家としては散々だったが、錬金術への挑戦は見込みがあるように見えた。彼はヤギの乳と海塩から脚の痛みを取る万能薬 を作った。ある男はなくした指が1インチ伸びてきたほどだった。足のような臭いがするが暗闇でも目が見えるようになる有機健康煙を作った。水梯子と呼ばれるものにも取り組んでいたが、私は現物を見たことがないし、それについて書かれたものも読んだことがない。登るためのものではありえないだろう。わからないけど。

地元の新聞社が実際ドクター・チャムの元を訪れたことがある。彼らの書評家が彼に4つ星の評価をしたのだ。本当に。その書評家は彼についての記事を書き、そして彼を評価したのだ。

ドクター・N・ハロルド・チャムが姪のことですごく辛く感じていたことは知っておいてほしい。彼はそのショック療法は効果があると思ったのだ。どのみち彼女はポリオのために死んでいたはずだ。彼はチャンスに賭けたのだ。

1941年9月9日、彼は自分の私有する手術室で、ハンナをフェナセチンで鎮静させた後、鼻と舌とつま先と肘に伝導クリップをつけた。弟子であるマービン・ホリオークという名のそばかすの大学生に手伝わせ、彼 が最高傑作(opus magnum)と呼ぶ物質の粉をハンナに振りまいた。粉末のホワイトゴールドが流れを運び、彼女にエネルギーを与え、血を活性化 させ、戦い、打ち破れるようにするのだ。

しかしそれがどんなに失敗したことか。スイッチが入れられたとき、彼女はエビ反りになって足をばたつかせ——カブラム!——それからブロイ オイ オイ クポイ!と叫んだ。彼女の巻き毛に光の壁、そして死の鐘が鳴った。実験は立ち上がるものすごい煙とともに失敗し、彼女の無邪気さは部屋の床と彼らの胸に大きな穴をあけた(何週間も、みんな「あの子にはチャンスがなかったんだ・・・」と口にしていた)。

ハンナのために、私はコードを書こう。

 opus_magnum = true  # 最高傑作 = true
 def save_hannah     # ハンナを救う
   success = opus_magnum
 end

メソッドは自分の島を持っている。そしてメソッドの中で起こることは、まわりにあるただの変数には影響を受けない。ドクター・チャムは姪の病気を打ち破ることができなかった。それはopus_magnum変数がメソッドの鋼鉄の外壁に入り込むことができないようなものだった。

save_hannahメソッドを実行すると、Rubyはopus_magnumが見あたらないと文句を言うだろう。

私はスコープについて話している。顕微鏡(マイクロスコープ)は視野を狭めて拡大する。望遠鏡(テレスコープ)は視野の範囲を押し広げる。Rubyではスコープはメソッドやブロックの中の視野を表している。

メソッドのdef ステートメントは視野を開く。そこで導入される変数名はメソッドの中では見ることができ、意味を持ち続けるが、endで目が閉じられる。メソッドに引数でデータを渡 したり、メソッドからデータを返すことはできるが、メソッドの中で使われている名前はそのスコープ内でしか効果を持たない。

ある種の変数はより広いスコープを持っている。お金の記号で始まる$LOAD_PATHのようなグローバル変数は、どのスコープでも使うことができる。アットマークで始まる@namesのようなインスタンス変数は、クラススコープの中でならどこでも使える。同じことは@@ticketsのようなクラス変数についても言える。クラス変数とインスタンス変数についてはもう少しあとで説明する。

ブロックもスコープを持つが、すこし曖昧で、もっと柔軟性がある。

 verb = '救った'
 ['鎮静剤を与えた', '振りかけた', '感電死させた'].
 each do |verb|
   puts "ドクター・チャムは姪のハンナに" + verb  
 end
 puts "そう、ドクター・チャムは姪のハンナを" + verb 

ブロックはドクターのそれぞれの行動をイテレート(循環)する。verb変数はそれぞれの回ごとに値が変わる。ある回では鎮静剤を与えている。その次では粉を振りかけている。それから感電死させる。

疑問は、ブロックが終わったらどうなるのかということだ。ドクターはハンナを救えたのだろうか?

 ドクター・チャムは姪のハンナに鎮静剤を与えた
 ドクター・チャムは姪のハンナに振りかけた
 ドクター・チャムは姪のハンナに感電死させた
 そう、ドクター・チャムは姪のハンナを感電死させた

ブロックは周辺にある変数を見ることができる。ブロックはverb変数が存在することに気付き、その内容を書き換えたのだ。ブロックが終了すると、ブロックの短い命は終わるが、verb変数は別な生き物として現れる。

ブロックが前に使われていない変数を使う場合には、変数はブロックの終わりとともに消滅する。ブロックのスコープは閉じられ、変数はそれとともに消えてしまう。verbがブロックの前では使われてなかったとしよう。

 ['鎮静剤を与えた', '振りかけた', '感電死させた'].
 each do |verb|
   puts "ドクター・チャムは姪のハンナに" + verb 
 end
 puts "そう、ドクター・チャムは姪のハンナを" + verb 

これはundefined local variable or method `verb' ('verb'は未定義のローカル変数ないしはメソッドです)というエラーになる。

まだ糊のきいた刺繍のあるドレスを着ているが、口の隅が暗い紫色になった少女の死体を運び出すというのは、優れた科学者にとっても難しいことだったのに違いない。ドクター・チャムの日記には、 輝く金と焦げたレースを身にまとったハンナの幽霊に苛まれたことが書かれている。妄想は次第に大きくなり、彼は地獄の犬と、復讐に燃える天使の大きな手に追いかけられた。

何週間もたたぬうちに、彼は去った。この悔恨から逃れ、この惑星から彼を飛び立たせる爆発とともに消えた。

そしてあなたが今これを読んでいるにしても、この瞬間にどこかで、孤独なドクター・チャムは釣鐘型の乗り物に乗り、60年の旅の後に遠く離れた惑星に降り立っている。新しい世界が視野に入り、近づくにつれ惑星の地平が広がり、 釣鐘が怒る天を駆け抜け、オーロラと太陽風を引き裂くのとともに、ドクター・チャムの目は揺り覚まされる。

Safe landing.  Amazement.

 

 

美しい

あなたが目撃しているのは、ドクター・チャムの惑星エンダートロムへの着陸だ。私にわかる限りで言うと、彼はこの惑星でほとんど何も起こらない荒涼とした季節に降り立った。ほとんどの住人は心をぼんやりしたざわめきの中に閉じこめ、一部の知恵と三部の靄からなる退屈な幽霊へと分解し ていた。

エンダートロムの歴史と天候についての私の知識は、私の娘のオルガン教師との付き合いから得たものだ。彼はこの惑星で育ったのだ。

Dead husbands could destroy the Doctor.

「お年寄りの人たちの中にはドクター・チャムがまだ宇宙を飛び回っていると思っている人もいます。なんてたわごとかしら!」

「もし悪いグラニーボマーが宇宙にいるのなら、私の夫のチャールズ——1997年に亡くなりました——彼の魂の安らかならんことを——彼が今では宇宙にいて、レーザーをぶちかまし、彗星の尻尾に乗っている わ」

「ドクターがチャーリーに勝てる見込みはないわね。チャーリーが彼にとどめを刺すでしょう・・・ドクターがチャーリーの欲しがるテクノロジーを持っていれば別だけど。その場合はチャーリーはドクターと同盟を組んで、爆破事件がもっと起きることになるわね」

私の娘のオルガン教師は、ちゃんと約束を守るように繰り返し言ってやる必要があった。彼は変な時間に訪問することがあり、緊急の電話にも即座に出た。彼が最後に明かしたのは、彼がエイリアンで、起きている時間が540時間続くということだった。私は彼にすごく元気づけられ、2060年まで続くことになる契約関係を彼と結んだ。

ドクター・チャムは(彼の懐中時計で)3日間暗い空気の柱の中を旅し、不毛な惑星のほこりっぽい風を吸った。しかし3日目に、荒涼とした季節が終わり、野生のリンゴの花と、霞んだ城に飾られた見事な光景が拡がるのを、彼は目の当たりにした。

2. コンピュータのある城

The panoramic vales of Sedna on Endertromb.

「これこそ城の未来だ!」

恐れを知らない我らがドクターは異星の城へと向かい、花の間を走り抜けた。地面は彼の踵の下を流れ過ぎた。城が地平線から徐々に姿を現してきた。彼は馬があればと思ったが、馬は現れなかった。そ れで彼はその惑星が心を読んで望みをかなえてはくれないのだと悟った。

私の娘のオルガン教師の説明によれば、その惑星は心を読むことができ望みをかなえることができるということだった。ただし同時にではなかったのだ。

ある日私がそのオルガンの名人を質問攻めにしていると、彼はチーズ色をしたレポートパッドに次のようなRubyのコードを書いた。(そして奇妙なチーズの臭いがどこからともなく漂ってきたが、それがどこなのかわからなかった。)

 require 'endertromb'
 class WishMaker
   def initialize
     @energy = rand( 6 )
   end
   def grant( wish )   # 望みをかなえる
     if wish.length > 10 or wish.include? ' '
       raise ArgumentError, "間違った望みです。" 
     end
     if @energy.zero?
       raise Exception, "エネルギーが残っていません。" 
     end
     @energy -= 1
     Endertromb::make( wish )
   end
 end

これはウィッシュメーカーだ。

正確にはそうでなく、ウィッシュメーカーの定義だ。これはRubyのクラス定義になっている。このコードはあるオブジェクトがどのように振る舞うかを記述 している。

毎朝、ウィッシュメーカーは最大5つのかなえられる望みで活動をはじめる。太陽が昇るときに新しいWishMaker が作られるのだ。

 todays_wishes = WishMaker.new

new メソッドはクラスメソッドで、新しいまっさらなオブジェクトを作る。それはまたオブジェクトのinitialize メソッドを自動的に呼び出す。WishMaker の定義では、initalize メソッドは@energy = rand( 6 )という一行のコードになっている。

rand( 6 )は0から5の数字を選ぶ。この数はその日の分の望みの数を表している。だからときどきウィッシュメーカーが1つも望みをかなえてくれない日もある。

乱数は@energyという名前のインスタンス変数に代入される。このインスタンス変数はクラスの中であればいつでも使うことができる。ただしクラスのスコープの外では使うことができない。

3章でインスタンス変数について簡単に見て、それを属性(アトリビュート)として考えることにした。(アットマークアトリビュートを意味するのだ。) インスタンス変数はどんな種類の情報でも格納することができるが、多くの場合、そのクラスで表されるオブジェクトの情報を格納するのに使われる。

上の例では、その日その日のウィッシュメーカーは、自身のエネルギーレベルを保持している。ウィッシュメーカーがマシンだったら、中に残っているエネルギーを示す計器がついているのを目にしているところだろう。インスタンス変数@energyはその計器 の役割を果たす。

 todays_wishes = WishMaker.new
 todays_wishes.grant( "枝角" )

OK、一歩下がって、この例が理解できたか確かめてほしい。WishMaker クラスは、魔法の望みプログラム全体がどう動くか記述した概略だ。壺に入った実際のジニーではなく、舞台裏の事務処理を表している。これはジニーが指針とするルールと義務だ。

todays_wishesが壺の中のジニーだ。私たちはそれにかなえてほしい望みを渡す。枝角がほしいんだ、ジニー。(あなたがこの例から本当に枝角を手に入れた としても、私はその話を聞きたくない。草原で新しい仲間たちと跳ね回るといい。)

前の章で覚えたのは、Rubyには2つの部分があるということだった。

  1. ものを定義する。
  2. それに行動させる。

Rubyにおける行動とは何か?  メソッドだ。そしてあなたは今、Rubyに組込まれた定義言語を味見したというわけだ。defを使ったメソッド定義に、classを使ったクラス定義だ。

あなたの教育のこの時点までくれば、Rubyではすべてがオブジェクトだということは理解しやすくなっていると思う。

 number = 5
 print number.next                  # numberの次。'6'がプリントされる

 phrase = 'wishing for antlers'     
 print phrase.length                # phraseの長さ。'19'とプリントされる

 todays_wishes = WishMaker.new
 todays_wishes.grant( "枝角" )

そしてそれぞれのオブジェクトの裏にはクラスがある。

 print 5.class                       # 'Fixnum'と表示
 print '枝角がほしい'.class            # 'String'と表示
 print WishMaker.new.class           # 'WishMaker'と表示

ドクター・チャムが地形を横断して急いでいたときにウィッシュメーカーを目にすることはなかった。ウィッシュメーカーは彼が着陸したセドナの谷からずっと離れた場所にあったのだ。幾重もの藪に覆われた絶壁を下り、 そこから(小さな1” x 6”の紙切れに書いた)望みを投げ込むと、それは口を開けた間隙に消えていく。望むらくは、それがトカゲの背中に着地して捻れた小さな角に張り付いてくれんことを。

あなたの望みがそこまで無事にたどり着けたとしよう。そのあと痩せたサラマンダーはよじれた木々の間を下って行き、朽ちた教会を急いで走り抜ける。その教会は岩だなを越えて険しい谷へと押し落とされる。中にいた任期の切れた牧師はかろうじてその落下を切り抜け、その小さな両生類を殺し——祝福された金の鎖を使って絞め殺し——そして年に一度の「 汝自身を知る」朝食のためにそれを取っておく。彼はあなたの大切な小さな望みを踏んづけて、盗人たちが押し入ったとき、その紙切れは依然そこに、彼の踵に張り付いている。盗人たちの好きな拷問方法はもちろん、牧師を頭からつま先まで薄いスライスにするというものだ。 そうすれば誰が証拠を見つけられる? そして彼らは最後の靴底の薄いスライスを刻み、幸運のお守りにゴムの戦利品を取っておく。この盗人たちはすごく熱心にカヌーを漕ぐ。彼らは流れの中をパドルで素早く叩き、大きな船外モーターのしぶきが上がる。靴底は男の1人のベルトに軽く留められている。そしてぞっとする老いたコイが飛び上がり、その履き物のわずかな断片をかっさらう。盗人たちは水の中を覗くことはできるが覗こうとはしない。もし覗いていたなら、彼らは何百万もの針のような光ファイバーの詰まった強力なケーブルを見たことだろう。実際、その魚は惑星エンダートロムの中心的な機構に直接接続された周辺機器なのだ。この魚 に飲み込ませることさえできれば、あなたの望みは無料で本拠へと送り届けられるのだ!

そしてこれが、この惑星の子どもたちの望みがかなえられる方法なのだ。

私の娘のオルガン教師はウィッシュメーカーのクラスを書き上げると、次に惑星のマインドリーダーのクラスを書いた。

 require 'endertromb'
 class MindReader
   def initialize
     @minds = Endertromb::scan_for_sentience # 意識をスキャンする
   end
   def read
     @minds.collect do |mind|
        mind.read
     end
   end
 end

これまでに見てきたものと同じように、MindReader オブジェクトが新しく作られるときにはinitialize が実行される。initalize は惑星のマインドシェアをスキャンする。それらのマインドは配列に格納されているらしい。それというのも、後の方でcollect メソッドを使ってイテレートされているからだ。

ウィッシュメーカーとマインドリーダーはどちらもEndertrombという名前のクラスを参照している。このクラスはendertromb.rbというファイルに格納されていて、コードrequire 'endertromb'によりロードされる。あなたも仕事の一部を他のクラスを使 ってやるようになるだろう。この本の後半の大部分は、Rubyにロードできる様々な有用なクラスの探求にあてられる。

 

ドクター・チャムは思い切って中に入る

ドクター・チャムが城に近づくと、惑星は彼の考えに気づき、彼の驚きと期待を感じ取ったが、ドクター・チャムが感じたのは死のような静寂だけだった。彼は開いたゲートの階段を上り、 とても美しい建築の入り口に来た。およそ誰もいそうになかった。

彼がノックしてしばらくして、それは報われた。

Blocky whale greeting.

コンコン

「私はドクター・チャム。宇宙旅行者だ。中に入ってあなた達のことを調べてもかまわないか?」
「いいよ」

「じゃあね」
「すごい!」

彼はその赤ちゃんクジラが断固とした風船のように上っていくのを見ていた。彼は最初の異星人との出会いに驚き、それがあまりに早く過ぎてしまったことを少し気にかけていた。まあ、中で待つことにしよう。

城の入り口を通り抜けながら、出迎えたのがどん欲なかぎ爪を持った巨大な鷲でなかったのをありがたく思った。あるいは巨大なネズミの頭とか。あるいは人間サイズのハリケーンとか。ただのずんぐりした小さなクジラだ。

「この城には腰掛けられるところがないな」と彼は言った。

はじめ彼はとても暗い玄関ホールに入ったものと思っていたが、目が慣れてくると、入り口はトンネルに繋がっていたことがわかった。城の扉は長くて平らな岩の板でできた通路にそのまま繋がっていたのだ。ある部分はよく整っていて回廊を思わせた。別な部分は狭くて傾いており、先の方は視界から見えなくなっていた。

通路はドアのない小さな冷蔵庫で照らされていた。それは一抱えのキャベツを入れられるくらいの大きさがあり、足下にあった。中を覗いてみると空っぽで、内壁はすべての面が照らされいて、整然と氷の破片が作られていた。

氷のかけらに触ると、指先にくっついた。ドクターは手をその氷にこすりつけた。皺にはどろが残っていたが、入浴に対する欲求のごく一部は満たすことができた。何年になるのだろう? 10年? 30年?

ある区画では、通路に沿って長い布のチューブが散らかっていた。それから磁器のシャベルとバケツに入った明るいピクセル物質があった。

彼はトンネルから掘り進めて作られた部屋に出た。地面に空の亀の甲羅がいくつかあり、大きな照らされた壁があった。彼は驚いて部屋の中を眺めていた。ここは何だろう?  すこし甲羅の上に腰掛けようかとも思った。入り口にある待合室にようやくたどり着いたのかもしれない。しかし一方で、座ったとたんに甲羅の穴からクモがあふれ出てくるかもしれないと思った。それで彼は先に進むことにした。

 

城のポケットの中での食事

通路を進んでいくと(中央トンネルが分岐してそれからもっと大きな何もない洞窟に合流した)、彼は所々でテーマがあるのに気付いた。いくつかの部屋はポンプ装置で占められていた。別なところでは、布とにかわの桶で占められていた。彼はビロード張りの空洞 から聞こえてくる声の方に進み、行き止まりまで行った。壁の目の高さのところに小さな部屋が彫られていた。

壁に近づいてみると、その小さな穴の中で、2匹のツチブタがテーブルについて食事をしていた。

彼らはドクターを穏やかに見つめていた。どちらも自分の2倍の大きさのある、割って背を下にテーブルに載せられた甲虫をむしゃむしゃと食べていた。

「こんにちは、小さな人形たち」とドクターは言った。彼らは噛み終え、すましてフォークを手にしたまま、ドクターを見つづけていた。

「私の姪のハンナが君たちに会いにここへ来たのじゃないかと思って」ドクターは油断のないミニチュアツチブタに話しかけた。「あの子は君たちを複雑な人形劇だと思うだろうね」。彼はそのダイニングや、何セットもの皿が入った棚やハンドタオルに目を向けた。小さなウサギがマシンの上から半分突き出していて、クリーム状の赤い麺が下から こぼれ出ていた。部屋の奥のドアは半開きになっていた。そのドアを通して、椅子と唸りを上げるモーターのある部屋を垣間見ることができた。

「子供はみんな人形の家を欲しがるよね」と彼は言った。「ハンナは、さっき言ったように私の姪だが、スピンドルの前に座って毛糸を紡ぐからくり人形を持っている。もちろん本当にではない。人形は全然毛糸を作ったりはしない」

ツチブタの一匹が床の上げ蓋を開いて中のボタンを押すと、灯りがついた。それから小さなフィルムプロジェクタがゆっくりと支柱に載って上がってきた。もう一方のツチブタは座っ たままドクター・チャムを見ていた。

「それでもハンナは 人形の家に行って想像上の毛糸を束ねて集めてくる。それを母親、つまり私の妹に渡す。妹はハンナに調子を合わせるのがうまくて、それで人形サイズの服を縫ってやる。その服をハンナは人形の家に持って帰る。

そして人形に『ほら、見て、あなたの努力と根気が、このきれいなドレスになったのよ。これで知事さんのお屋敷で今夜あるパーティへの警察署長さんのお誘いを受けられるわね』と言 うんだ。ハンナは署長役の警察官の制服を着た人形も持っているのだが、それは署長にしてはやせこけているので、ちょっと整形してやる必要があるだろうね」

プロジェクタ担当のツチブタはリールをセットして向こうの壁に狙いをつけた。フィルムが回りはじめ、ツチブタは席に着いた。壁の上に緑色の四角形が現れた。用心深いツチブタは相変わらずドクター・チャムを見ていた。

「君たちのフィルムはカラーなんだね」とドクター・チャムは言った。「なんて可愛らしい小さな生き物なんだ」

映像はつづいて青い四角形を映し出した。それから赤い円。それからオレンジの四角。用心深いツチブタも向こうを向き直って映像がピンク色の三角に変わるのを見た。それからツチブタたちは食事を再開した。

紫の星。赤い四角。静かな状況の中で、ドクター・チャムはプロジェクターのうなる音を聞くことができた。 線路にそってギアを回そうとする歩みの遅いオルゴールのようだった。

「食事を楽しむといい」とドクター・チャムは言って、礼儀正しく踵を返し、元来た方へ歩いていった。

 

ものごとが始まるもうひとつの袋小路

ドクターは城のトンネルの中で道に迷ってしまった。何も見覚えのあるものがない。しかし彼はあまり気にかけてはいなかった。別な惑星にいるのだから、もとより迷子のようなものだ。

彼は曲がりくねったトンネルを進みながら道を思い出そうとしたが、道を気にするには探索するのがあまりに面白かった。彼は1つのトンネルを深く降りて行った。坂があまりに急なので、出っ張りと出っ張りの間を飛び 移りながら、注意深く足がかりを探す必要があった。その場所の重力は地球とは違っているようで、彼は楽々と歩みを進めることができた。

しかし彼には自分がどこにいるのか知るすべがなかった。城の領域はすでに出ているのではないかと思った。これほど深く、これほど長く歩いたのだ。ドアを入ってから1時間はたつ。トンネルはまた上りになったので、どこかの新しい家の中に出るかもしれない。あるいはマンホールに出て、そこから城が見えるかもしれない。たぶんこの道をそんなに遠くまで進むべきではなかったのだろう。その辺で何かが冬眠したりしてないことを願った。

トンネルは先がなくなった。暗い行き止まりだ。

At the end of the tunnels: a computer and a book.

地下埋葬所の奥・・・

・・・マシン・・・

・・・マシンの横には、1冊の本。

ホワイの(感動的)Rubyガイド

彼には時間があったので、その本を読むことにした。彼はキツネたちとピックアップトラックを盗んだヤマアラシの追跡の話を読んだ。彼はエルフとハムの話を読んだ。彼は自分自身の絵を見、自分の奮闘が描かれているのを見た。彼はRubyを学びさえした。それがどういう終わりを迎えるのかを見た。

私が彼だったなら耐えられないだろう。しかし彼は耐えた。そして腹を据えて、何が起こるのかを見ることにしたのだ。

コンピュータモニタに、ドクター・チャムは点滅するirb のプロンプトを見た。あなたもドクター・チャムのように、「トラのチョッキ」(この本の最初の拡張パックで、インタラクティブRubyの基本について書かれている)を読んでirb のプロンプトがわかるようになっているかもしれない。

さっきまで彼は歩いてトンネルを探索してきたが、今度はプロンプトでマシンの設定を探索している。彼は本を元の場所に戻しておいた。もう必要がない。彼がその本を使おうが使うまいが、それはすべて起きることなのだ。

彼は次のように調べはじめた。

 irb> Object::constants
   => ["Marshal", "String", "Dir", "LoadError", "Float", ... そのほか ]

このコマンドはすべてのトップレベルの定数をリストアップする。クラス名もまた定数としてリストアップされる。だからこのリストはRubyにロードされているものを調べるためのいい方法なのだ。

彼はリストの中に見慣れないものがないか探した。Rubyにもともと入ってはいないものだ。MarshalStringDirLoadErrorFloat。これらはどれもRubyにはじめからあるものだ。

しかしリストの先の方を見てみると——

 ... "Struct", "Values", "Time", "Elevator", "Range" ...

Elevatorだって?  これは間違いなく調べるべきクラスだ。彼は調査を進めた。

 irb> Elevator::methods
   => ["method", "freeze", "allocate", ... 長いリストがつづく ... ]
 irb> Elevator::class_variables
   => ['@@diagnostic_report', '@@power_circuit_active', '@@maintenance_password']
        (診断レポート)                (電源回路アクティブ)           (保守用パスワード)
 irb> Elevator::constants
   => []

Elevatorクラスにはたくさんのメソッドがあるようだ。その多くはRubyのオブジェクトがみんな持っているもののようだった。たと えばmethodfreezeallocateはRubyのどのクラスにもある。(Elevator::freezeElevatorクラスが変更されないようにする。Elevator::allocateinitializeメソッドを呼ばずに新しいElevatorオブジェクトを作成する。)

class_variables はドクター・チャムには興味深いものだった。このエレベータは本物のようだ。一方constantはなく、これはElevator クラスの中にネストされたクラスがないことを示している。

彼はElevator オブジェクトを作ろうとした。

 irb> e = Elevator::new
 ArgumentError: wrong number of arguments (0 for 1), requires a password
         from (irb):2:in `initialize'
         from (irb):2:in `new'
         from (irb):2
         from :0

パスワードがいるようだ。適当に試してみた。

 irb> e = Elevator::new( "going up" ) # 上ヘ上がる
 AccessDeniedError: bad password
 irb> e = Elevator::new( "going_up" )
 AccessDeniedError: bad password
 irb> e = Elevator::new( "stairs_are_bad" ) # 階段は嫌いだ
 AccessDeniedError: bad password
 irb> e = Elevator::new( "StairsAreBad" )
 AccessDeniedError: bad password

こんなことをやっていてもしょうがない。いや、待って! メンテナンスパスワードだ。確かclass_variablesの中にリストされていた。

 irb> Elevator::maintenance_password
 NoMethodError: undefined method `maintenance_password' for Elevator:Class
         from (irb):1
         from :0

うーん。インスタンス変数はオブジェクトの中でしか使えないのだ。そしてクラス変数もクラスの中でしか使えない。どうやってパスワードを取り出せばいいのだろう?

 irb> class Elevator
 irb>   def Elevator.maintenance_password
 irb>     @@maintenance_password
 irb>   end
 irb> end
   => nil
 irb> Elevator::maintenance_password
   => "stairs_are_history!"
        (階段は過去のものだ!)

やった! パスワードを手に入れた。どうやったのかわかった?

彼はElevator クラスにクラスメソッドを追加したのだ。Elevator の新しいクラス定義を書くと、Rubyは既存のクラス定義を変更してくれる。これってすごいと思わない?

クラスメソッドは通常二重のコロンを使って呼ばれる。しかしピリオドも問題なく使える。Elevator 自体はクラスだが、RubyはあなたがElevator.maintenance_passwordを呼ぶとき、クラスメソッドを呼んでいるのだとちゃんとわかる。二重のコロンを使うのは、コードを読む人にそれがクラスメソッドだとはっきりわかるようにするためだ。

それだけのことだ。クラスメソッドというのは少し変わっている。普通はクラスに直接情報を持っておこうとは思わない。しかしそのクラスのすべてのオブジェクトの間で共有すべき情報がある場合、クラスをその保存場所として使うのは理にかなっている。@@maintenance_password が個々のオブジェクトでなくクラスに保存されていたのは理解できることだ。そうすることで、オブジェクトはクラスに手を伸ばして共有パスワードを見ることができる。

パスワード保護の仕組みは、おそらく以下のようになっているのだろう。

 class Elevator
   def initialize( pass )
     raise AccessDeniedError, "bad password" \
       unless pass.equals? @@maintenance_password
   end
 end

このようにクラスをパスワード保護するのは意味がない。Rubyの中のものはすべて、入れ替え、上書きし、作り直せるからだ。ドクター・チャムはパスワードを手に入れ、エレベータの所有権を得た。

 irb> e = Elevator.new( "stairs_are_history!" )
 #<Elevator:0x81f12f4 @level=4>
 irb> e.level = 1

コンピュータ端末の裏にあるエレベータの扉が開いたとき、ドクター・チャムはその真ん前に立っていた。圧倒的な達成感と、この先に待ち構えていることに対する興奮に包まれて、彼はエレベータに乗り込み、4のボタンを押した。

3. 娘のオルガン教師の話のつづき

私に娘がいると聞いたら驚くと思う。私が書くものには、麻痺や幼児的な精神の兆しが見られると思うだろう。安心していい。私には娘はいない。しかしそんなことで 娘の音楽教育の手はずを整えるのを私が思い留まったりはしない。

前に言ったように、惑星エンダートロムのこの精巧な歴史は、私が廊下をさまよい、しっかりボタンが埋め込まれたソファを指先でなぞり、私の娘のオルガン教師が演奏するパイプの響きに満たされているときに見 い出したものだ。彼の奏でる音は彼の家の壁に深く空ろに反響し、私はいつしかそれを不吉な沈黙と取り違えるようになった。そして自分の思考の深みに入り込むのがより容易になっていることに気付いた。 太古の惑星と、その わかりにくい哲学について考える。殉教者の皮膚をなめして作った肉体の神殿。敵を飲み込んで何十年も閉じこめ、肋骨の階段を引きずり上げ、引きずり下ろすクジラのカルテル。毒の霧と痛みを与える戸口。そして宇宙のすべての知的存在の父祖であると主張している種族であり、ぞっとする支配者のザ・オリジナルズのこと。

しかし突然、いっそう高い音を響かせるパイプの音に、私は元いたのと同じそよ風吹く午後へと連れ戻された。

他所から来た者には、私たちの惑星のそよ風さえすごく奇妙なものでありうるというのは、とても興味深いことだ。彼はラス-dからやって来た旅行者の話もしてくれた。彼らは500年前に地球へと旅してきたが、 彼ら自身も彼らの武器や乗り物も木炭でできていたため、速やかに気流の中へと霧散してしまった。

私はオルガンの前に座って、彼のする植民地についてのおぼろな話を聞いていた。彼がシンフォニーをより大きな音で強調するときには、物語はしばらく消え、コーダになるまでとぎれ た。彼と彼の兄弟は母親のしっぽの洞の中に入り込んで、内壁からべとべとした三日月細胞を引きはがした。水分が多く柔らかくねばねばした石けんが彼らの口を漂白し、食道を下りていきながら消毒した。彼らが噛み、むしゃむしゃ食べると、それ が泡を立てた。食べ終わった後、彼らは互いにシャボン玉を吹き合った。それぞれのシャボン玉は濃い泡で満たされており、彼らはその上で眠った。そして翌朝早く、母親はしっぽを拡げてみて、彼女の赤ちゃんが黒いミートボールと甘いべとべとした泡のシチューの中で眠っているのを穏やかに見つめた。

彼はエンダートロムの味覚について詳しく物語った。彼らの鮭は、デンプン器官がパスタ料理に使われ、目はこくのあるクリームになる。それに触手のあるバターメロン。彼は子供としてデリカシーを理解しはじめたばかりで、ひと組の直立したピグミーゾウに校庭から持ち上げられていた。ゾウは空から、長いクレーンで彼の襟をつかんだ。

彼らは彼を地球に移植した。彼を乗り物から連れ出し、グランド・ラピズの町に聞こえるよう大きな音で鼻を鳴らし、それから互いに泣き合い抱き合いながら立ち去った。

「しかし奇妙なことに(エン ピシー ダー)、私は故郷の惑星で(ポン シュー)オルガンを習い、演奏(オス リー)したのです」と彼は言った。

私の娘のオルガン教師は、ここでは括弧に入れて示した余分な言葉を口にする。それが彼の母国語から来ているのか、それとも彼特有の声を出すしゃっくりなのか、誰にもわからない。彼はエンダートロムの他の遺物も持ち続けていた。彼には12の名前があっただ。

「いいえ(ウェン イス ウェン)」と彼が言った。「私が持っているのは(イン アパラ)1つの名前で(イフ)それがさまざまな言い方をされるのです」

私は朝には彼をペイジ-リーと呼び、夜にはペイジ-プロと呼ぶ。私がこれを書いているのは昼間なので、私は彼をペイジ-リーと呼ぶことにしよう。

 

つぶやきなしの耳栓

Alien at the keys.

それで私はペイジ-リーに言った。「ペイジ-リー、私は本を書いています。世界の人にRubyを教えるためです」

「おお、(ピル ノグ ピル ヨット)それはいいですね」と彼は言った。彼は私より長くRubyを知っているが、それでも娘のRuby教師になるのはだ。

「ペイジ-リー、あなたはその本に出てきますよ。あなたの惑星の話も」。私は彼がE.T.みたいな言い方をした。なぜかわからない。そしてこんなふうに言った。「いつか故郷のお父さんやお母さんの元に帰れるかもしれませんね」

それに対して彼は「(ポン シュー)(ポン シュー)(エン ピシー ダー)」と答えた。これは沈黙と畏怖をはっきり声に出す彼のやり方なのだ。

彼は私の書いたものを見たがったので、あなたのために書いたこの短いメソッドを彼に見せた。

 def wipe_mutterings_from( sentence )
   while sentence.include? '('
     open = sentence.index( '(' )
     close = sentence.index( ')', open )
     sentence[open..close] = '' if close
   end
 end

「これが何をするかわかりますか、ペイジ-リー? スモッチキスは誰でもこのメソッドを使ってあなたの話から意味不明なつぶやきを取り除くことができます」と私は言った。

そして私は彼が前に言った言葉をこのメソッドに入れてみた。

 what_he_said = "しかし奇妙なことに(エン ピシー ダー)、
   私は故郷の惑星で(ポン シュー)オルガンを習い、
   演奏(オス-リー)したのです。" 
 wipe_mutterings_from( what_he_said )
 print what_he_said

そうすると普通の文章が出てくる。

 しかし奇妙なことに、 
 私は故郷の惑星でオルガンを習い、
 演奏したのです。

「あなたはそこでWhileループを(ウェアリ トゥ)使うべきじゃありません」と彼は言った。「もっときれいで(ソプト アー)やさしいやり方があります」

wipe_mutterings_from メソッドは、基本的に開く括弧を探している。見つけると、そのあとの閉じる括弧を探す。両方とも見つかったら、それらの括弧とその中身を空の文字列で置き換える。while ループは括弧がなくなるまで続く。つぶやきがすべて取り除かれたらメソッドは終わる。

「このメソッドを見直してみると」と私は言った。「ちょっとまごつく面がありますね。もう少しうまくやれそうです」。 こんなコードを書いたことで、あなたの教師をどうか見下さないでほしい。だらしのないテクニックを見せて、一緒にそれを直していくというのもいいやり方だと思う。じゃあはじめよう。

まごつく面の第1番。このメソッドは文字列をクリーニングする。しかし間違ってファイルを渡したらどうなるだろう? あるいは数を渡したら? 何が起こるだろう? たとえばwipe_mutterings_from( 1 )とやったらどうなるか?

wipe_mutterings_from に1を渡すとRubyは以下のような出力をして終了する。

 NoMethodError: undefined method `include?' for 1:Fixnum
         from (irb):2:in `wipe_mutterings_from'
         from (irb):8

あなたが目にしているのは、幾分奇妙で冗長な(しかし時にすごく役に立つ)バックトレースと呼ばれるものだ。これは興奮した警察官で、ほんのちょっとしたトラブルの兆候にも飛んできて怪しいやつをみんな捕まえ、壁に押しつけて権利をすごく早 口で読み上げるのだが、あまりに早くて誰も聞き取れない。しかしまあ問題があるのは明らかだ。そしてもちろん、それは大きな誤解なのだ。そうだよね?

Rubyがこのミランダ権利を読み上げるときには、最初の部分を真剣に聞くようにしよう。多くの場合、最初の行にあなたが必要とするすべてがある。この最初の行が本質的なメッセージを含んでいるのだ。上の例では、最初の行は数1にはinclude?メソッドがないと言っている。前の章でreverseメソッドについて話したのを覚えている?  あのとき私は「多くのメソッドはある種の値に対してしか使えない」と言った。reverseinclude?も文字列に対しては 使えるが、数に対しては意味がなく 、使えないのだ。

わかりやすく言うと、このメソッドを数に対して使おうとしたということだ。メソッドは最初sentenceに1をセットする。それから2行目のwhile sentence.include? '('のところに来るが、数はinclude?メソッドを持たない。すばらしい。バックトレースは問題がどこにあるか 正しく示してくれている。私は誰かが数を渡してよこすなんて考えてなかったので、数に対しては使えないメソッドを使っていたのだ。

それだけのことだ。私たちのメソッドは一個の小さなポケットツールみたいなものだ。他のものには依存しない独立した道具だ。wipe_mutterings_from メソッドを使う人が数を渡すとこのパニックメッセージを見せられることになるが、それは彼らには意味をなさない。彼らはメソッドの中身を調べるようにと言われているのだが、それは本当に彼らの知ったことではないのだ。彼らはその場所は不案内なのだ。

幸い、私たちは自分のエラーを、自分の例外を投げることができる。そうして不注意に間違ったオブジェクトをクリーニングに出した人に対し、もっと意味のわかるメッセージを出 してあげることができる。

 def wipe_mutterings_from( sentence )
   unless sentence.respond_to? :include?
     raise ArgumentError, 
       "#{ sentence.class }からつぶやきを取り除くことはできません" 
   end
   while sentence.include? '('
     open = sentence.index( '(' )
     close = sentence.index( ')', open )
     sentence[open..close] = '' if close
   end
 end

今度は、数を渡したときに(再び1を使う)、もっと理にかなった結果が得られる。

 ArgumentError: Fixnumからつぶやきを取り除くことはできません
         from (irb):3:in `wipe_mutterings_from'
         from (irb):12

respond_to?メソッドはすごく便利だ。お願いだからこれのことを忘れないでいてほしい。respond_to?はオブジェクトを調べ 、それがあるメソッドを持っているかどうか確認する。そしてtruefalseを返す。上の例では、入ってくるsentence オブジェクトがinclude?メソッドを持つか確認している。そしてinclude?メソッドが見つからない場合に、エラーを投げている。

あなたは私がrespond_to?に対してシンボルを使ったのを不思議に思っているかもしれない。私は文字列'include?'のかわりにシンボル:include?を使った。実際、respond_to?に対してはどちらでも使えるのだ。

通常メソッドやそのほかのRubyの構成物の名前を受け渡すときにはシンボルを使う。その方が効率的だからだが、そのほうが目に付きやすいということもある。respond_to?はRubyに自分の中を見てメソッドが使えるか確認してくれと言っている。私たちはRubyに聞いているので、そのことを示すためにシンボルを使うのだ。大したことじゃない。Rubyは文字列よりもシンボルの方が早く認識できるのだ。

まごつく面の第2番。私たちのメソッドが文を変えてしまっていることに気付いた?

 something_said = "1隻の(ギス)宇宙船です。" 
 wipe_mutterings_from( something_said )
 print something_said

これ、気付いていた? 最初の行でsomething_said 変数は文字列"1隻の(ギス)宇宙船です。"を持っている。しかしメソッドを呼んだ後の3行目でsomething_said 変数をプリントしてみると、クリーニングした文字列"1隻の宇宙船です。"になっている。

どうなっているのだろう? メソッドはどうやって文字列を変えたのかしら?  変更する前に文字列をコピーしておくべきじゃないだろうか?

そう、まったくもってそうすべきだ! こんなふうに文字列を変えるのはいいマナーではない。前の章でgsubgsub!を使った。どちらが文字列を直接変更する破壊的メソッドだったか覚えている?

私たちは(このメソッドを使うかもしれないすべての善良なる人々の好意に甘えて)このメソッドをwipe_mutterings_from!と呼ぶことにするか、あるいはメソッドが元の文字列でなくコピーした文字列を使うように変えるかする必要がある。そしてこの変更は簡単にすることができるのだ! 文字列をただdup するだけでいい。

 def wipe_mutterings_from( sentence )
   unless sentence.respond_to? :include?
     raise ArgumentError, 
       "#{ sentence.class }からつぶやきを取り除くことはできません" 
   end
   sentence = sentence.dup
   while sentence.include? '('
     open = sentence.index( '(' )
     close = sentence.index( ')', open )
     sentence[open..close] = '' if close
   end
   sentence
 end

dup メソッドは任意のオブジェクトのコピーを行う。追加した行を見てみよう。

 sentence = sentence.dup

変なコードだ。どうやってsentencesentence のコピーになるのだろう? 自分を消しているのだろうか? もとのsentenceはどうなってしまうのか? なくなってしまうのか?

変数は単なるニックネームであるというのを思い出そう。sentence = "1隻の(ギス)宇宙船です。"でRubyは文字列を作り、その文字列にニックネームを付ける。

同様に、sentence = sentence.dupでRubyは新しい文字列を作成してそれにニックネームを付ける。これはメソッドの中で使うのに便利で、今やsentence はコピーされた新しい文字列に対するニックネームになっており、メソッドに渡された文字列を変えることなく安全に扱うことができる。

変数名が再利用されている例はたくさん目にすることになるだろう。

 x = 5
 x = x + 1
 # x は 6 になっている

 y = "Endertromb" 
 y = y.length
 # y は 10 になっている

 z = :include?
 z = "a string".respond_to? z
 # z は true になっている

そして、オブジェクトが消失する場合もある。オブジェクトが変数を通してアクセスできない場合、Rubyはあなたがそのオブジェクトにはもう用がないのだとわかり、それを削除する。Rubyは 定期的にガーベジコレクターにそういったオブジェクトを開放するように指示する。すべてのオブジェクトは、ガーベジコレクターがそれを削除するまでコンピュータのメモリ 上に存在し続ける。

そうだ、dupについてはもうひとつ言うことがある。dupできないものもあるのだ。たとえば数がそうだ。シンボル(:deathみたいな形をし たもの)はスペルが同じなら同じものになる。その点で数と同じだ。

niltruefalseのようないくつかの特殊な変数もdupできない。これらを変更することはRubyが許さない。だからコピーを作ることにそもそも意味がない。falsetrueに変えることができたらどうなるか考えてごらん。すべてが嘘になってしまう。

まごつく面の第3番は簡単なものだ。私は角カッコを文字列に対して使った。文字列を配列かハッシュのように扱った。そうすることができるのだ。文字列は[]メソッドを持っている。

角カッコが文字列に対して使われると、文字列の一部が抽出される。再びフォークリフトの角だ。文字列は長い棚で、フォークリフトが文字列の塊を引っ張り出す。

角カッコの中にはインデックスを入れる。これはフォークリフトの作業者が見えるように角の間に書かれたラベルだ。文字列に対しては、いろいろなオブジェクトがインデックスとして使える。

 str = "A string is a long shelf of letters and spaces." 
 puts str[0]       # 65 がプリントされる('A'の文字コード)
 puts str[0..-1]   # 'A string is a long shelf of letters and spaces.' がプリントされる
 puts str[1..-2]   # ' string is a long shelf of letters and spaces' がプリントされる
 puts str[1, 3]    # 'A s' がプリントされる
 puts str['shelf'] # 'shelf' がプリントされる

まごつく側面の第4番。このメソッドは無限ループに陥る可能性がある。このメソッドをハングさせて戻ってこないようにする文字列を渡すことができるのだ。メソッドを見て。泥だらけの棒を放り込んでループを詰まらせることができる?

 def wipe_mutterings_from( sentence )
   unless sentence.respond_to? :include?
     raise ArgumentError, 
       "#{ sentence.class }からつぶやきを取り除くことはできません" 
   end
   sentence = sentence.dup
   while sentence.include? '('
     open = sentence.index( '(' )
     close = sentence.index( ')', open )
     sentence[open..close] = '' if close
   end
   sentence
 end

ほら、この泥だらけの棒のカーブを入れると故障する。

 muddy_stick = "ここに(カーブがある。" 
 wipe_mutterings_from( muddy_stick )

なぜメソッドはハングしたのだろう? それはwhile ループが開く括弧がなくなるまでループを止めないからだ。そして開く括弧が置き換えられるのは対応する閉じる括弧があるときだけだ。だから閉じる括弧が見つからない場合には開く括弧は置換されず、while の終了条件はいつまでたっても満たされない。

メソッドをどう書き直せばいいだろう? Rubyの抜け道を知っている私は、正規表現を使うことにする。

 def wipe_mutterings_from( sentence )
   unless sentence.respond_to? :gsub
     raise ArgumentError, 
       "#{ sentence.class }からつぶやきを取り除くことはできません" 
   end
   sentence.gsub( /\([-\w]+\)/, '' )
 end

ループを使うときには慎重にするようにしよう。whileループやuntilループはすぐ手に負えなくなる。イテレータを使う方がいい。そして場合によっては正規表現が使える。

まとめると、私たちがメソッドの書き方について学んだのは:

  1. メソッドを使う人が予期してないようなものを渡してきても驚かないようにしよう。彼らのよこしたものがまったく使えないなら、エラーをraiseすればいい。
  2. メソッドが渡されたオブジェクトを変更するのはエチケットに反する。dupでコピーを作ろう。あるいはgsub のような自動的にコピーを作るメソッドを使ってもいい。
  3. ArrayHashStringのオブジェクトは[]メソッドを持っており、中にあるものを探すのに角カッコメソッドが使える。また、これらのオブジェクトは[]=メソッドも持っているので、角カッコ を代入文(の等号の左辺)で使って中身を書き換えることができる。
  4. 手に負えないループに気をつけること。可能であればwhileuntilを避ける。

 

呼び名のメカニズム

Cat salesmen from the sky.

ペイジ-リーの家の裏の木がガサガサという音を立て、それは空から男が降ってきたためだとわかった。彼は名をダグと言い、猫を売っていた。

彼が視界に入ると、彼の影(と彼の足にくくりつけられた猫の影)が、私たちがラケットボールで撃とうとしていた芝生の鳥を隠した。大きな風船からヘリウムを絞り出している彼に、私たちは「こんにちは、ダグ!」と叫んだ。

すると彼は「こんにちは、ゴンク-リー! こんにちは、ホワイ!」と答えた。

ペイジ-リーはポケットを調べ、1ドル27セントあるのを確かめた。暖炉の番と衛星放送アンテナの向きを変える仕事に必要な3匹の猫を買うためだ。ペイジ-リーが猫たちをジェネレーターに放り込むとひと しきり文句を言ったが、たくさんある巨大なガラス棒が猫たちを絶えず愛撫するだろう——でも待って、猫商人は彼をゴンク-リーと呼ばなかった?

そして彼は朝にはゴンク-リーと呼び、夜にはゴンク-プロと呼んだ。

だからサフィックスは昼夜と関係があるに違いない。私にわかる限りでは、プレフィックスの方は呼ぶ人のペイジ-リーに対する関係を表すようだ。

 class String

   # 娘のオルガン教師の名前のパーツ
   @@syllables = [
     { 'Paij' => '私用',
       'Gonk' => '仕事',
       'Blon' => '奴隷',
       'Stro' => '主人',
       'Wert' => '',
       'Onnn' => '' },
     { 'ree'  => '午前',
       'plo'  => '午後' }
   ]

   # 彼の名前が何を意味するか判断するメソッド
   def name_significance
     parts = self.split( '-' )
     syllables = @@syllables.dup
     signif = parts.collect do |p|
       syllables.shift[p]
     end
     signif.join( ' ' )
   end

 end

私は単にだらしのないコードを見せるというのを越えてしまった。これははなはだ不作法であり、自然に対する罪だ。ほとんどの言語はそもそも犯すことを許していないような罪だ。私たちはRubyのコアのクラスの1つであるStringを変更しているのだ!

「これがちょっと危険なことはわかっています」ペイジ・リーの鼻先にこのコードを差し出したとき、私はそう言った。「これで困る人がいないといいのですが」

「スモッチキスはみんなこれがどんな危険をもたらすか(ケプ ヨ イコ)味わうべきです」と彼は言った。「犬に、丸太に、沼へのはまり込み(クル イプ)、すべて味わうべきです」。そして彼はビーグルベリー沼地ドリンクをぐいっと飲んだ。

String クラスに追加するとはどういうことなのか? 2つある。クラス変数、それにメソッド。普通のインスタンスメソッドだ。

私はアットマークをアトリビュートを意味する文字だと思うようにしている。二重のアットアトリビュート・オールを意味 する。クラス変数だ。クラスのすべてのインスタンスはこの変数を見ることができ、値はどのインスタンスにも同じになる。@@syllables変数は配列で、今やStringクラスの中ならどこでも使えるようになった。

新しく作ったメソッドのname_significanceは、どんな文字列に対しても使える。

print "Paij-ree".name_significance 私用 午前とプリントされる。

これにより、ペイジ・リーは私的な名前であることがわかる。友達が早い時間に使う名前だ。

selfを使っている行を注意して見てほしい。これは特殊変数で、メソッドの呼ばれている対象のオブジェクトを表している。 わかりやすくするため、ハイフンで文字列を分割するメソッドを作ってみることにしよう。

 class String
   def dash_split
     self.split( '-' )
   end
 end

このメソッドもまた、すべての文字列に対して使えるようになる。

"Gonk-plo".dash_split は配列['Gonk', 'plo']を返す。

self の使用はRubyのより進んだ概念へと入りはじめたことのしるしだ。Rubyは定義言語だ。あなたはメソッドを、それが使われる前に定義ずる。そのメソッドを使うオブジェクトの存在を準備しているのだ。 ここであなたは「dash_splitが使われるときには、ハイフンで分割しようとしている文字列が あるわけだが、self はその文字列を参照する特殊変数だ」と言っているのだ。

Rubyはすごい定義言語だ。この本を深く読み進めていけば、興味深く頭を悩ませる議論が出てくる。

多くの場合にはself を明示的に使う必要はない。メソッドは他のメソッド定義の中から直接呼び出すことができるからだ。

 class String
   def dash_split; split( '-' ); end
 end

name_significanceメソッドにあるループを見つけてごらん。Array#collectについて学ぶことはとても重要だ。詳しく見てみよう。

 signif = parts.collect do |p|
   syllables.shift[p]
 end

配列partsは分離された名前を持っている。たとえば['Paij', 'plo']のような。この配列のそれぞれの要素をcollectを使ってイテレートするのだが、collecteachより進んでいる。eachと同様に、collectはそれぞれの要素をブロック引数として滑り台で落とす。それから、ブロックの終わりで、collectブロックが返す答えを取っておいて新しい配列の中に追加していくcollect メソッドは既存の配列の値に基づいて新しい配列を作るためのすばらしい方法だ。

ダグは売り物の猫を3匹持っている。1匹は12セント、別な1匹は63セント、最後の1匹は9セントだ。20%チップをつけるとしたら、それぞれの猫がいくらになるか見てみよう。

 catsandtips = [0.12, 0.63, 0.09].collect { |catcost| catcost + ( catcost * 0.20 ) }
  (猫とチップ)                                   (猫代)

ペイジ-リーの家の敷地は森のとてもすばらしい場所にあると思う(猫やダグが降ってこないときには)。よくペイジ-リーと私は家の裏にある川の側にテントを張ってキャンプし、薫製のブラックバードを食べ、小さな眠るインディアンの像を薄暗い光の中で彫った。時折彼はスペードで負け、彼がエンダートロムのことを思い出して集中していないのがわかった。このすべてがときどき彼を揺り動かしていたに違いない。私は彼の話を聞く最初の人間だった。

「私はアンブローズから来たばかりです」と私言った。「地下の我が家のような場所で、エルフが動物を完全なものにしようと努力しています」

彼はもごもご言いながらうなずいた。「あなたはそのような(ポス イン オイン)ことに関わって(イン)はいけません」

「私たちが失敗すると思いますか?」

「私は(プリープ)以前そこにいました」と彼は言った。それからくじの話をした。

4. ヤギは映画を全部見たがっている

Blinky, winky, a goat... awakes...

「こんにちは??
何か学べる相手は誰かいませんか??」

「エレベーター?・・・
・・・まさか・・・
エ・エ・エレ・レベーター・・・
・・・ありえない・・・
・・・エレ・・・
いや・・・」