[[プログラミング言語/Ruby]] #contents #br *Ruby Minumums [#e948fe18] Rubyは行指向型言語です。つまり、改行が一文の区切りとなります。 文末記号としてセミコロン';'も使えるのですが、一行に複数の文を書きたいのでもない限り必要ありませんし、推奨されてもいません。rubyのパーザはとかく巨大で複雑なので、少しでも負担を減らしてあげましょう。 class NoVipError < StandardError ; end ↑セミコロンで文を区切る頻出例。中身のない例外クラスの定義。 なお、Cと同様に、バックスラッシュを打つことで行を継続させることができますし、メソッド呼び出しの引数も複数行にわたって書くことができます。 どこでなにが複数行にわたっての記述が可能か、ということはおおむねユーザの直感にそぐうように決められているので、問題はないでしょう(CやPerlプログラマなら、この直感はより鋭くなるでしょう)。 **コメント [#k72682c4] #行末までコメントだぜ シェルスクリプトやPerlと同じです。 =begin RDを書くところ。 =end このように、「'=begin'」のみの行から「'=end'」のみの行 までの間もコメントとして処理系には無視される。 クラスやメソッドの説明をRD(Ruby Document)形式で 書くために使われたりする。 **変数 [#m9b7f45e] Rubyの変数には型がなく、宣言は初回の代入と同時に 行われます(例外あり)。 Javaな人は「Objectクラスへの参照」型が唯一の型だ、 と考えてもいいかもしれません。Rubyの値はすべてオブジェクト であり、Objectクラスをスーパークラスに持ちますから。 Rubyの変数には数種類ありますが、その種類はPerlのファニー文字の ようにプレフィクスに左右されます。 :ローカル変数 -- local_variable| 普通に英数字・アンダーバーで名前をつけます。 宣言は代入と同時で、一度も代入していない変数名を参照すると叱られます。 なお、その場合、引数なし・カッコ省略メソッド呼び出しと区別が付かないので、rubyはそのように叱ります。 :グローバル変数 -- $global_variable| 危険なアイツ。 「$」を先頭に付けます。一度も代入せずに参照するとnilを返します。そういう意味でも危険です (「$var」と間違って「$val」とタイプしてもエラーになりません)。 自分で作った変数ならば、「「$」が先頭 = グローバル変数」なのですが、組み込み変数の中には例外があるので注意が必要です。 :定数 -- Constant| Cの感覚な人は、たとえばArrayオブジェクトが入った定数Arrに向かって Arr.push("hoge") とかで内容を変えられることに困惑するかもしれません。 この場合、定数として変更不可能なのは「参照先」です。「参照先のオブジェクトの内容に関する変更の禁止」は、また別の方法があります。 大文字から名前が始まるとそれは定数とされ、constやfinalのような特別な修飾はありません。宣言(つまり代入)されていない定数を参照するとエラーになります。 :インスタンス変数 -- @instance_variable| オブジェクトの状態。 「@」から始まり、宣言なしに参照するとnilを返します。 C++やJavaなひとはメンバ変数のことだと思ってください。 ただし可視性は設定できず、例外なくクラスの外からはアクセスできません。セッタ・ゲッタを定義する必要があります。 :クラス変数 -- @@class_variable| 同じクラスのインスタンスに共通の変数。 「@@」から始まり、宣言なしに参照するとエラーになります。 Javaな人はstaticなメンバと似たようなものだと思ってください。 **リテラル [#xae3e799] Rubyのリテラル表現は豊富です。以下に代表的なものを挙げます。 ***数値リテラル(Numeric) [#s7ad24b5] |123 |整数| |4567.89 |実数| |?a |aの文字コード| ***文字列リテラル(String) [#v8f805f4] |"double_quote" |式展開などが解釈されます| |'single_quote' |書いたままになります| |`back_quote` |コマンドとして実行し、出力を値とします| ■□式展開 val = 123 puts "put #{val} value" 上のコードの出力は put 123 value となります。ダブルクォート文字列中に#{ ... }で囲った Rubyの式を書くと、それの値を文字列で表現して埋め込みます。 ***配列リテラル(Array) [#x08adefd] [12, "abc", 123.45]~ ... コンマで区切って要素を指定します。もちろん一つも要素を指定 しない(`[]'; 空の配列)ことも可能です。 ***ハッシュリテラル(Hash) [#k6377b8b] { "key" => "value", "KEY" => 123 }~ ... コンマで区切ってキーと値のペアを指定します。 ***シンボルリテラル(Symbol) [#y309f60b] :symbol ... symbolというSymbolオブジェクトを生成します。 **ブロック付きメソッド呼び出し [#t9f4cd37] メソッドに引数としてオブジェクトを渡すのみならず、処理のカタマリを渡すことができます。 method_name(arg, ...) do |x| ... end method_name(arg, ...){|x| ... } のように記述すると、method_name内に処理を引き渡せます。 いわゆるクロージャ、無名関数あたりのことだと思ってもらえると。 def method_name(arg, ...) ... yield(x) ... end 引き渡した処理は、yield(x)のように、必要ならば引数を与えてメソッド内で呼び出せます。 イテレータなども、この機能によって実装されています。 ■[少しわかる人へ] 要はレキシカルクロージャっぽいものを1つだけメソッドに与えてやる 機能が文法として用意されてると思ってもらえれば。 **Rubyの真偽評価 [#jbb0c75c] 以下に述べる条件分岐などで、Rubyの値(=オブシェクト)がどの ように真偽評価されるかを説明します。 結論から言うと、Rubyにおける偽とはfalseとnilであり、 それ以外のあらゆるオブジェクトは真です。 明示的に真を表す代表的な値にtrueがあります。 これら(true, false, nil)は%%予約語ではなく%%(嘘です。予約語です。が) それぞれTrueClass, FalseClass, NilClassの唯一のインスタンスです。 **制御構造 [#bd2afe6f] ***条件分岐 [#f3190124] ■if文, if修飾子 if expr then ... ... if expr if expr then ... end exprを評価して真ならば、ブロック内を実行する。 最後のブロック形式の書き方では、thenは省略可能。 if expr1 then ... #(a) elsif expr2 then ... #(b) else ... #(c) end expr1を評価して真ならば(a)部を実行して終わりますが、 そうでない場合、expr2の評価に移ります。 expr2の評価が真ならば(b)を実行し、 それもまた偽ならば(c)を実行します。 elsifはいくつでも使えますし、 もちろんelsifなしでいきなりelseを書いてもOKです。 ■unless文, unless修飾子 ifに似た文法をとりますが、unlessは条件式の評価基準が逆です。 つまり、 ... unless expr は、exprの評価が偽のときだけ...を実行しますし、 unless expr then ... (d) else ... (e) end は、exprの評価が真ならば(d)部を実行し、 そうでなければ(e)部を実行します。 ただ、ifにおけるelsifに相当する文はありません。 **繰り返し [#pfda227d] Rubyにおける繰り返し(ループ)のための文とは、 - while - until - for - イテレータ です。以下これら総称してただ単にループと呼びます。 ■while文 while cond do ... end condの評価が真である限り、ブロック内を実行します。 doは省略可能です。 ■until文 until cond do ... end は、if-unlessの関係と同じで、condの評価が偽である限り、 ブロック内を実行します。 ■for文 for x in list ... end Rubyのfor文はCなどのものと違い、シェルスクリプトやPerlのforeachに近いです。これは、配列のように複数の要素の集合であるlistに対して、その各要素をxに取り出して順次処理していくときに使われます。 (listの基準 : listのクラスがCollectionだった場合、Collection#eachというメソッドが定義されている必要があります。これはイテレータというやつです。Rubyのforは内部的にこれを利用しています) ■イテレータ 例:Array#each array = [1, 2, 3, 4, 5] array.each do |x| p x #=>1, 2, 3, 4, 5 end ブロック付きメソッド呼び出しを利用して、 わざわざカウンタ用の変数を用意したり、脱出条件を書かなくても全要素 に対して同じ処理を繰り返せます。 loop do ... end のように、一見制御構造のようなイテレータから、 n = 1 n.upto(10) do |x| p x #=>1, 2, 3, ..., 9, 10 end のように、用途が特化した便利なイテレータもあります。 ■break, next, redo, retry ループ内でのフロー制御文です。 :break|現在のループから脱出します。 :next|現在のループの、現在の回の残りの文を飛ばして、次の繰り返しに移ります。 :redo|現在のループの、現在の回の繰り返しをまた初めからやり直します。 :retry|現在のループを始めからやり直します。 **例外処理 [#q496224f] begin ... #(a) rescue ExClass => ex ... #(b) else ... #(c) ensure ... #(d) end begin ... endブロック内(の、(a)部)で起きた例外を、 rescueで捕捉します。 rescueのExClassには捕捉する例外クラスを指定し、 => ex で、exという変数に捕捉した例外オブジェクトを 代入します。どちらも省略可能で、ExClassを省略した場合は RuntimeErrorクラスを指定したとみなされます。 指定した例外クラスと、その全てのサブクラスのオブジェクトを 捕捉します。 elseは1つ以上のrescueと組で、例外が発生しなかった場合、 (c)部を実行します。 ensure以下の(d)部は、例外が捕捉されようがされまいが 実行する文を記述します。 もちろん、rescue、else、ensureは省略可能です。 …が、rescueの一つも書かずにbegin ... endでくくっても しょうがないんですが。 **クラス定義 [#pb39a7b7] せっかくの純オブジェクト指向言語ですから、クラスの作り方をば。 class ClassName < SuperClassName ... end 「< SuperClassName」は名の通り継承するクラスを指定します。省略すると自動的にObjectクラスを継承します。 ClassNameは大文字で始める必要があります。定数と同じです(というか、まんま定数なのですが)。 定義したクラスのインスタンスはobj = ClassName.new()のカタチで作れます。カタチを見て判るとおり、newは予約語ではなくメソッド名で、普通に変数名とかにも使えます。 生成したクラスを初期化したい場合、C++やJavaでいうコンストラクタのようなものを定義します。class ... end内で定義されるinitialize()というメソッドがそれです。newは自分の引数をinitializeに丸投げします。 クラスと似たようなものにモジュールがあります。 module ModuleName ... end モジュールはクラスと違って継承できず、インスタンスも持てません。 しかし、include ModuleNameによってそのモジュールで定義した機能をクラス定義中に取り込むことができます(Mix-In)。 ■アクセス制御 class ClassName public ... #(A) private ... #(B) protected ... #(C) end (A)で定義したメソッドはクラス定義の外から(つまりそのクラスのインスタンスをレシーバにして)呼び出せます。 (B)で定義されたメソッドはクラス定義内からしか呼び出せません。 (C)で定義されたメソッドですが、C++やJavaのprotectedとは意味合いが違い、自分のクラスとそのサブクラスからだけ呼べるメソッドになります。 ■attr系メソッド -- アクセサ定義 Rubyオブジェクトのインスタンス変数は外からはアクセスできません。がセッタとゲッタを定義することでその問題を回避します。 その場合、set_member(val)、get_member()のような名前で定義するのはRubyでは一般的ではありません。member=(val)、member()のように定義して、あたかもメンバやらプロパティへの代入や参照のように見せかけることができます。インスタンス変数名とメソッド名が衝突しないからです。 ただ、外からアクセスする必要があるインスタンス変数全てに、いちいち def member() @member end def member=(val) @member = val end などと記述しているのは無駄すぎます。そこで、上記のような定義を行うことを請け負ってくれるメソッドがあります。 - attr_reader ..... 前者(ゲッタ)を定義 - attr_writer ..... 後者(セッタ)を定義 - attr_accessor ... 両方を定義