Ruby文法のおさらい

既にRubyでの簡単なプログラムを作った経験があるという前提で, 手短に見直して把握できる程度の説明をこの章にまとめておく。

プログラム全体

ソースプログラムをファイルに記述する上での特徴をまとめる。

変数

制御構造

Rubyの主要な制御構造を示す。

比較演算子等

制御構造の条件の部分に利用する比較のための演算子のうち 本書で必要なものを示す。

演算子 意味
== 左辺と右辺が等しいか
< 左辺が右辺より小さいか
<= 左辺が右辺以下か
> 左辺が右辺より大きいか
>= 左辺が右辺以上か
&& 「かつ」
|| 「または」
! 否定
not 否定

条件が成り立つ場合は,「真」を表す true が, 成り立たない場合は「偽」を表す false を返す。 なお,Rubyでは空を意味する値に nil があり, これも「偽」の役割を持つ。したがって,nil でも false でもない値が「真」の役割をする。

&&|| と同じ意味で, 演算優先順位の低い andor がある。 これはある条件の成否によって処理を行なうかどうかを決める 短縮表記として有用である。

条件 and 
条件 or 

のように書くと,前者は条件が成り立つときに後続する文を評価し, 後者は条件が成り立たないときに後続する文を評価することになる。

また,条件演算子 ? も文の短縮化に有用である。

条件 ? 1 : 2

は,この表現全体として値を返す。 条件 が成り立つときには1の値を, そうでないときには 2 の値を返す。

算術演算子

数値の演算は他の言語と共通するものが多い。 ただし,C由来のインクリメント/デクリメント演算子の ++-- はない。

演算子 意味
+ 加算
* 乗算
- 減算
/ 除算
% 剰余
** べき乗
=通常代入 例: x=5 でxに5が代入される
+=加算代入 例: x+=5 でxが5増える
*=乗算代入 例: x*=5 でxが5倍になる
-=減算代入 例: x-=5 でxが5減る
/=除算代入 例: x/=5 でxが1/5になる
%=剰余代入 例: x%=5 は x = x%5 と同じ
**=べき乗代入 例: x**=5 は x=x**5と同じ

メソッド定義

メソッド定義は以下の形式で行なう。

def メソッド(引数リスト)
  定義本体
end

の形式で メソッド を定義する。

def foo(x, y)
end

とすると,2つの引数を取るメソッド foo が定義される。 xy仮引数と呼び, 呼ぶときに渡された2つの値がその順番で代入される。定義した foo メソッドを呼ぶときは,

foo(a, b)

のようにする。メソッドの引数を省略可能とすることもできる。

def bar(x, y="foo")
end

とすると第2引数を省略可能になる。省略時の値が "foo" となる。

繰り返し

数を基準とした繰り返し

配列

値の1次元的な集合を表すのが配列。 Rubyでは大括弧内にカンマ区切りで表記する。 先頭要素は添字0でアクセスする。 Rubyでは配列の要素にどんな種類の値が入っても構わない。

[1, nil, "foo", [2, true], 3.14]

という配列も可能で,これは第0要素から順に 整数の1,nil,文字列の "foo", 配列の [2, true],浮動小数点数の3.14が格納されている。

配列に備わっている代表的なメソッドには以下のものがある。

length要素数を返す
sort並べ替え
reverse逆順化
uniq重複要素の削除
delete(val)指定要素の削除
shift先頭要素を取り出して削除
unshift(val)要素を配列の先頭に追加
<< val要素を配列の末尾に追加
index(val)指定要素の位置を返す

ブロックを伴うメソッドで,要素を1つずつ変数に代入し,ブロックの評価を 繰り返して加工したり集めたりすることができる。

each {|変数| ブロック} 要素を1つずつ取り出し変数 に順次代入して ブロック を繰り返す
collect {|変数| ブロック} 要素を1つずつ取り出し変数 に順次代入して ブロック を評価した値を配列で返す
select {|変数| ブロック} 要素を1つずつ取り出し変数 に順次代入して ブロック を評価して真になったときの要素を集めた配列を返す
reject {|変数| ブロック} 要素を1つずつ取り出し変数 に順次代入して ブロック を評価して真になったときの要素を以外 を集めた配列を返す
reject! {|変数| ブロック} reject と同様だが元の配列自身を直接操作して 該当要素を取り除く

配列要素を一つずつ取り出して繰り返す処理は以下のように eachfor を利用する。

# eachを使う場合
array.each do |val|
  # valに要素が順に入る
end

# forを使う場合
for val in array do
  # valに要素が順に入る
end

ハッシュ

配列の添字を任意の値にできるものがハッシュである。 ハッシュ値のリテラル表記はブレース(中括弧; {})を用いる。

v = {key1 => value1,
     key2 => value2,
     key3 => value3, ...}

これは以下のように代入するのと同じである。

v = Hash.new
v[key1] = value1
v[key2] = value2
v[key3] = value3
   :

ハッシュの各要素にアクセスするときの記法は配列と同じく ブラケット(大括弧; [ ])を用いる。

x = v[key]

ハッシュに備わっている代表的なメソッドには以下のものがある。

