1. てほどき
シェルから下記を打鍵して,Ruby で hello, world を印字しよう。
$ ruby -e 'puts "hello, world"' hello, world $
コマンド ruby は Ruby 言語のインタープリタであり,C 言語で実装されている。
-e オプションを与えると,それに続く引数を1行の Ruby プログラムと見なして実行する。
ここでは puts "hello, world" を実行した。
1行におさまらない複雑なプログラムを実行するには,Ruby プログラムをテキスト・ファイルに書き,そのファイル名を ruby コマンドに引数として与えるとよい。 普通はファイルの拡張子を .rb にする。
複雑なプログラムではないが,試しに,下記の内容の hello.rb を作り,
puts "hello, world"
実行してみよう。
$ ruby hello.rb hello, world $
ここで puts は,いわゆる関数であり,引数として与えられた文字列 (ここでは "hello, workd") を印字する。
引数が文字列でなければ,その文字列表現 (例えば,整数 1 ならば文字列 "1") を得て,それを印字する。
複数の引数を与えてもよい。
puts は各引数ごとに,もしも引数の文字列表現が改行で終わっていなければ,改行する。
\t はタブ,\n は改行です。
puts の引数を
"\thello,\nworld" としたとき,
"hello, world\n" としたとき,
"hello, world\n\n" としたとき,どうなるか確かめてみてください。
2番目の結果に納得がいかないときは,上の本文の最後の文をもう一度読み直してください。
関数に複数の引数を与えるときは,カンマで引数を区切る。
引数 (厳密にいえば,カンマで区切られたゼロ個以上の引数の並び) は丸括弧で囲む。
ただし,あいまいでなければ丸括弧を省略できる。
いままでの例では省略していた。
省略せずに書けば puts("hello, world") のようになる。
$ ruby -e 'puts(1, 2, 3)' 1 2 3 $ ruby -e 'puts 1, 2, 3' 1 2 3 $
デフォルトの設定では Ruby は漢字などのマルチバイト文字を認識せず,単にバイトの列として扱う。 ruby にオプション -Ku か -Ke か -Ks を与えたときは,それぞれ utf-8,euc-jp, shift_jis が文字エンコーディングとして設定される。 プログラムの字句解析や正規表現の解釈などでは,設定された文字エンコーディングが使われる。
とりわけ,プログラム中の文字列に "ソ" や "表" などのいわゆる SJIS ダメ文字が 含まれているときは,正しい字句解析のために -Ks が必須である。
$ nkf -S sososo.rb puts "ソソソ" $ ruby sososo.rb | nkf sososo.rb:1: unterminated string meets end of file $ ruby -Ks sososo.rb | nkf ソソソ $ jruby -Ks sososo.rb | nkf :1: sososo.rb:2: unterminated string meets end of file (SyntaxError)
ここまでで君は Ruby による hello, world をマスターした。 君は,今,たいていの文字列を Ruby で端末に表示できる。 もちろん,ただメッセージを表示できるだけではつまらない。 次のステップとして Ruby を電卓がわりに使うことをおぼえよう。
1.1 irb: 対話型 Ruby シェル
ruby には対話型 Ruby シェル irb が付属している。i は interactive (形容詞: 対話型の) を意味する。 irb は Ruby プログラムの簡単なテスト・ツールとして,手軽な Ruby 学習ツールとして,そして強力な電卓として使える。 終了するには Control-D を打鍵するか,exit または quit を入力する。
$ irb irb(main):001:0> 5 + 6 => 11 irb(main):002:0> quit $
プロンプト irb(main):002:0> の意味は順に, 自分自身のコマンド名 (irb), ここでの self の文字列表現 (main), 入力テキストとしての行番号 (002), 入れ子のレベル (0),そして開始行 (>) である。 self は Ruby で重要な役割を演ずるが,まだ,しばらくは見なかったことにしよう。
入力された式が構文的に完結していないときは,自動的に次の行に継続される。 継続行ではプロンプトに > ではなく * が使われる。
irb(main):001:0> 1 + irb(main):002:0* 2 + ( irb(main):003:1* (3 + 4 + irb(main):004:2* 5)) => 15 irb(main):005:0>
ここで計算した 5 + 6 (⇒ 11) や 1 + 2 + ( (3 + 4 + 5)) (⇒ 15) は,実は Ruby
の文法にしたがった「式」である。
多くのプログラミング言語と同じく,「式」はおなじみの四則演算と括弧と数だけには限られない。
真理値 true,false や文字列も許される。
irb(main):005:0> true => true irb(main):006:0> false => false irb(main):007:0> "hello, world" => "hello, world" irb(main):008:0> "hello\n world" => "hello\n world" irb(main):009:0>
これから分かるように true,false や文字列の計算結果の値は,数それ自身の計算結果の値と同じく,自分自身である。
irb(main):009:0> 4242.564 => 4242.564 irb(main):010:0>
つまり,難しく言えば,これらは自己評価的 (self-evaluating) な値といえる。 もちろん,これだけだったらほとんど何の役にも立たない。 重要なのは,単純な式から複雑な式を組み立てられることであり,文字列や数はその典型的な構成要素だということである。 式を組み立てる方法として四則演算があるが,より一般的な方法として 関数 の呼出しがある。
一方,jirb は自分自身で同様の行編集機能を備えており, どのプラットフォームでも便利に行編集ができます。 ただし,jruby 1.1.6 は,まだ日本語入力に難があります。 irb や jirb で日本語入力ができないときは,コマンド行引数として --noreadline オプションを与えて,行編集機能を切ってみてください。
下記は,jirb でオプションをいろいろかえて文字列
"いろは" を入力してみた例です。
--noreadline オプションを与えたとき,入力行が正しくエコーバックされていることが分かります。
-Ku オプションを与えたとき,ひらがながマルチバイト文字として認識され,
出力がエスケープ列ではなくひらがなの "いろは" となっていることにも注意してください。
ここでは utf-8 端末を使っています。
$ jirb irb(main):001:0> "?????" => "\343\201\204\343\202\215\343\201\257" irb(main):002:0> exit $ jirb -Ku irb(main):001:0> "?????" => "いろは" irb(main):002:0> exit $ jirb --noreadline irb(main):001:0> "いろは" => "\343\201\204\343\202\215\343\201\257" irb(main):002:0> exit $ jirb -Ku --noreadline irb(main):001:0> "いろは" => "いろは" irb(main):002:0> exit $
1.2 nil
irb から Ruby の任意の関数を呼び出すことができる。 さっき習った puts を呼び出してみよう。
irb(main):006:0> puts "hello, world" hello, world => nil irb(main):007:0>
puts の動作として hello, world が印字される。
ここで,puts が印字した行の,次の行にある「=>」に注目しよう。
今までの例では,その先には,式を計算した結果の値が書かれていた (5 + 6 に対する 11 など)。
今は「無」を意味する nil が書かれている。
つまり,puts の 結果の値 (result value,または単に result,
関数呼出しだから 戻り値, return value とも言う) は nil である。
Ruby には C/C++ で言うような,いわゆる void 関数はない。 すべての関数呼出し,さらにいえば,すべての文が,式としての何らかの結果の値を持つ。 結果の値に意味がないことを示すために nil を使う。
例えば,C++/Java と同じく Ruby でもループを中断するには break を使いますが, これ自体は Ruby が直接「値」として扱えるような値を持ちません。 値を持たせようと思っても持たせることはできません。ナンセンスです。 しかし,その場合でも,break されたループそのものは値を持ちます。 実際,break に引数を与えてその値を指定できます。 ですから,より正確には「Ruby のすべての文は,仮に結果の値があってもおかしくないならば,必ずなんらかの結果の値を持つ」です。
より厳密な議論を求めたい人は「継続」(continuation) をキーワードにして調べてみましょう。
Ruby では,「偽」である false と「無」である nil の二つだけが, 真理値としての偽を表す。
irb(main):007:0> if nil then "Yes" else "No" end => "No" irb(main):008:0>
ここで if X then Y else Z end は, 妥当な任意の X,Y,Z について, X の結果の値が真理値として真ならば Y を実行してその結果の値を返し, 偽ならば Z を実行してその結果の値を返す。
「真」である true を含め,他のすべての値は,真理値としては真と扱われる。
0 も空文字列 "" もすべて真と見なされます。
上記の if nil then の nil を 0 などに置き換えて確かめてみましょう。
1.3 puts/p と to_s/inspect とオブジェクト指向
puts で文字列を印字するとき,クォーテーションマークは印字されない。
しかし,これまでの例から分かるように,「=>」の先では文字列は (上記の "No"
などのように) クォーテーションマーク付きで印字される。
そのような形式で文字列を印字したいときは,関数 p を使う。 たった1文字の関数名だが,中身は puts と同格の立派な関数だ。
irb(main):008:0> p "hello, world" "hello, world" => nil irb(main):009:0>
この puts や p のすぐ裏側では,Ruby のいわゆる オブジェクト指向 のからくりが働いている。 さっそくだが,少し立ち寄ってみよう。
puts が任意の引数 x を印字するとき,その内部では x に属するメソッド (method, C++ 用語でいうメンバ関数) のうち,一般的な文字列表現を戻り値とする x.to_s() を呼び出して,その戻り値を印字している。 一方,p は,オブジェクト x の (可能ならば) より詳しい文字列表現を戻り値とする x.inspect() を呼び出して,その戻り値を印字している。
puts(x) | : | x.to_s() の戻り値を印字する。
|
p(x) | : | x.inspect() の戻り値を印字する。
|
これがうまく働くのは,Ruby のどの値にも (つまり,どのオブジェクトにも) メソッド to_s と inspect が定義されていて,値の型に応じて (つまり,オブジェクトのクラスに応じて) 適切に実装されているからである。 つまり,ここではオブジェクト指向のからくりが持つ 多相性 (polymorphism) が使われている。
このメソッドの振舞は暗記すべき理屈ではない。 今すぐ君の手で試し,君の目で見ることができる事実だ。 文字列のあとにピリオドとメソッド名を続けて to_s と inspect を呼び出してみよう。もちろん,丸括弧は書いても省略してよい。
irb(main):008:0> "hello, world".to_s => "hello, world" irb(main):009:0> "hello, world".inspect => "\"hello, world\"" irb(main):010:0>
ここで二つめの「=>」の先にある戻り値に含まれる \" は, C++ や Java でおなじみのエスケープ列であり,「"」1文字を表す。 つまり,inspect の戻り値は内部的には,1文字ごとに | で区切って書くと |"|h|e|l|l|o|,| |w|o|r|l|d|"| であり, たしかに元々の文字の列を「"」文字で囲んでいることが分かる。
"\r\n".inspect がどんな結果になるか,確かめてみましょう。
整数も試してみよう。 今度は to_s と inspect の両メソッドの結果が同じになる。
irb(main):010:0> 2.to_s => "2" irb(main):011:0> 2.inspect => "2" irb(main):012:0>
プログラムのデバッグ用に,途中経過の値を印字するとき,p を使うと,打鍵数が少なく済み,しかも (可能ならば) より詳細な情報が得られて都合がよい。 逆にいえば,そのような便利さを求めたからこそ,たった1文字の奇妙な関数名が採用されたと言える。 その趣旨から,とりわけ p は引数の丸括弧を省いて使うのが普通である。
"hello, world".to_s や 2.inspect でピリオドの前にある
"hello, world" や 2 は レシーバ (receiver) と呼ばれます。
メソッド呼出しをオブジェクトへの メッセージ (message) とみなしたとき,
これらはそのメッセージの受取人と見ることができるからです。
この用語の背景には Smalltalk の伝統があり,それなりに広く普及しています。
1.4 self と private メソッド
ここまでの説明で,Ruby に普通の「関数」と,オブジェクト指向的な「メソッド」 (メンバ関数) の二つがあるように誤解させてしまったかもしれない。 実際には Ruby にいわゆる普通の関数はない。 すべてがなんらかのクラスに属する メソッド である。 関数のように見える puts にも レシーバ として C++/Java の this にあたる self が暗黙裏に与えられている。
他の構文要素の内側というわけではない トップレベル (top level) にも,self は (まるで空気のように存在感がないけれども,たしかに) 存在している。 君はすでにその文字列表現 "main" をプロンプトの中に見ている。 self はズバリ self という名前でアクセスできる。さっそく確かめてみよう。 C++/Java の this と同様,メソッド呼出しのレシーバが self ならば,それを省略できる。 そのことも確かめてみよう。
irb(main):012:0> self => main irb(main):013:0> self.inspect => "main" irb(main):014:0> inspect => "main" irb(main):015:0> to_s => "main" irb(main):016:0>
ここまでの例から想像できるように,puts や p の関数呼出しは,実は self. を省略したメソッド呼出しにほかならない。 ところが,そこからの当然の予想に反し,これらに陽に self. を接頭することはできない。
irb(main):016:0> self.puts 3
NoMethodError: private method `puts' called for main:Object
from (irb):16
from :0
irb(main):017:0>
これは puts や p が Ruby 独自の用語法でいうところの private メソッドとして定義されているからだ。 上記のエラーメッセージ "private method `puts' called for main:Object" は,private メソッド puts が,Object クラスのインスタンス main をレシーバとして呼び出されたことを咎めている。これは下記の第1項に反する。
- Ruby の private メソッドは,構文的にレシーバを指定できない。
- Ruby の private メソッドは,public メソッドと同じく派生クラス (サブクラス, subclass とも) からアクセスできる。
第1項の帰結として,自分自身,つまり self だけをレシーバとできることが「private」という形容の由来である。 しかし,一般的なオブジェクト指向の用語としての "private" といくらかでも似ている点は,ここまでである。
第2項により,基底クラス (スーパークラス, superclass とも) で用意した private メソッドは, そのすべての派生クラスの文脈で呼び出せることになる。 したがって,もしも仮にすべてのクラスの基底クラスであるような特別なクラスがあるとすれば, そこに private メソッドを用意することにより, (文脈により self の値は変わるけれども) そのメソッドは Ruby のすべての文脈で呼び出せることになる。
さらに,もしも private メソッドの動作が,レシーバである self の値に依存していなければ, そのメソッドは,すべての文脈で同じように振舞うことになる。 つまり,そのメソッドは事実上,いわゆる普通のグローバルな関数と同じになる。 これが p や puts の正体である。
継承に関するクラスどうしの関係も,うるさく言えば,単純な木構造ではありません。 Java と異なり,Smalltalk と類似して, Object は,メタクラス (metaclass) である Class の1インスタンスです。 そして Class は Module の派生クラスであり, Module は Object の派生クラスです。 したがって,その構造はむしろ 循環グラフ です (図としては 5 章の挿し絵の右半分を見てください。 ただし,この図はまだ近似表現です。ある意味では Object よりも基本的であり, ある意味ではそうではない微妙な Kernel モジュールを省略しています)。
しかし,いきなり,はじめからこの種の微妙な細部まで熟知する必要はありません。 さしあたり,Ruby がその単純そうな外見の裏に複雑さを隠していることだけ理解すれば十分です。 本チュートリアルで,あえて真実を垣間見せているのは,後で君が裏切られたと感じないようにするためです。
Ruby についての重要な資料の多くは,Ruby の話者だけに通じる独特な用語法で書かれている。 君が本当に Ruby をマスターするには,遅かれ早かれ,そういった資料にあたる必要があるが, そこに何気なく書いてある用語の定義を読み過ごすと意味がとれなくなる。 さしあたり道に迷わないように,繰返しになるが,次のことをよく銘記しておこう。
- Ruby では private メソッドに派生クラスからアクセスできる。
現在,広く使われているオブジェクト指向言語のなかで,このようなメソッドを private と形容しているのは Ruby だけである。 他の言語の経験と直感に頼って Ruby の奇妙な private を理解しようとしてはいけない。
現在の文脈で利用可能な private メソッドの一覧は self.private_methods で得られる。 p と puts が含まれていることを確認しよう。 きっと君は一覧の中に他の言語で親しんでいた名前をいくつか見つけるはずだ。 各メソッドの解説は, Ruby リファレンスマニュアル の「組み込み関数」のページにある。 名前におぼえがある関数について,Ruby ではどんな関数になっているのか,君自身の目と手で確かめてみよう。
irb(main):017:0> self.private_methods() => ["getc", "rand", "split", "initialize", "fail", "gsub!", "String", "putc", "e xec", "sprintf", "load", "iterator?", "catch", "irb_binding", "proc", "p", "sub" , "syscall", "callcc", "fork", "test", "caller", "Array", "puts", "chop!", "form at", "require", "scan", "lambda", "block_given?", "throw", "warn", "gsub", "loop ", "open", "initialize_copy", "trap", "singleton_method_added", "exit!", "chomp! ", "gets", "autoload", "system", "trace_var", "irb_exit_org", "global_variables" , "remove_instance_variable", "`", "chop", "Integer", "printf", "singleton_metho d_removed", "abort", "readline", "eval", "untrace_var", "sleep", "local_variable s", "select", "srand", "chomp", "raise", "print", "DelegateClass", "sub!", "Floa t", "method_missing", "singleton_method_undefined", "autoload?", "binding", "at_ exit", "readlines", "set_trace_func"] irb(main):018:0>
private_methods.sort
のように打鍵するとよいでしょう。
1.4.1 配列
ここで private_methods メソッドの戻り値がブラケット [ ] で囲まれていることに注意しよう。 これは Ruby の 配列 (Array) の一例である。 Ruby の配列は,大ざっぱにいえば,Java の java.util.ArrayList や C++ の vector に相当する可変長の列である。 君がここで見ているように, inspect メソッドでは,各要素をカンマで区切り,前後をブラケットで囲んだものとして表現される。 君は,この表現と同じ表記方法で配列のオブジェクトを作ることができる。
配列について大体の感じをつかむため,
試しに irb で [1, 2, 3] や ["one", "two", "three"] と打鍵してみよう。
また,それに対して inspect や to_s を呼び出してみよう。
また,p や puts で表示してみよう。
irb(main):001:0> [1, 2, 3] => [1, 2, 3] irb(main):002:0> [1, 2, 3].inspect => "[1, 2, 3]" irb(main):003:0> [1, 2, 3].to_s => "123" irb(main):004:0> ["one", "two", "three"] => ["one", "two", "three"] irb(main):005:0> ["one", "two", "three"].inspect => "[\"one\", \"two\", \"three\"]" irb(main):006:0> ["one", "two", "three"].to_s => "onetwothree" irb(main):007:0> p [1, 2, 3] [1, 2, 3] => nil irb(main):008:0> puts [1, 2, 3] 1 2 3 => nil irb(main):009:0> p ["one", "two", "three"] ["one", "two", "three"] => nil irb(main):010:0> puts ["one", "two", "three"] one two three => nil irb(main):011:0>
1.5 演算子と式と文
Ruby はその外見を伝統的な手続き型言語のように見せるため, おなじみの 単項 前置演算子 や 二項 中置演算子 を採用している。 その実体は,再定義可能なメソッド呼出しか,再定義不可能な制御構造である。
1.methods.sort を実行して,1
という 整数
オブジェクトに四則演算子と同名のメソッドがあることを確かめてみましょう。
優先順位にしたがって演算子を並べると次のようになる。
::
[]
+(単項) ! ~
**
-(単項)
* / %
+ -
<< >>
&
| ^
> >= < <=
<=> == === != =~ !~
&&
||
.. ...
?: (条件式)
= (+=, -= ... )
, =>
not
and or
君が C++ や Java で慣れ親しんできた演算子は Ruby でも概ね同じ意味で通用する。
ただし,!, &&, || と not, and, or は優先順位が異なる。 C++ と異なり,単なる代替つづりではない。 カンマ (,) と => は引数並びの区切りとハッシュ・リテラル (4 章) の区切りである。 厳密には演算子とは言えないが,表の中でこの位置を占めると考えるのが 合理的である。 これらのやや風変わりな仕様は Perl にちなんでいる。
1.2 節 で述べたように,Ruby ではあらゆる文が結果の値をもつ。 したがって,始めと終わりを文括弧などで明確にすることにより, 一つの項として自由に文を式に混ぜることができる。
irb(main):018:0> 1 + if 2 < 3 then p "hi"; 4 else 5 end "hi" => 5 irb(main):019:0> [3, begin i = 3; while i < 10; i += 4 end; i end] => [3, 11] irb(main):020:0> i => 11 irb(main):021:0>
ここで
- begin … end は Java/C++ の { … } に相当する文括弧である。 (2.4 節)
- while 式; 文… end は Java/C++ の while (式) { 文… } に相当する制御構造である。 (2.3 節)
- 制御構造の構文で 改行 は空白と同じではない。 (Unix の B-Shell と同じく) セミコロン と同じである。(2 章 前書き)
である。
念のため,最初の例 「1 + if 2 < 3 then p "hi"; 4 else 5 end」
についてかみ砕いて説明しよう。
強調した部分が irb の出力となっている。
- この式は,
1と if 文 (2.1 節) の結果の和を求めている。 - if 文は,条件
2 < 3が真だから, then 以降のp "hi"; 4の値を結果とする。 - 関数呼出し (厳密に言えば,self
を暗黙のレシーバとするメソッド呼出し)
p "hi"は"hi"を印字して戻り値を nil とする。 - しかし,その戻り値は捨てられ,セミコロンの次の式が評価される。
- 次の式である
4の結果の値,つまり4自身が if 文の結果となる。 - 結局,
1と4の和が求められる。 -
5が得られる。
下記はセミコロンのかわりに改行を使った例である。
irb(main):021:0> 10 + begin irb(main):022:1* i = 3 irb(main):023:1> while i < 10 irb(main):024:2> i += 4 irb(main):025:2> end irb(main):026:1> i irb(main):027:1> end => 21 irb(main):028:0>
ただし,こうした書き方が可能だからといって,こうした書き方をしなければならない, ということでは決してない。 とりわけここに述べた説明用のわざとらしい例のように, 文字列 "hi" を印字したり,変数 i を後に残したりなどと, 文が副作用 (side effect) をもつときは,式と文を区別して 書いたほうが分かりやすい。 つまり,上記はむしろこう書いたほうがよい。
i = 3 while i < 10 i += 4 end 10 + i
しかし,君は,Ruby では式と文を自由に混ぜることができる,という事実を忘れてはならない。 Lisp とも共通する Ruby のこの性質は,これから Ruby を学び,使っていく過程で時折姿を見せるだろう。
1.6 ローカル変数とメソッド定義
特に説明しなかったが,上の例では変数 i の代入と参照をしている。
Ruby は,事前に宣言しなくても 代入するだけ で変数を設けることができる。 君はそうしようと思えば irb を無尽蔵のメモリ機能付き電卓として使うことができる。
ところで,他の構文要素の内部ではない トップレベル で設けたこの変数は,どんな変数だろうか?
C++/Java の常識には反するが,驚くべきことに, Ruby では,この変数は ローカル変数 (local variable) と呼ばれる。 君は疑問に思うだろう。 ローカル変数とは,メソッドとか関数の中だけのローカルな変数のことではなかっただろうか? その前に,そもそも Ruby ではどのようにメソッドを定義するのだろうか?
メソッド定義 (2.5 節) はキーワード def で始まり,end で終わる。 メソッド定義の頭書きではメソッド名と仮引数を宣言できる。
def foo(x) # メソッド foo と仮引数 (ローカル変数の一種) x の宣言 … i = 3 # メソッド foo のローカル変数 i への代入 … end i = 4 # トップレベルのローカル変数 i への代入 (上の i とは無関係)
仮引数は,メソッドに与えられる実引数との対応付けで設定されるローカル変数である。 メソッドの外側からそれを見ることはできない。 メソッド内部で,代入によってローカル変数を設けることもできる。 設けたローカル変数は,仮引数と同じくそのメソッドの呼出しのための変数になる。
Ruby に限らず,一般に,再帰可能なメソッドや関数,手続き (以下,単にメソッド) をもつ言語処理系の内部では, 実行時,メソッド呼出しごとに新しくフレーム (frame) が作られる。 概念的には,フレームとはローカル変数名からその値への写像である。 典型的には,メソッド呼出しごとにスタックに積み上げられ, メソッドから戻るごとに順に下ろされるから,スタック・フレーム (stack frame) と呼ばれる。
メソッドを呼び出したとき,その内部で使われるローカル変数の実体は,その呼出しのために作られたフレームの中に設けられる。 メソッドが自分自身を呼び出したとき,フレームが別々だから,呼出しごとにローカル変数は別々になる。 したがって,再帰的 (recursive) なメソッドを定義した場合でも,期待される通りに実行される。
トップレベルは決してメソッドの内部でも本体でもないが,Ruby はトップレベルにもフレームを作る。
おそらく,その動機は,「普通の変数への代入」を実装するとき,
現在有効なフレームにローカル変数を設ける,という処理に統一したかったのだろう。
結果として,トップレベルで i = 4 のような代入をしたとき,
ローカル変数 (と同じように実装される変数) i がトップレベル用のフレームの中に設けられる。
もちろん,実装方式がローカル変数と同じでも,言葉としてはそぐわないのだから, 呼称としては別の名前を考えるべきだったのかもしれない。 実際には Ruby の設計者はそうしなかったし, その用語法は少なくとも今 Ruby を使っている人々のあいだで支持されている。 だから君は,たとえ奇天烈だと思っても,さしあたり「ローカル変数」という呼び方で納得する必要がある。 結局のところ,トップレベルが仮想的にメソッド内部にあると見なせば, その振舞をよく説明しているのだから,十分に妥当な選択と言える。
Ruby はそうではありません。 Ruby は文法の若干の類似性から Algol 系言語と呼ばれることがありますが,Algol やその代表的な派生言語と異なり,メソッド定義の外側の「ローカル変数」にアクセスできません。
irb(main):001:0> i = 100
=> 100
irb(main):002:0> def foo
irb(main):003:1> puts i
irb(main):004:1> end
=> nil
irb(main):005:0> foo
NameError: undefined local variable or method `i' for main:Object
from (irb):3:in `foo'
from (irb):5
from :0
irb(main):006:0>
同様のことを Algol 60 でしたとき,エラーは起こりません。
$ algol60 -
begin
integer i;
procedure foo;
print(i);
i := 100;
foo
end
^D
100
$
Algol との類似性だけから判断すれば,Ruby は典型的な実装方法を中途半端にまねた粗悪な模造品です。
しかし,Algol のかわりではなく,Ruby
らしくプログラムを組むかぎり,こうした仕様が現実的な問題になることは,まずありません。
広域的に命名された値という役割は,
Ruby では典型的には,定数 と インスタンス変数 が担います
(本物の グローバル変数 もありますが,良い Ruby プログラムではあまり使いません)。
入れ子の関数定義に代わる優れた代替物として
(Algol ではなく Ruby の用語法でいうところの) ブロックを使うことができます。
Ruby は Ruby です。
平凡な例だが,下記は,引数に 1 を足した値を返すメソッドの定義と,その呼出しである。
メソッド本体 (ここでは「n + 1」) の結果の値が,メソッド自体の戻り値となることに注目しよう。
irb(main):001:0> def add1(n) irb(main):002:1> n + 1 irb(main):003:1> end => nil irb(main):004:0> add1(0) => 1 irb(main):005:0> add1(1) => 2 irb(main):006:0> add1(2) => 3 irb(main):007:0> 10 + add1(3) => 14 irb(main):008:0>
1.6.1 さらにメソッド定義について
それほど平凡ではない例として,次のようにトップレベルでメソッドを定義し,それを呼び出してみよう。
irb(main):028:0> def foo(x) irb(main):029:1> if x <= 0 irb(main):030:2> return [] irb(main):031:2> else irb(main):032:2* return [x, foo(x - 1)] irb(main):033:2> end irb(main):034:1> end => nil irb(main):035:0> foo(3) => [3, [2, [1, []]]] irb(main):036:0>
foo は1個の仮引数 x を持つ。 return は C++/Java と同様の意味を持つ。 x がゼロ以下のとき,[] つまり空の配列が戻り値として返される。 そうでないとき,x と foo(x - 1) の値からなる長さ2の配列が 戻り値として返される。 最後に実行した文や式の結果の値が戻り値となるから,この例では return を省略して,式をじかに書いてもよい。 irb の「=> nil」から分かるように,メソッド定義それ自体は戻り値として nil を返す。
上記の定義の [x, foo(x - 1)] を
foo(x - 1).unshift(x) や
foo(x - 1).push(x) に変えて実験してみよう。
unshift と push はどちらも Perl にちなんだ要素追加のメソッドである。
いろいろ変えて実験するときは,関数定義をファイル (例えば foo.rb) に格納して,
それを変更するようにすると手間がかからない。
いろいろな引数を思いつきで試すには,irb を -r オプション付きで実行するとよい。
引数で指定したファイルを実行してから,対話セッションに入る。
$ emacs foo.rb
$ cat foo.rb
def foo(x)
if x <= 0
return []
else
return [x, foo(x - 1)]
end
end
$ irb -r foo.rb
irb(main):001:0> foo(4)
=> [4, [3, [2, [1, []]]]]
irb(main):002:0> foo(0)
=> []
irb(main):003:0> exit
$
階乗関数,tarai 関数,
Ackermann 関数など,君の知っているお気に入りの関数を書いて実行してみよう。
複数の仮引数を宣言するときは,実引数のときと同じようにカンマで区切ればよい。
多重に分岐するときは
if 式; 文… elsif 式; 文… elsif 式; 文… else 文… end
が使える (2.1 節)。
メソッドの名前は大文字で始めても小文字で始めてもよいが,
ローカル変数の名前は (仮引数も含めて) 小文字で始める必要がある (3.5 節)。
$ emacs ackermann.rb
$ cat ackermann.rb
def Ackermann(m, n)
if m == 0
return n + 1
elsif n == 0
return Ackermann(m - 1, 1)
else
return Ackermann(m - 1, Ackermann(m, n - 1))
end
end
$ irb -r ackermann.rb
irb(main):001:0> Ackermann(0, 0)
=> 1
irb(main):002:0> Ackermann(1, 1)
=> 3
irb(main):003:0> Ackermann(2, 3)
=> 9
irb(main):004:0> Ackermann(3, 4)
=> 125
irb(main):005:0> exit
$
1.6.2 トップレベルのメソッド定義の真実
ところで,トップレベルでのメソッド定義は「メソッド」をどのようなものとして定義するのだろうか?
君が予想するとおり,ruby のトップレベルでのメソッド定義は, Object クラスの private メソッドとして「メソッド」を定義する。 より厳密にいえば,Object の private インスタンス・メソッド として定義する。
Ruby では,Smalltalk と同じく,クラスもまた (メタクラスの) インスタンスですから, それ自身に対してもメソッドが定義されています。 インスタンスを生成する new は,Ruby ではそのようなメソッドとして用意されています。
a = Array.new(3) # 長さ 3 の配列を生成する
このようなメソッドはインスタンス・メソッドと区別してクラス・メソッドと呼ばれます。
ただし,メタクラスの立場から見れば,それらは (メタクラスの) インスタンス・メソッドです。
つまり,メタクラスを意識する考え方では,すべてはインスタンス・メソッドです。 一方,メタクラスを意識しない観点では, インスタンス・メソッドとクラス・メソッドの2種類があります。
p や puts に対する説明の繰り返しのようになるが, Object クラスは,(メタクラスも含めて) あらゆるクラスの基底クラスであり, Ruby のあらゆるオブジェクトは例外なく Object クラスのインスタンスである。 したがって,トップレベルで定義したメソッドは, 事実上,グローバルな関数としてどの文脈からも利用できる。
ただし,微妙な違いだが,非対話的インタープリタ ruby と異なり, irb は,その対話的なトップレベルで与えられたメソッドを, Object クラスの public インスタンス・メソッドとして定義する。 private ではなく public である。 実際にはこうなっても使い勝手は変わらない。ただ,レシーバを陽に指定できる点が異なる。
テキスト・ファイルに書いたメソッド定義を ruby に読み込ませたとき, それが確かに Object の private インスタンス・メソッドとして定義されること, それに対し irb で対話的に定義したとき,public インスタンス・メソッドとして定義されることを確かめてみよう。
実用上は決して勧められないが,定義を1行にまとめられることも確認しよう。 機械的に文末の改行をセミコロンにかえていけばよいが,実際にはセミコロンのほとんどは省略できる。 then という構文キーワードで if の条件とそれ以降の間のセミコロンを無くすことができる。
$ cat foo1.rb def foo(x) if x <= 0 then [] else [x, foo(x - 1)] end end p foo(5) puts "Public:" p Object.public_instance_methods.sort puts "Private:" p Object.private_instance_methods.sort $ jruby foo1.rb [5, [4, [3, [2, [1, []]]]]] Public: ["==", "===", "=~", "__id__", "__send__", "class", "clone", "display", "dup", "e ql?", "equal?", "extend", "freeze", "frozen?", "hash", "id", "inspect", "instanc e_eval", "instance_exec", "instance_of?", "instance_variable_defined?", "instanc e_variable_get", "instance_variable_set", "instance_variables", "is_a?", "kind_o f?", "method", "methods", "nil?", "object_id", "private_methods", "protected_met hods", "public_methods", "respond_to?", "send", "singleton_methods", "taint", "t ainted?", "to_a", "to_s", "trap", "type", "untaint"] Private: ["Array", "Float", "Integer", "String", "`", "abort", "at_exit", "autoload", "au toload?", "binding", "block_given?", "callcc", "caller", "catch", "chomp", "chom p!", "chop", "chop!", "eval", "exec", "exit", "exit!", "fail", "foo", "format", "gets", "global_variables", "gsub", "gsub!", "inherited", "initialize", "initial ize_copy", "iterator?", "lambda", "load", "local_variables", "loop", "method_mis sing", "open", "p", "print", "printf", "proc", "putc", "puts", "raise", "rand", "readline", "readlines", "remove_instance_variable", "require", "scan", "select" , "set_trace_func", "singleton_method_added", "singleton_method_removed", "singl eton_method_undefined", "sleep", "split", "sprintf", "srand", "sub", "sub!", "sy stem", "test", "throw", "trace_var", "untrace_var", "warn"] $
$ jirb irb(main):001:0> def foo(x) if x <= 0 then [] else [x, foo(x - 1)] end end => nil irb(main):002:0> foo(5) => [5, [4, [3, [2, [1, []]]]]] irb(main):003:0> Object.public_instance_methods.sort => ["==", "===", "=~", "__id__", "__jtrap", "__send__", "class", "clone", "com", "display", "dup", "eql?", "equal?", "extend", "foo", "freeze", "frozen?", "hash ", "id", "include_class", "inspect", "instance_eval", "instance_exec", "instance _of?", "instance_variable_defined?", "instance_variable_get", "instance_variable _set", "instance_variables", "is_a?", "java", "java_kind_of?", "javax", "kind_of ?", "method", "methods", "nil?", "object_id", "org", "private_methods", "protect ed_methods", "public_methods", "respond_to?", "send", "singleton_methods", "tain t", "tainted?", "to_a", "to_s", "trap", "type", "untaint"] irb(main):004:0>
ここでメソッド定義を1行にまとめてみることを勧めたのは, 実験の便宜と,Ruby の構文規則のより深い理解をねらってのことである。 要領がつかめたら,もう普段はこんなことはしないようにしよう。
Ruby のプログラムは慣習的に 2 文字ずつ空白で字下げ される。
この慣習は実際によく守られているから,君も普段からその書き方に目と手を慣らした方がなにかと有利だろう。
1.6.3 Ruby のための Emacs の設定
ruby-mode が設定された GNU Emacs またはその派生エディタを使うと, ほとんど苦労することなく,慣習どおりに字下げを整えることができる。 おそらく,君が目にする多くの Emacs 環境で ruby-mode が設定済みになっているはずだ。 もしそうでなくても,ruby-mode は ruby のソースに同梱されているから,君自身でインストールすればよい。 いくつか似たようなファイルが同梱されているが,さしあたりプログラムの編集に必要なのは ruby-mode.el である。 これを /usr/share/emacs/site-lisp/ などのローカルな Emacs ライブラリ用ディレクトリに置き,~/.emacs ファイルに下記を追記すればよい。
(autoload 'ruby-mode "ruby-mode" "Ruby editing mode." t)
(nconc auto-mode-alist '(("\\.rb$" . ruby-mode)))
1行目は ruby-mode という Lisp 関数が最初に呼び出されたとき,
"ruby-mode.el" という名前のファイルをライブラリ用のディレクトリから自動的に探し出してロードするようにする。
2行目は,.rb という拡張子のファイルが開かれたとき,ruby-mode という
Lisp 関数を自動的に呼び出すようにする。
Ruby 言語の骨格には Lisp からインスパイアされた要素が多数ある。 Ruby をよりよく理解するために Lisp をかじってみることは,アイディアとして悪くない。 Emacs はそのための手頃で自己完結的な Lisp 学習用ツールとしても有用である。 そのときは, Emacs Lisp てほどき − .emacs を書くための − などが役立つだろう。
1.7 スクリプト例
ここまで述べた言語要素だけでも, 組込み関数等と適切に組み合わせることによって,多くのスクリプトを記述できる。 下記はコマンド行引数として与えられたテキスト・ファイルの内容を 行番号を付けて印字するスクリプトである。
#!/usr/bin/ruby -Ku count = 0 while line = gets count += 1 printf("%6d %s", count, line) end
Unix 上でファイル catn.rb としてセーブしたときの実行例を示す。
$ chmod a+x catn.rb
$ ./catn.rb catn.rb
1 #!/usr/bin/ruby -Ku
2 count = 0
3 while line = gets
4 count += 1
5 printf("%6d %s", count, line)
6 end
$
第1行は,ファイルを Unix で実行可能な utf-8 の Ruby スクリプトにする。
#!/usr/bin/ruby -Ku
スクリプト catn.rb に対し,次のように poi.txt がコマンド行引数として与えられたとき,
$ ./catn.rb poi.txt
第1行の内容とあわせて次のようなコマンド行が自動的に組み立てられて実行される。
/usr/bin/ruby -Ku ./catn.rb poi.txt
このとき,ruby は,マルチバイト文字列を utf-8 文字列と解釈して, ./catn.rb を実行する。 poi.txt は Ruby プログラムへのコマンド行引数として扱われる (ただし,この例では,たまたまマルチバイト文字列を utf-8 として解釈しなければならない箇所がないから,-Ku は省略できる)。
Ruby プログラムにとって # 以降は コメント であり, ./catn.rb の第1行は無視される。 ちなみに,Ruby に限らず Unix 上の多くのスクリプト言語が # 以降をコメント扱いするのは,この #! のメカニズムと相性が良いことが一因である。
#!/usr/bin/ruby -Ku
トップレベルにローカル変数 count を設けてその値を 0 にする。
count = 0
puts と同類の組込み関数 gets は,コマンド行引数をテキスト・ファイルの名前とみなし, 1回呼び出されるごとにファイルの内容を1行ずつ返す。 ファイルの内容が尽きたときは nil を返す。
gets を呼び出して,戻り値をローカル変数 line に代入する。 line は代入によって新しく設けられる。 代入演算の結果の値は,代入された値と等しい。 ファイルの内容が尽きて nil が結果の値となるまで,while の条件は成立する。
while line = gets
自己代入によって count の値を 1 だけ増やす。
count += 1
やはり puts と同類の組込み関数 printf を使って書式付き印字を行う。その仕様は C/C++ の <stdio.h> の printf 関数, Java の java.lang.System.out#printf メソッドと概ね同じである。
line に代入されている1行の内容には,行末の改行文字も含まれているから,
書式文字列で "%6d %s\n" のように改行文字を含めることはしない。
printf("%6d %s", count, line)
1.8 変数の代入とオブジェクトのアイデンティティ
ところで上記の例で
line = gets
をしたとき,実際には何が起こっているのだろうか。 文字列を代入したとき,その内容が1文字ずつ変数 line にずらずらと転送されているのだろうか?
答えは否である。実際には (Java の場合と同じく) 文字列オブジェクトへの参照 (実質的にポインタ) だけが渡されるにすぎない。
このことを実験的に確かめるにはどうしたらよいだろうか?
いろいろ手段があるが,Ruby には直接的な方法が用意されている。 Object の public インスタンス・メソッド object_id (object identity) の戻り値をみればよい。 これはオブジェクトごとに実装依存の一意的な整数を返す。
Ruby は object_id の同義語として __id__ というメソッドも用意しています。
文字列について簡単に実験してみよう。
$ irb irb(main):001:0> a = "a quick brown fox" => "a quick brown fox" irb(main):002:0> a.object_id => 209382 irb(main):003:0> b = a => "a quick brown fox" irb(main):004:0> b.object_id => 209382 irb(main):005:0> a += " jumps over" => "a quick brown fox jumps over" irb(main):006:0> b => "a quick brown fox" irb(main):007:0> a.object_id => 193772 irb(main):008:0> b.object_id => 209382 irb(main):009:0>
この結果から分かるように,代入 b = a により,二つの変数は同じオブジェクトを指す。
a += " jumps over" により,a は文字列を連結して作られた新しい文字列を指す。
object_id は文字列に限らず,整数にも配列にも使える。いろいろ実験してみよう。
Java などと同じく Ruby では,文字列や配列を変数に代入したり,実引数から仮引数へと渡したり,戻り値として返したりするとき, 参照だけが効率良く渡される。
プログラミングにとって,このような参照渡しは,値の変更の影響範囲を考えるうえで重要である。 Java などと異なり Ruby の文字列は,普通の配列のようにブラケットで添え字を指定して,要素を参照したり,変更したりできる。 この機能をつかって文字列の一部を変更してみよう。
$ irb irb(main):001:0> a = "a quick brown fox" => "a quick brown fox" irb(main):002:0> b = a => "a quick brown fox" irb(main):003:0> a[2] = "Q" => "Q" irb(main):004:0> a => "a Quick brown fox" irb(main):005:0> b => "a Quick brown fox" irb(main):006:0>
代入 b = a によって a と b は同じ文字列オブジェクトを指しているから,
a[2] = "Q" で文字列の (0 から数えて) 2 番目の文字を書き換えたとき,b からもその変更をみることができる。
配列についても同様に実験してみよう。
a = "abc"; a.concat("def") のように使います。
+ 演算と似ていますが,新しいオブジェクトを作るかわりにレシーバ自身を変更します。
これを使ったとき,値の変更はどこまで及ぶでしょうか?
まとめ
ここでは処理系の基本的な使い方と,Ruby 言語のごく基礎的な部分をひととおり導入した。
もしも,君が初心者で,ただ読み流しただけでここまでたどり着いたのならば, おそらく無味乾燥な知識の断片の集積だったように感じているだろう。 自分の手で irb を通して Ruby と対話すること, 書かれているとおりの入力を繰り返すだけでなく, こうしたらどうなるだろう,と思いついたことを自ら積極的に試してみることが大切である。