情報学部 菅沼ホーム 目次 索引

Ruby 概説

  Ruby は,Perl の後継を目指したオブジェクト指向プログラミング言語であり,以下に示すような特徴を持っています.しかし,かなり問題のある言語だと思います.プログラムを実行するのはコンピュータですが,それを書くのは人間です.従って,プログラムを書くことにとって,最も重要なことは,「読みやすいこと,理解しやすいこと」です.プログラムを記述した言語に対してあまり知識がない人であっても,読めばある程度理解できることが必要です.その点,Perl は,知っている人にとっては書きやすい言語かもしれませんが,知らない人にとっては非常に読みにく,最悪の言語です.Ruby は,読みやすさの点における改善はなされていますが,Perl の悪い箇所をそのまま継承しているように思えます.例えば,記述を簡単にすることを目的に,$_ などの意味のない記号に対して特別の意味を持たせる,基本的な仕様で十分記述できる内容を仕様の複雑化によって簡略化を図っている,など,納得できないことが多いと思います.そのような意味から,Ruby を使用することはあまり推奨できませんが,使用する場合には,できる限りわかりやすさに重点を置いて記述してもらいたいと思います.

  1. Ruby の実行

    1. 対話モード

        コマンドプロンプト上で,irb と入力すると,以下に示すように,Ruby の式をキーボードから簡単に入力/実行できます.また,quit と入力することによって,会話モードを終了できます.
      c:\tmp>irb
      irb(main):001:0> a = 10
      => 10
      irb(main):002:0> 10
      => 10
      irb(main):003:0> p a+10
      20
      => 20
      irb(main):004:0> quit
      
      c:\tmp>		

    2. ファイルの利用

        Ruby インタプリタを起動して,ファイル( programfile )に記述された Ruby のプログラムを実行するためには,コマンドプロンプト上で,以下のコマンドを入力します.
      ruby [ option ] [ programfile ] [ argument ]
      	# 例えば,ruby test.rb				
      ここで,
      option : インタプリータに対するオプション
      programfile : プログラムファイル
      argument : プログラムに対する引数				
      とします.

  2. データ型

    1. リテラル

        数字の 10 や文字列 "hello world" のように Ruby のプログラムの中に直接記述できる値のことをリテラルといいます.Ruby においては,リテラルもオブジェクトです.C/C++ における定数に相当します.

      1. 数値リテラル

          数値において,整数や ?a などは,Integer クラス のサブクラスである Fixnum クラスのインスタンスです.ただし,整数の大きさが Fixnum によって表現できる範囲を超えると Bignum クラスのインスタンスとして扱われます.また,浮動小数点数は,Float クラスのインスタンスです.数値リテラルは,以下のように記述しますが,? で始まる箇所を除いて,C/C++ においても同様です.

        • 123,0d123  整数
        • -123  符号つき整数
        • 123.45  浮動小数点数.ピリオド( . )で始まる浮動小数点数は許されません.0.1 などと書く必要があります.
        • 1.2e-3  浮動小数点数
        • 0xffff,0xFFFF  16 進整数
        • 0377,0o377  8 進整数
        • 0b1011  2 進整数

      2. 文字列リテラル

          文字列リテラルは,ダブルクォート " ),または,シングルクォート ' )で囲んで表現し,String クラスのインスタンスです.ダブルクォートで囲まれた文字列では,後に述べる,バックスラッシュ記法式展開が有効になります.シングルクォートで囲まれた文字列では,\\ (バックスラッシュそのもの)と \' (シングルクォート),行末の \ (改行を無視)を除いて文字列の中身の解釈は行われません.C++ における string クラスを利用した文字列の扱いと似ていますが,多少異なっています.特に,C++ においては,式展開の機能はありません.

          文字列を複数行にわたって書くこともできます.この場合,含まれる改行文字は常に \n になります.また,空白を間に挟んだ文字列リテラルは,1 つの文字列リテラルと見倣されます.文字列の結合は,+ 演算子によって可能ですので,以下の 2 つの式は,同じ結果 abcefg になります.
        "abc" + "efg\n"
        "abc" "efg\n"					
          バッククォート ` )で囲まれた文字列は,ダブルクォートで囲まれた文字列と同様に,バックスラッシュ記法の解釈と式展開が行なわれた後,コマンドとして実行され,その標準出力が文字列として与えられます.コマンドの終了ステータスを得るには,組み込み変数 $? を参照します(このような仕様は許すべきではない).次に示すプログラムは,「 ls -l 」コマンドを実行し,その結果を変数 y に代入しています.このプログラムを実行すると,次のような結果が得られます.C/C++ における system 関数の仕様に相当します.
        #! ruby -Ks
        y = ` ls -l `
        printf "%s\n", y
        printf "終了ステータス %d\n", $?					
        (出力)
        total 1
        -rwxr-xr-x   1 1005     everyone       44 Jun  8 13:45 test.rb
        
        終了ステータス 0					

    2. 変数

        Ruby においては,代入または入力したデータによって,その変数の型が決まります.例えば,Ruby においては,
      a = 10
      a = "abc"				
      のような記述が許され,最後に代入されたデータがその変数に記憶されます.変数に対してその型を宣言しなくてはならないという規則は,非常に面倒なように感じられるかと思いますが,他の場所で別の目的に使用していた変数の値を誤って変更してしまったなどの誤りを防いでくれます.特に,複数の人で作成する大規模なプログラムの場合,同じ変数名を使用しないように管理することはほとんど不可能です.この様な点からも,Ruby のようなインタプリータ言語は,大規模なプログラム作成には不向きです.

        C/C++ における変数の有効範囲(スコープ)は,変数の型宣言された位置や型修飾子によって決まりますが,Ruby における変数の有効範囲は,変数名の最初の 1 文字によって,ローカル変数インスタンス変数クラス変数グローバル変数定数のいずれかに区別されます.2 文字目以降は英数字,または,_ であり,変数名の長さにはメモリのサイズ以外の制限はありません.一般に,識別子(変数名,クラス名,メソッド名など)として,予約語キーワード)を使用することはできません.

        小文字,または,_ で始まる識別子はローカル変数,または,メソッド呼び出しです.ローカル変数の有効範囲(スコープ)は,その変数を宣言した位置から,その変数が宣言されたブロックメソッド,または,クラスモジュールの終りまでです.また,$ で始まる変数はグローバル変数であり,プログラムのどこからでも参照できます.

        アルファベット大文字( [ A - Z ] )で始まる識別子は定数です.定数の定義は代入によって行われますが,メソッドの中では定義できません.また,一度定義された定数に再び代入を行おうとすると,警告メッセージが出ます(代入は可能です).定数は,その定数が定義されたクラス/モジュールの中(メソッド本体やネストしたクラス/モジュール定義中を含みます),クラスを継承しているクラス,モジュールをインクルードしているクラス,または,モジュールから参照することができます.C/C++ における const 指定された変数に似ています.
      conts int x = 10;   // 変数 x の値を,以後,修正できない				
        @ で始まる変数は,インスタンス変数であり,特定のクラスのオブジェクトに所属しています.インスタンス変数はそのクラスまたはサブクラスのメソッドから参照できます.インスタンス変数は,クラスから生成されたインスタンス(オブジェクト)毎に別の値を持つことができるため,各オブジェクトの状態を表すためなどに使用されます.このように,インスタンス変数は,C/C++ における protected 指定されたメンバー変数と似た働きをします.従って,そのままでは,クラスの外部から参照することはできません.参照可能にするために,Module クラスのメソッド attr を使用します.@@ で始まる変数はクラス変数と呼びます.クラス変数は,クラス定義の中で定義され,そのクラス,及び,そのサブクラスから参照できます

  3. 演算子

    1. 演算子の種類

        Ruby には以下にあげる演算子があります.最初にあげた演算子ほど優先順位が高い演算子です.(*) マークをしたものを除き,C/C++ にも同じような演算子が存在しますが,演算子によっては,多少その機能が異なります.ただし,インクリメント演算子( ++ ),デクリメント演算子( -- )が存在しない点に注意してください.
      演算子 使用目的
      :: クラス内部の定数などの参照  例: Test::Const
      [] 配列要素などの参照  例: x[2]
      +(単項) 数値の符号
      ! 論理否定
      ~ ビット演算子(否定)
      **(*) べき乗  例: 2**0.5
      -(単項) 数値の符号
      * 乗算など
      / 除算,整数どうしの場合は小数点以下切り捨て
      % 余り演算など,浮動小数点どうしの演算も可能
      + 加算,文字列の連結など
      - 減算など
      << ビット演算子(左シフト)など
      >> ビット演算子(右シフト)など
      & ビット演算子(論理積)など
      | ビット演算子(論理和)など
      ^ ビット演算子(排他的論理和)など
      > 比較演算子  例: a > b( a は b より大)
      >= 比較演算子  例: a >= b( a は b 以上)
      < 比較演算子  例: a < b( a は b より小)
      <= 比較演算子  例: a <= b( a は b 以下)
      <=>(*) 比較を行い,正,0,負の判定など
      == 比較演算子  例: a == b( a と b は等しい?)
      ===(*) クラス又はそのサブクラスのインスタンスであるか否かの比較など
      != 比較演算子  例: a != b( a と b は等しくない?)
      =~(*) 正規表現とのマッチ
      !~(*) 正規表現にマッチする場合に false,マッチしない場合に true
      && 論理積
      || 論理和
      ..(*) 範囲式
      ...(*) 範囲式
      ?: 条件演算子
      = 代入演算子
      not 論理否定( ! )
      and 論理積( && )
      or 論理和( || )
    2. 代入演算子

        例えば,
      x = x + 5				
      のような式は
      x += 5				
      のように表現することができます.このような代入を,自己代入と呼びます( C/C++ にも同様の機能).自己代入が可能な演算子には以下に示すようなものがあります.
      +, -, *, /, %, **, &, |, ^, <<, >>, &&, ||				

      プログラム例 2.1] 簡単なプログラム

        ここまでの学習を終えたところで,2 つのデータを与えると,乗算と除算の結果を出力するという簡単なプログラムを書いてみましょう.Ruby では,整数のような基本的なデータ型をはじめとして,全てのデータをオブジェクトとして統一的に取り扱います.Ruby のプログラムを書くことは,Object クラスを継承したクラスの内容を記述することに相当します.

        プログラムは式を並べたものであり,式と式の間はセミコロン( ; ),または,改行で区切ります.ただし,バックスラッシュ \ )に続く改行は文の区切りにならず,次の行へ継続します.従って,1 行に 1 つの式を記述する場合は,C/C++ とは異なり,必ずしもセミコロンを必要としません.

      01	############################
      02	# 2つのデータの乗算と除算 #
      03	#      coded by Y.Suganuma #
      04	############################
      05				# データの入力
      06	printf "2つのデータを入力して下さい ";
      07	#print "2つのデータを入力して下さい ";
      08	str = gets().strip();   # gets でも良い(以下の関数も同様);
      09	a   = str.split(" ");
      10	x   = Integer(a[0]);
      11	y   = Integer(a[1]);
      12				# 和と差の計算
      13	mul = x * y;
      14	div = x / y;
      15				# 結果の出力
      16	printf "乗算=%d 除算=%d\n", mul, div;
      17	#print "乗算=", mul, " 除算=", div, "\n";
      				
      01 行目~ 04 行目

        プログラム全体に対する注釈コメント)です.Ruby の注釈は,この例のように, # から行末までという形で表現します.複数行にわたるコメントは,以下に示すような形式でも表現できます.
      	=begin
      	  2つのデータの乗算と除算
      	       coded by Y.Suganuma
      	=end					
        注釈は,プログラムの該当する部分における処理内容を説明するのに使用され,人間がプログラムを読む際,理解しやすくするためのものであり,コンパイラ等によって特別な処理はされません.プログラムを実行するのはコンピュータですが,プログラムを書いたり修正したりするのは人間です.従って,プログラムを書く際に最も注意すべきことは,いかに読み易く,かつ,分かり易いプログラムを書くかという点です.できるだけ多くの注釈を入れておいて下さい.そのことにより,他の人がプログラムを理解しやすくなると共に,プログラム上のエラーも少なくなるはずです.

      06 行目,16 行目

        2 つのデータを入力するためだけであれば,08 行目~ 11 行目だけで十分ですが,その場合,何のメッセージも出力されずキーボードからの入力待ちになってしまうため,一見コンピュータが止まってしまったように感じます.また,多くの入力を要求するような場合は,どの入力を要求しているのかが分かりません.そのため,06 行目の組み込み関数 printf が使用されています.また,16 行目は,結果を出力するための printf です.なお,これらの文においては,一つの文の終わりにセミコロン ; 」が付加してありますが,改行だけで文の終わりを示しても構いません.

        標準入出力装置(一般的には,キーボードとディスプレイ)に対して入出力を行う場合,対象とするものは文字列です.キーボードから入力する場合,たとえ数値であっても,2 進数を使用したコンピュータの内部表現ではなく,例えば 123.45 のように,我々が読むことができる文字列として入力します.しかし,数値データを,入力された文字列としてそのまま記憶したならば,演算等を行うことができません.そのため,入力された文字列をコンピュータ内部で使用する表現方法に変換して記憶してやる必要があります.例えば,123.45 という文字列を数値として扱いたければ,浮動小数点表現に変換して記憶しておく必要があります.また,ディスプレイに出力する場合も,メモリ(変数)に記憶されているデータを文字列に変換して出力します.さもなければ,我々は,0 と 1 の並びとして出力される結果を自分自身で解釈しなければなりません.printf を使用する場合,C++ における printf と同様,記憶されたデータを文字列に変換する操作を指定してやる必要があります.例えば,以下のようにして使用します.

        関数は,外部から与えられたデータに基づき,何らかの処理を行いその結果を返します.関数にデータを与える方法の一つが引数です(以下の例において,printf の後ろに書かれた部分).なお,Ruby においては,関数を呼び出す場合,
      	printf("結果は %f %10.3f %d %5d %s\n", d_data, d_data, i_data, i_data, c_data)					
      のように引数の部分を括弧で囲んでも,次の例のように,括弧を使用しなくても,いずれの方法でも構いません.
      	d_data = -12.34456
      	i_data = -12
      	c_data = "abc";
      	printf "結果は %f %10.3f %d %5d %s\n", d_data, d_data, i_data, i_data, c_data					
        上のプログラムを実行すると,まず,"結果は" という文字列が出力されます.printf においては,% で始まる文字列とエスケープシーケンス(後述)以外は,記述された内容がそのまま出力されます.次に,以下の順序で 5 つのデータが出力されます(各データ間には,1 つのスペースが入る).

      1. %f は 浮動小数点型のデータを固定小数点表現の文字列に変換します.なお,1.23x10-3 のような表現方法に対応する 1.23e-03 という方法-浮動小数点表現-も存在します( %e ). % と f の間に何も記述しなければ,全体の桁数や小数点以下の桁数がシステムの標準形式に従います.この場合,d_data の内容が,システムの標準形式に従って出力されます.

      2. 最初のデータと変換方法は同じですが,この場合は,出力形式を指定しています.d_data の内容が,小数点以下 3 桁,全体の桁数 10 桁で出力されます(例: △△△-12.345 ).桁数が 10 桁に満たない場合は,左側にスペースが挿入されます.全体の桁数を指定しない場合は,10 の部分を省略しても構いません.

      3. %d は 整数型のデータを文字列に変換します.% と d の間に何も記述しなければ,システムの標準形式に従って出力されます.この場合,i_data の内容が,システムの標準形式に従って出力されます.

      4. 上の i_data と変換方法は同じですが,この場合は,出力形式を指定しています.i_data の内容が,全体の桁数 5 桁で出力されます(例: △△-12 ).桁数が 5 桁に満たない場合は,左側にスペースが挿入されます.

      5. c_data の内容が文字列として出力されます.なお,1 文字に対する入出力には,「%c」を使用します.

        以上の説明に従うと,06 行目の printf は,入力を促すメッセージを出力するだけです.16 行目では,mul と div の値を出力しています.16 行目の " の中の最後の記号 \n も 1 文字を表し,そのまま出力されますが, \ の付いた記号は,エスケープシーケンスといって,特別な働きをします.例えば, \n が出力されると改行が行われます.また,13 行目,14 行目を省略し,16 行目の mul と div の代わりに,(x * y),(x / y) という式を記述することも可能です.

      07 行目,17 行目

        06 行目,16 行目の代わりに,これらの行を使用することもできます.print においては,カンマで区切られたデータが順番に出力されます.その際,mul や div の値が自動的に文字列に変換されます.

      08 行目

        組み込み関数 gets を利用して,標準入力から 1 行分のデータを文字列として入力し,変数 str に記憶しています.Ruby のようなインタプリタでは,C++ のような型宣言を行う必要がありません.変数にデータを代入することによって,その変数の型が決まります.変数 str には,文字列が代入されていますので,str の型は文字列型になります.なお,strip は,文字列の前後にある空白や改行を取り除くための関数です.

      09 行目

        08 行目において,2 つのデータを半角スペースで区切った 1 行分のデータが,文字列として変数 str に記憶されます.そこで,これらのデータを分離してやる必要があります.split は,String クラスの関数であり,指定した区切り文字で文字列を分割し,配列に入れて返します.ここでは,a[0] と a[1] に入力した 2 つのデータが文字列として入ります.

      10 行目~ 11 行目

        組み込み関数 Integer を利用して,文字列を整数に変換しています.なお,Ruby においては,整数値の大きさに制限はありません.

      13 行目~ 14 行目

        これらの文では,見て明らかなように,乗算と除算の計算をし,結果を変数 mul と div に代入しています.変数 x,及び,y に対しては,それらの値が参照されているだけですので,10 行目~ 11 行目で記憶された値がそのまま保たれています.なお,2 番目( y )の値として 0 を入力しない( 0 で割ることになる)ようにしてください.

        このプログラムを実行し,8 と 3 を,
      2つのデータを入力して下さい 8 3					
      のように入力すると(下線部が入力する部分),以下のような結果が得られます.除算において,結果の小数点以下が切り捨てられていることに注意してください.なお,x または y が浮動小数点数の場合,17 行目の出力文を使用すると,小数点以下も出力されます.
      乗算=24 除算=2					

    3. 範囲式
    4.   範囲式は,演算子 ..,または,... を使用して生成され,Range クラスのインスタンスです... 演算子によって生成された範囲オブジェクトは終端を含み,... 演算子によって生成された範囲オブジェクトは終端を含みません.

    5. 条件演算子

        条件演算子は,
      式1 ? 式2 : 式3				
      のように記述され 式1 が真であれば 式2,偽であれば 式3 を返します.例えば,
      a = (b > 0) ? 1 : 2				
      のように記述した場合,b が正の時は 1,0 以下の時は 2 が,変数 a に代入されます.

  4. 制御文

    1. 分岐

      • if 文
        if 式1 [then]
        	式2
        [elsif 式3 [then]
        	式4 ]
        ・・・
        [else
        	式5 ]
        end					

    2. 繰り返し

      • while 文
      • while 式1 [do]
        	式2
        end					

      • for 文
      • for ループ変数 in 式1 [do]
        	式2
        end					

      • break 文
      • break					
          もっとも内側のループを脱出します.
        for i in Array[1, 5, -1, 6, 7]
        	if i < 0
        		break
        	end
        	printf "%d ", i
        end
        printf "\n"					

      • next 文
      • next					
          もっとも内側のループの次の繰り返しにジャンプします.C/C++ における continue に相当します.
        for i in Array[1, 5, -1, 6, 7]
        	if i < 0
        		next
        	end
        	printf "%d ", i
        end
        printf "\n"					

      • redo 文
      • redo					
          ループ条件のチェックを行なわず,現在の繰り返しをやり直します.next や redo のような機能を設ける必要がありますか? continue だけで十分ではないでしょうか.誤りの原因になりかねません.
        sw = 0
        for i in Array[1, 5, -1, 6, 7]
        	if i < 0 && sw == 0
        		sw = 1
        		redo
        	end
        	printf "%d ", i
        end
        printf "\n"					

  5. 配列

    1. 1 次元配列

        配列は,複合データ型の一種であり,複数のデータを処理する場合に利用されます.Ruby においては Array クラスのインスタンスとして定義されます.C/C++ における配列とは異なり,ほぼ,C++ 標準ライブラリ内の vector クラスに相当します.Ruby において,例えば,
      x1 = Array[1, 2.3, "abc"];   # x1 = [1, 2.3, "abc"]; でも可
      p x1
      x2 = Array.new(3)
      x2[0] = 1
      x2[1] = 2.3
      x2[2] = "abc"
      p x2
      x3 = Array.new()
      x3.push(1)
      x3.push(2.3)
      x3.push("abc")
      p x3
      				
      のように宣言すれば,x1,x2,及び,x3 は,3 つのデータを記憶できる領域が確保され,各要素に記憶される値が「 1,2.3,"abc" 」で初期設定されます.また,変数名と添え字を利用して,x[i] のようにして参照できます( i = 0 ~ 2 ).この例に示すように,各要素は,必ずしも,同じデータ型である必要はありません.しかし,間違いの元になる可能性がありますので,配列は,同じデータ型だけで構成するようにした方が良いと思います.また,プログラムの実行時に,要素の追加,削除等を行うことが可能です.なお,上のプログラムにおける p は,引数を,人間に読みやすい形で出力するための組み込み関数です.

    2. 多次元配列

        多次元の配列を扱うことも可能です.例えば,

      v1 = Array[[10, 20, 30], [40, 50, 60]]
      p v1
      v2 = Array.new(2)
      v2[0] = Array[10, 20, 30]
      v2[1] = Array[40, 50, 60]
      p v2
      v3 = Array.new()
      v3.push([10, 20, 30])
      v3.push([40, 50, 60])
      p v3				
      のように,配列の要素を,さらに配列として定義すれば,2 次元の配列を定義できます( 3 次元以上も同様).この例では,いずれの方法においても,2 行 3 列の配列になります.また,変数名と添え字を利用して,x[i][j] のようにして参照できます( i = 0 ~ 1,j = 0 ~ 2 ).

    3. 代入と複製

        C/C++ において,変数 b に a を代入するとは,a に記憶されている値をコピーして,b に記憶することです.例えば,

      a = 10;
      b = a;
      b = 20;				

      のような操作によって,b は a のコピーですから,b の値を変更しても a の値は変化しません.Ruby の場合は,整数のような基本的なデータ型をはじめとして,全てのデータをオブジェクトとして統一的に取り扱い,変数はそのオブジェクトへの参照(アドレス)を記憶しているはずです.2 行目の代入によって,変数 a の 10 への参照を b にコピーして代入しているはずです.しかし,10 そのものを直接変更する手段がありませんので,基本的なデータ型の場合,C/C++ と同じ結果になります.しかし,

      u1 = Array[1, "abc", 2]
      u2 = Array[1, "abc", 2]
      u3 = u1
      u3[1] = 4
      print u1[1]," ",u2[1]," ",u3[1]   # 4 abc 4				
      のような配列の場合について考えてみます.u1 と u3 の関係は,整数型における a と b との関係と同じです.しかし,上で述べたように,3 行目の代入は,u1 の領域及びそこに記憶されている要素をすべてコピーし,u3 に代入しているわけではありません.ここでは,u1,u2,u3 をポインタとしてとらえた方が理解しやすいと思います.つまり,3 行目の代入によって,u1 に記憶されているアドレス(参照)が u3 に記憶され,u1 と u3 が同じ領域を指しているという意味です.実際,u1 が記憶している参照先である配列の値を変更すれば,u3 の値も変化します(逆も同様).このことを概念的に示せば以下のようになります.
        以下に示すのは,配列の全ての要素を出力した場合です.ただし,初期設定に使用するデータは,C/C++ と比較するために,変更してあります.
      u1 = Array[1, 2, 3];
      u2 = Array[1, 2, 3];
      u3 = u1;
      print "u1 : ", u1[0], " ", u1[1], " ", u1[2], "\n"
      print "u2 : ", u2[0], " ", u2[1], " ", u2[2], "\n"
      print "u3 : ", u3[0], " ", u3[1], " ", u3[2], "\n"
      print "    u1[0] -> 10,u2[1] -> 20,u3[2] -> 30\n"
      u1[0] = 10
      u2[1] = 20
      u3[2] = 30
      print "u1 : ", u1[0], " ", u1[1], " ", u1[2], "\n"
      print "u2 : ", u2[0], " ", u2[1], " ", u2[2], "\n"
      print "u3 : ", u3[0], " ", u3[1], " ", u3[2], "\n"
      				
      (出力)
      u1 : 1 2 3
      u2 : 1 2 3
      u3 : 1 2 3
          u1[0] -> 10,u2[1] -> 20,u3[2] -> 30
      u1 : 10 2 30
      u2 : 1 20 3
      u3 : 10 2 30				

        次に,2 次元配列について考えてみます.
      v1 = Array[[10, 20, 30], [40, 50, 60]]
      v2 = Array[[10, 20, 30], [40, 50, 60]]
      v3 = v1				
      のような設定を行った場合,3 行目の記述によって,1 次元配列の場合と同様,v3 も,v1 と同じ 2 行 3 列の配列になります.従って,v1 の値を変更すれば,v3 の値も変化します(逆も同様).そのイメージは,以下のようになります.
        また,
      v4 = v1[0];   // v1,v3 の 1 行目				
      のように,各行を 1 次元配列として扱うことも可能です.勿論,v4 と v1[0] は同じ場所を指していますので,例えば,v4[1] の値を変更すれば,v1[0][1] の値も変化します(逆も同様).

        参照だけではなく,全ての要素をコピーして新しい配列を生成する方法はないのでしょうか.一つの方法はArray クラスclonedupnew を使用することです.以下に示すプログラム例とその出力結果を見てください.1 次元配列に対しては( 01 行目~ 10 行目),希望とする結果になっています.しかし,2 次元配列に対しては( 12 行目~ 21 行目),代入と全く同じ結果になっています.これは,clone などによって,上で示した 2 次元配列に対する図における v1[0] と v1[1] だけが複製されたためであると考えられます.
      01	u1 = Array[10, 20, 30]
      02	u2 = Array.new(u1)
      03	u3 = u1
      04	u4 = u1.clone
      05	u3[0] = 100
      06	u4[1] = 200
      07	print "u1 ", u1, "\n"
      08	print "u2 ", u2, "\n"
      09	print "u3 ", u3, "\n"
      10	print "u4 ", u4, "\n"
      11	
      12	v1 = Array[[10, 20, 30], [40, 50, 60]]
      13	v2 = Array.new(v1)
      14	v3 = v1
      15	v4 = v1.clone
      16	v3[1][1] = 100
      17	v4[0][1] = 200
      18	print "v1 ", v1, "\n"
      19	print "v2 ", v2, "\n"
      20	print "v3 ", v3, "\n"
      21	print "v4 ", v4, "\n"
      				
      (出力)
      u1 [100, 20, 30]
      u2 [10, 20, 30]
      u3 [100, 20, 30]
      u4 [10, 200, 30]
      v1 [[10, 200, 30], [40, 100, 60]]
      v2 [[10, 200, 30], [40, 100, 60]]
      v3 [[10, 200, 30], [40, 100, 60]]
      v4 [[10, 200, 30], [40, 100, 60]]				

  6. メソッドとブロック

    1. メソッド(関数)

        メソッド関数)は,与えられた情報に基づき何らかの処理を行い,その結果を返します.メソッドは,独立したプログラムに似ていますので,何らかの方法によって必要な情報をメソッドに渡してやる必要があります.グローバル変数によって渡すことも可能ですが,最も基本的な方法は,引数を利用することです.ここで,引数とは,関数を呼び出した側と関数との間で,情報の受け渡しに使用される変数や定数のことです.メソッドは,一般的に,
      def メソッド名 [ 引数の並び ]
         式
        ・・・
        [rescue [error_type,..] [=> evar] [then]
           式..]..
        [else
           式..]
        [ensure
           式..]
      end				
      のような形式で記述します(あえて,このような複雑な構造にする理由は何でしょうか).rescue 節には,本体の実行によってエラーが発生した場合の処理を記述します.else 節は,本体の実行によって例外が発生しなかった場合に評価されます.また,ensure 節が存在する時は,メソッド評価の最後に ensure 節の本体を評価します.

        メソッドによって処理した結果をメソッドを呼んだ側に返したい場合,基本的に,戻り値を通して行います.メソッドの戻り値は,return 節に渡した値です.return が呼び出されなかった場合は,メソッドの本体から ensure 節実行直前までの最後に評価した式の値を返します(このような機能,必要?).

        引数には,例えば,
      def func (par1, par2=10, par3=30)
      	x = par1 + par2 + par3
      	return x
      end
      print func(100, 50)				
      のように,デフォルト値を設定することができます(デフォルト引数).ただし,n 番目の引数にデフォルト値を設定した場合は,それ以降の引数に対してもデフォルト値を設定する必要があります.上の例の場合,3 番目の引数を省略したため,par3 の値は 30 であるとして処理され,結果は 180 になります.なお,この例の場合は,最後に処理された式の値がメソッドの返すべき値であるため,return が無くても,同じ結果になります(このような機能は利用しない方が良い).

        メソッド定義において,仮引数はその種類毎に,以下に示す順序でしか指定することはできません.なお,いずれも省略することは可能です.

      • デフォルト式のない引数(複数指定可)
      • デフォルト式のある引数(複数指定可)
      • * を伴う引数(1つだけ指定可)
      • & を伴う引数(1つだけ指定可)

    2. ブロック

        ブロックとは,{ ... },または,do ... end のペアで囲まれた Ruby の文及び式の集まりです.ブロックの先頭には,縦棒 ( | ) で囲み,パラメータをカンマで区切って指定できます.ブロックは,Proc クラスのオブジェクトであり,以下に示すように,Proc クラスの new メソッド,または,組み込み関数である proc や lambda によって生成可能です.なお,ブロックは,C++ におけるラムダ式に相当する機能です.C/C++ においても同様ですが,プログラムを分かりにくくする原因になりかねません.できる限り,使用は避けた方が良いと思います.
      Proc.new do | パラメータ| 式 end
      Proc.new { | パラメータ| 式 }
      proc do | パラメータ| 式 end
      proc { | パラメータ| 式 }
      lambda do | パラメータ| 式 end
      lambda { | パラメータ| 式 }				
        return と break は,lambda と Proc.new や proc では挙動が異なります.例えば return を行った場合,lambda では手続きオブジェクト自身を抜けますが,Proc.new や proc では手続きオブジェクトを囲むメソッドを抜けます.この点は,以下に示す例に従って説明していきます.03 ~ 05 行目において,組み込み関数 lambda を使用してブロックを生成しています.この例においては,複数行で記述していますが,01,02 行目のように,1 行で記述することも可能です(以下においても同様).06 行目では,Proc クラスのメソッド call を利用してブロックを実行しています.また,08 ~ 11 行目では,メソッド内でブロックを生成し,同様のことを行っています.いずれの場合においても,期待した結果が得られています.

        14 ~ 23 行目においては,上と同様のことを Proc クラスの new メソッドを使用して行っています(組み込み関数 proc を使用した場合も同様).15 行目は,04 行目に対応していますが,return を使用した 04 行目と同じ記述では,期待した結果が得られません.最後に処理された式の値がメソッド(この場合は,ブロック)の返すべき値であるという仕様のため,期待した結果になります.メソッドも式の一種あると考えれば,ある程度納得できますが,非常に分かり難いと思います.
      01	# add1 = lambda { |x, y| return x + y }
      02	# add1 = lambda do |x, y| return x + y end
      03	add1 = lambda { |x, y|
      04		return x + y
      05	}
      06	print "lambda : ", add1.call(1, 2), "\n"
      07	
      08	def add2(x, y)
      09		f = lambda { return x + y }
      10		return f.call
      11	end
      12	print "lambda(関数) : ", add2(1, 2), "\n"
      13	
      14	add3 = Proc.new { |x, y|
      15		x + y   # return x + y は No
      16	}
      17	print "Proc.new : ", add3.call(1, 2), "\n"
      18	
      19	def add4(x, y)
      20		f = Proc.new { return x + y }   # f = proc { return x + y }
      21		return f.call
      22	end
      23	print "Proc.new(関数) : ", add4(1, 2), "\n"
      				
      (出力)
      lambda : 3
      lambda(関数) : 3
      Proc.new : 3
      Proc.new(関数) : 3				
        ブロックは,
      method(arg1, arg2, ...)  do [ | パラメータ | ] 式 ... end
      method(arg1, arg2, ...) { [ | パラメータ | ] 式 ... }
      method(arg1, arg2, ..., &proc_object)   # proc_object は別に定義				
      のような形式で,メソッドに引数として渡すことができます.ブロックを引数として受け取るメソッドを,ブロック付きメソッドと呼びます.C++ におけるラムダ式を引数とする場合や 関数名を引数とする場合に似ています.なお,Ruby においては,関数名を引数とすることはできません.

      プログラム例 5.1] 様々な引数

        この例では,整数,デフォルト引数,配列,及び,ブロックを引数とした場合について検討しています.ブロック付きメソッドは,ブロックを 3 つの方法(メソッドの後ろに直接記述する方法,lambda を使用する方法,Proc を使用する方法)で記述しています.また,引数とするブロック名を変更することによって,結果を変更する方法についても示しています.
      01	#************************/
      02	# 様々な引数            */
      03	#   coded by Y.Suganuma */
      04	#************************/
      05	a = 10
      06		#
      07		# ブロック付きメソッド
      08		#
      09	print "ブロック付きメソッド\n"
      10	def add5(x, y = 20, &blk)
      11		x = 5
      12		r = blk.call(x, y)   # yield(x, y)
      13		return r
      14	end
      15	
      16	res = add5(a) { |x, y| x + y }   # return x + y は No
      17	print "   ブロック付きメソッド(直接) : a = ", a, ", sum = ", res, "\n"
      18	
      19	add_l = lambda { |x, y|
      20		return x + y
      21	}
      22	res = add5(a, &add_l)
      23	print "   ブロック付きメソッド(lambda) : a = ", a, ", sum = ", res, "\n"
      24	
      25	add_p = Proc.new { |x, y|
      26		x + y   # return x + y は No
      27	}
      28	res = add5(a, &add_p)
      29	print "   ブロック付きメソッド(Proc.new) : a = ", a, ", sum = ", res, "\n"
      30		#
      31		# 最小値,最大値を求めるブロック付きメソッド
      32		#
      33	print "最小値,または,最大値を求めるメソッド\n"
      34	def min_max(x, &blk)
      35		x[3] = -3
      36		re   = x[0]
      37		for a in x[1...x.length]
      38			re = yield(re, a) ? re : a
      39	#		re = blk.call(re, a) ? re : a
      40		end
      41		return re
      42	end
      43		# ブロック max の定義と利用
      44	max = lambda { |x, y|
      45		return x > y
      46	}
      47	print "   2 > 1 ? ", max.call(2, 1), "\n"
      48		# ブロック min の定義と利用
      49	min = lambda { |x, y|
      50		return x < y
      51	}
      52	print "   1 < 2 ? ", min.call(1, 2), "\n"
      53		# 最大値,最小値の計算
      54	x = [1, 4, 2, 3, 5]
      55	print "   max ", min_max(x, &max), " x ", x, "\n"
      56	print "   min ", min_max(x, &min), " x ", x, "\n"
      				
      05 行目

        a の値を 10 に設定しています.

      09 行目~ 14 行目

        ブロック付きメソッド add5 を定義しています.メソッド add5 の中では,x の値( a に対応)を 5 に変更しています( 11 行目).また,二番目の引数はデフォルト引数となっています.

      16 行目~ 17 行目

        16 行目においてブロックの記述と共に,2 番目の引数を省略して,add5 を呼び出しています.コメントに記したように,return を記述すると正しく動作しません.メソッド add5 の中では,x の値( a に対応)を 5 に変更していますが( 11 行目),17 行目の出力からも明らかなように,a の値に変化はありません.このように,整数型のような基本型をメソッドの引数として渡したとき,メソッド内でその値を変更しても,メソッドを呼んだ側の値は影響を受けません,しかし,和の計算結果は,変更された 5 と,15 行目において 2 番目の引数が省略されているため,デフォルト値 20 の和になっています.

      19 行目~ 23 行目

        19 行目~ 21 行目において lambda を使用してブロックを定義し,22 行目において,それを引数として add5 を呼び出しています.期待通りの結果が得られています.

      25 行目~ 29 行目

        25 行目~ 27 行目において Proc.new を使用してブロックを定義し,28 行目において,それを引数として add5 を呼び出しています.26 行目のコメントにあるように,return を記述すると正しく動作しません.

      33 行目~ 42 行目

        引数として渡されたブロックによって,配列 x 内の最大値,または,最小値を求めています.その際,x[3] の値を -3 に変更しています.このように,ブロックを引数にすることによって,同じメソッドに異なる機能を持たせることが可能になります.

      44 行目~ 47 行目

        最大値を求めるための比較を行うブロックを生成し,その単独動作を確認しています.

      49 行目~ 52 行目

        最小値を求めるための比較を行うブロックを生成し,その単独動作を確認しています.

      54 行目~ 56 行目

        上で作成したメソッド及びブロックを利用して,最大値と最小値を求めています.35 行目で,x[3] の値を変更していますので,54 行目で定義された x の値も変化しています.このように,配列のようなオブジェクトを引数とした場合,メソッド内の変更が,メソッドを呼んだ側にも反映されます.
      (出力)
      ブロック付きメソッド
         ブロック付きメソッド(直接) : a = 10, sum = 25
         ブロック付きメソッド(lambda) : a = 10, sum = 25
         ブロック付きメソッド(Proc.new) : a = 10, sum = 25
      最小値,または,最大値を求めるメソッド
         2 > 1 ? true
         1 < 2 ? true
         max 5 x [1, 4, 2, -3, 5]
         min -3 x [1, 4, 2, -3, 5]				

  7. クラスとモジュール

    1. クラス

        クラスの定義は,以下のようにして行われます.なお,< の後は継承するクラスを表すものであり,次節を参照して下さい.
      class 識別子 [ < superclass ]
        式(演算式,メソッド,クラスなど)
          ・・・
      end				
        クラス名はアルファベットの大文字で始まる識別子(定数)です.Ruby では,クラスもオブジェクトの一つであり,Class クラスのインスタンスです.クラス定義は,Class クラスのインスタンスを生成し,その名前を識別子で指定した定数へ代入することに相当します.そのため,C++ や Java とは異なり,後に示す例にも示すように,クラスを定義した時点で式が実行されます.

        クラスが既に定義されているとき,さらに同じクラス名でクラス定義を書くとクラスの定義の追加になります.また,クラス定義は,ネスト(入れ子)にすることもできます.クラスのネストは,意味的に関連するクラスを外側のクラスやモジュールでひとまとまりにしたり,包含関係を表すために使用されますが,継承関係などの機能的な関連はまったくありません.

        クラスのインスタンス(オブジェクト)を生成するためには,次に示すように(クラス Test のインスタンスを生成),Class クラスの new メソッドを利用します.クラス内に Object クラスinitialize メソッドがオーバーライドされているときは,インスタンスの生成時に自動的に呼ばれます(次節の例を参照).initialize メソッドは,C++ におけるコンストラクタと同じような働きをします.
      obj1 = Test.new
      obj2 = Test.new(10, 20)   # 引数がある場合				

      プログラム例 6.1] クラスの定義と参照

      01	#************************/
      02	# クラスの定義と参照    */
      03	#   coded by Y.Suganuma */
      04	#************************/
      05	v_local_1    = 5
      06	$_global_1   = 15
      07	V_const_1    = 25
      08	@_instance_1 = 35
      09	@@_class_1   = 45   # 警告メッセージ出力
      10	
      11	class Test
      12		v_local_2    = 10
      13		$_global_2   = 20
      14		V_const_2    = 30
      15		@_instance_2 = 40
      16		@@_class_2   = 50
      17	
      18		print "クラス内からクラス外の変数を参照\n"
      19	#	print "     v_local_1    : ", v_local_1, "\n"   # 参照不可(エラー)
      20		print "     $_global_1   : ", $_global_1, "\n"
      21		print "     V_const_1    : ", V_const_1, "\n"
      22		print "     @_instance_1 : ", @_instance_1, "\n"   # 参照不可
      23		print "     @@_class_1   : ", @@_class_1, "\n"
      24	
      25		print "クラス内からクラス内の変数を参照\n"
      26		print "     v_local_2    : ", v_local_2, "\n"
      27		print "     $_global_2   : ", $_global_2, "\n"
      28		print "     V_const_2    : ", V_const_2, "\n"
      29		print "     @_instance_2 : ", @_instance_2, "\n"
      30		print "     @@_class_2   : ", @@_class_2, "\n"
      31				# コンストラクタ
      32		def initialize(par = 100)
      33			@_instance_2 = par
      34		end
      35				# インスタンス変数の出力
      36		def out
      37			print "     @_instance_2 : ", @_instance_2, "\n"
      38		end
      39				# インスタンス変数を外部から参照可能にする
      40		attr("_instance_2", true)
      41	end
      42	
      43	print "クラス外からクラス外の変数を参照\n"
      44	print "     v_local_1    : ", v_local_1, "\n"
      45	print "     $_global_1   : ", $_global_1, "\n"
      46	print "     V_const_1    : ", V_const_1, "\n"
      47	print "     @_instance_1 : ", @_instance_1, "\n"
      48	print "     @@_class_1   : ", @@_class_1, "\n"   # 警告メッセージ出力
      49	
      50	print "クラス外からクラス内の変数を参照\n"
      51	#print "     v_local_2    : ", Test::v_local_2, "\n"   # 参照不可(エラー)
      52	print "     $_global_2   : ", $_global_2, "\n"   # Test:: を付加するとエラー
      53	print "     V_const_2    : ", Test::V_const_2, "\n"
      54	print "     @_instance_2 : ", @_instance_2, "\n"   # 参照不可
      55	#print "     @@_class_2   : ", @@_class_2, "\n"   # 参照不可(エラー)
      56				# インスタンスの生成
      57	obj1 = Test.new
      58	obj2 = Test.new(200)
      59	
      60	print "インスタンスからクラス内の変数を参照\n"
      61	#print "     v_local_2    : ", obj1.v_local_2, "\n"   # 参照不可(エラー)
      62	#print "     $_global_2   : ", obj1.$_global_2, "\n"   # 参照不可(エラー)
      63	#print "     V_const_2    : ", obj1.V_const_2, "\n"   # 参照不可(エラー)
      64	obj1.out()
      65	obj2.out()
      66	#print "     @_instance_2 : ", obj1.@_instance_2, "\n"   # 参照不可(エラー)
      67	print "     @_instance_2 : ", obj1._instance_2, "\n"   # attr による設定が必要
      68	print "     @_instance_2 : ", obj2._instance_2, "\n"
      69	#print "     @@_class_2   : ", obj1.@@_class_2, "\n"   # 参照不可(エラー)
      				
      05 行目~ 09 行目

        クラスの外で,ローカル変数グローバル変数定数インスタンス変数クラス変数を定義しています.インスタンス変数,クラス変数は,本来クラス内で定義すべき変数ですので,ここで定義することにはあまり意味がありません.そのため,09 行目に対しては警告メッセージが出力されます.

      12 行目~ 16 行目

        05 行目~ 09 行目と同様,5 つの変数を定義しています.

      18 行目~ 23 行目

        05 行目~ 09 行目において定義した変数をクラス内から参照しています.その中の,ローカル変数及びインスタンス変数は参照不可能です.グローバル変数及び定数は,定義された以降はどこからでも参照可能な変数ですので,当然の結果だと思います.

      25 行目~ 30 行目

        12 行目~ 16 行目において定義した変数をクラス内から参照しています.同じクラス内の変数ですので,当然,全ての変数を参照可能です.

      32 行目~ 34 行目

        Test クラスのコンストラクタです.Test クラスのインスタンスが生成されたとき( 57,58 行目),最初に呼ばれます.ここでは,インスタンス変数の設定を行っています.

      36 行目~ 38 行目

        インスタンス変数はそのクラスまたはサブクラスのメソッドから参照可能な変数です.従って,クラスの外から 66 行目のような形では参照できません.このメソッドはの目的は,インスタンス変数を参照することです( 64,65 行目).

      40 行目

        インスタンス変数を,66 行目のような形では参照するための処置です.この記述があるため,67,68 行目の参照が可能になります.

      43 行目~ 48 行目

        クラス外からクラス外の変数を参照を参照しています.基本的に,全ての変数を参照可能です.

      50 行目~ 55 行目

        Test クラスのインスタンスを生成する前に,クラス外からクラス内の変数を参照を参照しています.グローバル変数及び定数を参照可能です.定数に対しては,「 Test:: 」を付加しないとエラーになり,グローバル変数に対しては,付加するとエラーになります.

      57,58 行目

        インスタンス変数の値を変えた 2 つのインスタンスを生成しています.

      60 行目~ 69 行目

        インスタンスを通して,5 つの変数を参照しています.「 インスタンス.変数名」の形で参照できるのは,インスタンス変数だけです( 40 行目の処置が必要).勿論,この時点でも,グローバル変数と定数の参照は可能です(参照方法が異なる)
      (出力)
      test.rb:9: warning: class variable access from toplevel
      クラス内からクラス外の変数を参照
           $_global_1   : 15
           V_const_1    : 25
           @_instance_1 :
           @@_class_1   : 45
      クラス内からクラス内の変数を参照
           v_local_2    : 10
           $_global_2   : 20
           V_const_2    : 30
           @_instance_2 : 40
           @@_class_2   : 50
      クラス外からクラス外の変数を参照
           v_local_1    : 5
           $_global_1   : 15
           V_const_1    : 25
           @_instance_1 : 35
      test.rb:48: warning: class variable access from toplevel
           @@_class_1   : 45
      クラス外からクラス内の変数を参照
           $_global_2   : 20
           V_const_2    : 30
           @_instance_2 :
      オブジェクトからクラス内の変数を参照
           @_instance_2 : 100
           @_instance_2 : 200
           @_instance_2 : 100
           @_instance_2 : 200
      				

    2. 派生クラス(継承)

        あるクラスに宣言されているメソッドなどをすべて受け継ぎ(メソッドなどを利用できるようにする),それに新たなメソッドを追加,受け継いだメソッドの修正などを行い,新しいクラスを宣言することができます.これを,クラスの継承と呼びます.このとき,基になったクラスをスーパークラス(親クラス),新たに作成したクラスをサブクラス(子クラス)と呼びます.継承するクラスを示すためには,下の例に示すように, < を使用します.なお,Ruby においては,複数のクラスを継承する多重継承は許されません.

        @@ で始まる変数はクラス変数と呼びます.クラス変数は,クラス定義の中で定義され,そのクラス,及び,そのサブクラスから参照できます( C++ における protected 宣言された変数に似ています).このような点から,クラス変数は,クラスやサブクラスなどで共有されるグローバル変数であるとみなすことができます.

        以下に示すプログラム例において,クラス Test は,クラス P_Test を継承しています.また,Ruby のプログラムを書くことは,Object クラスを継承したクラスを定義することに相当しますので,例に示すプログラム全体が,Object クラスを継承したサブクラスになっています.従って,プログラムのどこからでも,Object クラスに定義されているメソッドやクラス変数を参照可能です.

        クラス P_Test で定義されているクラス変数 @@_class やメソッド func は,クラス P_Test を継承しているクラス Test からも参照可能です.しかし,クラス変数 @@_class は,クラス Test 内にネストされたクラス Test_in やクラスの外からは参照できません.
      class P_Test
      	@@_class = 10
      	print "クラス変数 @@_class = ", @@_class, " (in P_Test)\n"
      	def func
      		print "こんにちは!!"
      	end
      end
      
      class Test < P_Test
      	print "クラス変数 @@_class = ", @@_class, " (in Test)\n", 
      	class Test_in
      #		print "クラス変数 @@_class = ",  @@_class, " (in Test)\n"  参照不可
      	end
      end
      
      #print "クラス変数 @@_class = ", @@_class, "\n"  参照不可
      t_obj1 = P_Test.new
      t_obj1.func
      print " (in P_Test)\n"
      t_obj2 = P_Test.new
      t_obj2.func
      print " (in Test)\n"
      				
      (出力)
      クラス変数 @@_class = 10 (in P_Test)
      クラス変数 @@_class = 10 (in Test)
      こんにちは!! (in P_Test)
      こんにちは!! (in Test)				

        super は現在のメソッドがオーバーライドしているスーパークラスのメソッドを呼び出します.括弧と引数が省略された場合には現在のメソッドの引数がそのまま引き渡されます.引数を渡さずにオーバーライドしたメソッドを呼び出すには super() と括弧を明示します.以下に示すプログラムを実行すると,250 という出力が得られます.
      class P_Test
      	def add(p1, p2=20, p3=30)
      		return p1 + p2 + p3
      	end
      end
      
      class Test < P_Test
      	def add(p)
      		return p + super
      	end
      end
      
      print Test.new.add(100)
      				

    3. モジュール

        Ruby では,多重継承(複数のクラスを継承する機能)を許していません.その代わりに,モジュールという機能があります.モジュールはインスタンスを持たないクラスといっても良いかもしれません.クラスの定義において,Module クラスの include メソッドによりモジュールの機能を取りこんで使用します.このことを,Mix-in と呼びます.下の例では,モジュール Add_m をクラス Test に取り込んでいます.実行すると,30 と 90 という結果が得られます.
      module Add_m
      	Const = 100
      	def add(a, b)
      		return a + b
      	end
      end
      
      class Test
      	include Add_m
      	def sub(a, b)
      		return a - b + Const
      	end
      end
      
      printf "%d\n", Test.new.add(10, 20)
      printf "%d\n", Test.new.sub(10, 20)
      				
        下に示す例のように,クラスではなく特定のインスタンス(オブジェクト)にだけモジュールの機能を追加することも可能です.その際には,Object クラスの extend メソッドを使用します。
      module Add_m
      	Const = 100
      	def add(a, b)
      		return a + b
      	end
      end
      
      class Test
      	def sub(a, b)
      		return a - b
      	end
      end
      
      obj = Test.new
      obj.extend Add_m
      printf "%d\n", obj.add(10, 20)
      printf "%d\n", obj.sub(10, 20)
      				

  8. 入出力

    1. 標準入出力

        入出力を行うためには,組み込み関数の gets,getline,getlinespprintprintf などを使用しますが,デフォルトで,その入出力先は標準入力,標準出力になっていますので,そのままキーボードからの入力,コマンドプロンプトへの出力に利用できます.以下のプログラムでは,入出力関数を使用し,その違いを見ています.

        gets は,入力された 1 行分のデータを文字列として読み込みます.下のプログラムにおいては,読み込んだデータを String クラスのメソッド strip によって前後の余分なスペースを削除した後,split を利用して 2 つのデータに分割し,配列 x に代入しています( 02 行目).03 ~ 10 行目では,いくつかの関数を利用して,配列 x の内容を出力しています.関数 print は,カンマで区切られたデータを順に出力します.データ毎にスペースは入らず,また,改行も行いませんので,自分で設定してやる必要があります.関数 p は,関数 print と似ていますが,データを出力する毎に改行が行われます.関数 puts も,データ毎に改行が行われると共に,配列の場合は,その要素毎に出力されます.関数 printf は,C/C++ の標準関数 printf と同様,フォーマットされて出力されます.11 ~ 12 行目では,String クラスのメソッド to_i を利用して,配列に記憶されている文字列を整数に変換した後,加算処理を行い,その結果を出力しています.
      01	print "データを 2 つ入力してください "
      02	x = (gets().strip()).split()
      03	print "*** 配列 x を print で出力 ***\n"
      04	print "print ", x, "\n"
      05	print "*** 配列 x を p で出力 ***\n"
      06	p "p ", x
      07	print "*** 配列 x を puts で出力 ***\n"
      08	puts "puts ", x
      09	print "*** 配列 x を printf で出力 ***\n"
      10	printf "x[0] %d x[1] %d\n", x[0], x[1]
      11	print "*** x[0] と x[1] の加算結果を print で出力 ***\n"
      12	print "x[0] + x[1] = ", x[0].to_i() + x[1].to_i(), "\n"
      				
      (出力)
      データを 2 つ入力してください 1 2
      *** 配列 x を print で出力 ***
      print ["1", "2"]
      *** 配列 x を p で出力 ***
      "p "
      ["1", "2"]
      *** 配列 x を puts で出力 ***
      puts
      1
      2
      *** 配列 x を printf で出力 ***
      x[0] 1 x[1] 2
      *** x[0] と x[1] の加算結果を print で出力 ***
      x[0] + x[1] = 3
      				

    2. ファイル入出力

        ファイル入出力のためには,IO クラス,または,File クラスを利用します.下に示すプログラムにおいては,ファイル data1.txt の各行には,2 つの整数がスペースで区切って,以下に示すように記述されているものとします.
      1 2
      3 4
      ・・・・・				
        また,11 ~ 12 行目に示すように,標準入出力を示す組み込み変数 $stdin$stdout に対してファイル入出力を設定することも可能です.これを元の状態に戻すには,17 ~ 18 行目に示すように,組み込み定数 STDINSTDOUT を使用します.
      01	inp = File.open("data1.txt", "r")   # data1.txt から入力
      02	out = File.open("data2.txt", "w")   # data2.txt へ出力
      03	while line = inp.gets   # データが無くなるまで繰り返す
      04		x = line.split()
      05		out.print x[0].to_i() + x[1].to_i(), "\n"
      06	end
      07	inp.close   # ファイルを閉じる
      08	out.close   # ファイルを閉じる
      09	
      10	=begin
      11	$stdin  = open("data1.txt", "r")
      12	$stdout = open("data2.txt", "w")
      13	while line = gets
      14		x = line.split()
      15		print x[0].to_i() + x[1].to_i(), "\n"
      16	end
      17	$stdin  = STDIN   # 通常の標準入力に戻す
      18	$stdout = STDOUT   # 通常の標準出力に戻す
      19	=end
      				

  9. 変数の有効範囲(スコープ)

      変数の有効範囲については,プログラム例を使用して説明していきます.

    プログラム例 8.1] 変数の有効範囲(スコープ)

    01	#***************************/
    02	# 変数の有効範囲(スコープ) */
    03	#      coded by Y.Suganuma */
    04	#***************************/
    05	
    06	#******************/
    07	# クラス Example1 */
    08	#******************/
    09	class Example1
    10		Const    = 1000;
    11		@@_class = 2000;
    12	
    13		def initialize
    14			@_pro = 3000;
    15		end
    16	
    17		def sub1()
    18			printf("sub1 Const %d @@_class %d @_pro %d\n", Const, @@_class, @_pro);
    19		end
    20	end
    21	
    22	#******************/
    23	# クラス Example2 */
    24	#******************/
    25	class Example2 < Example1
    26		public
    27			def sub2()
    28				printf("sub2 Const %d @@_class %d @_pro %d\n", Const, @@_class, @_pro);
    29			end
    30	
    31		attr_accessor("_pro");
    32	end
    33	
    34	#***********/
    35	# 関数 sub */
    36	#***********/
    37	def sub()   # この位置で定義する必要がある
    38		x = 40;
    39		printf("   sub x %d\n", x);
    40		printf("   sub z %d\n", $z);
    41	end
    42	
    43	#***************/
    44	# main program */
    45	#***************/
    46			# ブロック
    47	x  = 10;
    48	$z = 30;
    49	if x > 5
    50		printf("block x %d\n", x);
    51		x = 15;
    52		y = 20;
    53		printf("block x %d\n", x);
    54		printf("block y %d\n", y);
    55	else
    56		printf("block x %d\n", x);
    57		x = -15;
    58		printf("block x %d\n", x);
    59	end
    60	sub();
    61	printf("x %d\n", x);
    62	printf("y %d\n", y);   # 最初の x が 1 の時は,y が未定義のためエラー
    63			# クラス
    64	ex = Example2.new;
    65	ex.sub1();
    66	ex.sub2();
    67	printf("public member( Const ) %d\n", Example2::Const);
    68	printf("public member( @_pro ) %d\n", ex._pro);
    			
      まず,47 行目において,変数 x が定義され(変数 x に値が代入され),10 で初期設定されています.この変数 x は,この位置から main プログラムが終わる 68 行目まで有効になります.50 ~ 54 行目の if ブロック内の 50 行目において,変数 x の値が出力されていますが,当然,その結果は,47 行目で宣言されたときの値になります.しかし,51 行目において,再び,変数 x が宣言されていますが,Ruby の場合は,C++ の場合とは異なり,47 行目で宣言された変数 x に置き換わることになります.従って,ここで宣言された変数 x の有効範囲は,main プログラムの終わりである 68 行目までになります.実際,61 行目における出力文では,51 行目において宣言された変数 x の値が出力されます.同様に,52 行目で宣言された変数 y の有効範囲も 68 行目までとなります.この例では,問題がありませんが,47 行目における変数 x の初期値を 5 以下に設定すると,56 ~ 58 行目が実行されることになります.そのブロック内では,変数 y が使用されていませんので,62 行目の出力文はエラーになってしまいます.変数が定義されているか否か(変数に値が代入されているか否か)の見極めが困難である場合も多いと思いますので,十分注意してください.

      60 行目において関数 sub を呼んでいます.38 行目では,変数 x を宣言し,39 行目において,その値を出力しています.当然,38 行目の宣言を行わなければ,エラーになってしまいますし,また,38 行目の宣言によって,51 行目で宣言された x の値が影響を受けることはありません( 61 行目の出力文に対応する結果参照).しかし,変数 $z は関数 sub 内に宣言されておらず,main プログラムの 48 行目において宣言されています.このように,名前が $ で始まる変数はグローバル変数と呼ばれ,すべての関数等から参照が可能になります.逆に,変数 x や y のように,あるブロック(関数を含む)内だけで有効な変数を,ローカル変数と呼びます.以上,47 ~ 62 行目内の出力文(関数 sub 内の出力文を含む)によって,以下に示すような出力が得られます.
    block x 10
    block x 15
    block y 20
       sub x 40
       sub z 30
    x 15
    y 20				
      次に,クラスに付いて考えてみます.09 ~ 20 行目においてクラス Example1 が定義され,25 ~ 32 行目では,クラス Example1 を継承する形で,クラス Example2 が定義されています.Ruby においては,クラス Example の各変数や関数のアクセス権は,そのままクラス Example2 に引き継がれます.

      64 行目において,Example2 のインスタンス ex を生成し,65 行目では,クラス Example1 から継承した関数 sub1 を通して,3 つの変数を出力しており,3 つの変数の値がそのまま出力されます.また,66 行目では,クラス Example2 に追加された関数 sub2 を通して各変数を出力しています.Ruby においては,大文字で始まる変数名は,定数とみなされ,クラスの外からも 67 行目のような形で参照可能です.また,@@ で始まるる変数名は,クラス変数と呼ばれ,クラス及びそのサブクラスで共通の値を持ちます.ただし,クラスの外からは,参照することはできません.さらに,@ で始まる変数は,インスタンス変数と呼ばれ,各インスタンス毎に異なる値を持つことができます.C++ における private 指定されたメンバー変数に似ています.そのため,そのままの状態では,クラスの外部から参照することはできません.しかし,31 行目のような指定( attr_accessor )を行うことによって,68 行目のような形でクラス外部から参照可能になります.以上,64 ~ 68 行目内の出力文によって,以下に示すような出力が得られます.
    sub1 Const 1000 @@_class 2000 @_pro 3000
    sub2 Const 1000 @@_class 2000 @_pro 3000
    public member( Const ) 1000
    public member( @_pro ) 3000				

情報学部 菅沼ホーム 目次 索引