lengthキーと値のペアの個数を返す
keysキーのみからなる配列を返す
values値のみからなる配列を返す
has_key?(key) keyと一致するキーが存在するか(trueかfalseが返る)
delete(key) keyと一致するキーと対応する値を削除する

配列のメソッドでブロックを伴って繰り返すものは,ブロック変数を 2つにするとハッシュで用いることができる。1つ目の変数にはキー, 2つ目の変数にはそれに対応する値が同時に代入されブロックが繰り返される。

ハッシュのキーと値を一組ずつ取り出して繰り返す処理は以下のように書く。

# eachを使う場合
hash.each do |key, val|
  # keyにキー,valに値が順に入る
end

# forを使う場合
for key, val in hash do
  # keyにキー,valに値が順に入る
end

ハッシュのキーに存在しないものを指定したときは通常nil を返す。

hash["detarame"]			# 存在しないキーだとする
nil

存在しないキーに対する値は default= メソッドで変更できる。

hash.default = "ありまへん"
hash["detarame"]
ありまへん

文字列

文字列リテラルはダブルクォート,またはシングルクォートで括って表す。

# いずれも同じ foo という文字列
"foo"
'foo'

ダブルクォートの中では #{ } による値の展開が行なわれる。

x = 5
"xの値は#{x}です"
xの値は5です。
'xの値は#{x}です'
xの値は#{x}です。

文字列に対する操作の代表的なメソッドを示す。[ ] 内の添字は0から始まる。添字に負の整数を与えると末尾から数える。

str.length文字列の長さを返す
str1+str2 文字列どうしの結合
str*N 文字列のN回繰り返し
str[N] 文字列のN文字目の取り出し(str[0]が先頭)
str[B,L] B文字目からL文字分の部分文字列を返す
str[B..E] B文字目からE文字目までの部分文字列を返す

ファイル操作

データの入力

Rubyプログラムにデータを与えるには,

  1. getsでコマンドライン引数のファイルを読ませる
    ./program.rb datafile.txt
    
  2. getsで標準入力を読ませる
    ./program.rb                     (その1)
    データ1
    データ2
       :
    [C-d]
    ./program.rb < datafile.txt      (その2)
    cat datafile.txt | ./program.rb  (その3)
    
  3. openで特定のテキストファイルを読ませる

の方法がある。

1と2はgetsのみで,

while line=gets
  〜処理〜
end

という構成で,3はopenメソッドを用い

open(file, "r") do |handle|
  while line=handle.gets
    〜処理〜
  end
end

という構成で作成する。

データの出力

データをファイルに書き出すにはopenメソッドのモード指定に "w" を指定する。

open(outfile, "w") do |handle|
  handle.print(....)
end

以下のプログラムは output.txt ファイルに,1から10までの数字を書き出す。

open("output.txt", "w") do |w|
  1.upto(10) do |x|
    w.printf("%d\n", x)
  end
end

openメソッドのモード

openメソッドで指定できるモードを以下に示す。このモードは Cに近いプログラミング言語ではほぼ共通である。

"r"読み込み用で開く。
"r+" 読み書き両用で開く。読み込みはファイル先頭から行なわれる。
"w" 書き込み用で開く。同名ファイルがあったら空にしてから書き込む。
"w+" 読み書き両用で開く。既存のファイルがあったら空にしてから書き込む。
"a" 書き込み用で開く。既存のファイルがあったら末尾に追加して書き込む。
"a+" 読み書き両用で開く。既存のファイルがあったら末尾に追加して書き込む。

モード指定のアルファベットに,"b" を追加するとバイナリモードの指定になり,文字列として扱う場合の文字コード 変換処理を無効化する。

文字コード指定

プログラムファイルの文字コード

Ruby1.9以降では,プログラム中に英字以外の文字を含ませる場合に, その文字コードを明示する必要がある。プログラムファイルの1行目か2行目に, 以下の1から3の書式いずれかで文字コードを記述する。

  1. # coding: Encoding
  2. # -*- coding: Encoding -*-
  3. # vim:fileencoding=Encoding

2と3の方法はテキストエディタに保存文字コードを指定する記法で, それぞれEmacs,vim 用のものである。Rubyはその記法も解釈するので, 利用するテキストエディタに即した記法を利用すると便利である。

Encodingには用いる文字コード体系に対応して, 以下のいずれかの文字コード名を指定する。

文字コード文字コード名
UTF-8utf-8
日本語EUCeuc-jp
シフトJIS
shift_jis

Rubyが認識する文字コード名一覧は 以下のようにして得ることができる(1.9以降)。

ruby -e 'p Encoding.name_list'

2,3の記法を用いる場合は,得られる一覧のうち テキストエディタの認識する文字コード名と共通のものを選ぶ必要がある。

データファイルの文字コード

これもRuby1.9以降で意識すべき点で,プログラムファイルの文字コードと 文字列として読み込むデータの文字コードは原則として統一する。 データが別の文字コードの場合は,入出力のときに変換した上で処理を進める。

ファイルからのデータ読み込みで,ファイルの文字コードが決まっている場合は openのときのモード指定に変換先文字コード指定を付け加えることで自動変換が 行なわれる。たとえば,プログラムファイルが utf-8 で, JISコード(iso-2022-jp)の入力ファイルを読み込み, なんらかの処理をした結果を euc-jp のファイルに書き出す場合は以下のようにする。

#!/usr/bin/env ruby
# coding: utf-8
open("jis.txt", "rb:iso-2022-jp:utf-8") do |j|	#JISコードはバイナリモードで
  open("euc.out", "w:euc-jp") do |e|
    while line=j.gets
      print line
      e.print line
    end
  end
end

openメソッドのモード指定の後ろにコロンで区切って 「外部Encoding」あるいは「外部Encoding:内部Encoding」 の指定を追記でき,外部Encodingを内部Encodingに自動変換して読み込んだり, あるいはその逆方向に変換してデータファイルに書き込ませることができる。

正規表現

基本表記

正規表現は / / で括る。

if /正規表現/ =~ 文字列 then
  〜文字列がマッチする場合の処理〜
end

文字列から正規表現を生成するには Regexp.new を利用する。

reg = Regexp.new(正規表現)
if reg =~ 文字列 then
  〜文字列正規表現 にマッチする場合の処理〜
end

正規表現では,特定の文字を探すための特別な働きを持つ メタキャラクタを利用できる。主なものを以下に示す。

. (ピリオド)任意の1字
[文字クラス]選択(下記参照)
*直前の正規表現の0回以上の繰り返し
+直前の正規表現の1回以上の繰り返し
?直前の正規表現の0か1の繰り返し
{N}直前の正規表現の N回の繰り返し
{N,}直前の正規表現の N回以上の繰り返し
{M,N}直前の正規表現の MN回の繰り返し
^行頭
$行末
\w英数字 [0-9A-Za-z_]
\W非英数字(\wの逆)
\s空白文字 [ \t\n\r\f]
\S非空白文字(\sの逆)
\d数字 [0-9]
\D非数字(\dの逆)
\b単語境界
\B非単語境界

文字クラスの利用例をいくつか示す。

[abc123]a, b, c, 1, 2, 3のどれか1字
[a-z]a〜zのどれか1字
[^a-z]a〜z以外のどれか1字
[-0-9]ハイフンまたは0〜9のどれか1字
[\[\]][ または ] のどちらか1字

グルーピングと後方参照

正規表現パターンの一部を ( ) で括るとその部分を一塊にできる。

/abcd?efg/
/(abcd)?efg/

たとえば前者は ? が直前の d だけに作用するが, 後者は abcd 一まとめに対して作用する。また, 括弧でグルーピングした部分にマッチした文字列は, 同じ正規表現内では \数字, 正規表現マッチを抜けた直後では $数字 で参照できる。数字 には何番目の括弧かを指定する。 たとえば以下の例を考える。

/(['"]?)[a-z]+\1/ =~ "foo"
/(['"]?)[a-z]+\1/ =~ "'foo'"

正規表現自体は「シングルクォートまたはダブルクォートが1回,または0回 現れた後ろに,小文字英字が1回以上続き,その後ろにグルーピングの1番目 が来る」という意味で,1行目で照合している文字列「foo」の場合は, クォートがないので第1グルーピングでは0回マッチ,つまり空文字なので \1 が空文字に置き換えられる。 2行目で照合している文字列「'foo'」の場合は,シングルクォートが 第1グルーピングでマッチするため,\1 はシングルクォート(')に置き換えられる。

正規表現のマッチングから抜けた直後は $1 に 空文字列,またはシングルクォートが代入されている。その他, 正規表現マッチ直後で利用できる以下の変数も有用である。

$&照合文字列のうち正規表現にマッチした部分全体
$`照合文字列のうち正規表現に マッチした部分より前の部分
$'照合文字列のうち正規表現に マッチした部分より後ろの部分

最長マッチと最短マッチ

メタキャラクタの *+ は,マッチするものが最も長くなるようにマッチングを試みる(最長マッチ)。

/Th.*s/ =~ "This is a pen."
=> 0
$&
=> "This is"

*?+? は最短マッチを試みる。

/Th.*?s/ =~ "This is a pen."
=> 0
$&
=> "This"

正規表現オプション

指定したパターンの照合方式を調整するために正規表現オプションを指定できる。 正規表現オプションは /…/ の直後に1文字,あるいは, Regexp.new() の第2引数に定数を与えて指定する。

英大文字小文字を同一視する
リテラル表記Regexp.new第2引数意味
/.../iRegexp::IGNORECASE
/.../mRegexp::MULTILINE 改行文字も . でマッチする

正規表現の文字コード

正規表現にも文字コードの概念があり,正規表現と照合対象の文字列 の文字コードに食い違いは許されない(Ruby1.9以降の場合)。 プログラムの記述されたファイルの文字コードが正規表現の文字コードとなり, 照合したい文字列の文字コードと異なるとエラーが発生する。 そのような場合は,照合文字列を正規表現の文字コードに変換する。

Rubyマニュアル 正規表現 も参照。

本日の目次

yuuji@e.koeki-u.ac.jp