M.Hiroi's Home Page
http://www.geocities.jp/m_hiroi/

お気楽 Ruby プログラミング入門

第 1 回 Ruby の基礎知識 データ構造編

[ PrevPage | R u b y | NextPage ]

はじめに

Ruby はまつもとゆきひろ氏によって開発されたオブジェクト指向スクリプト言語です。現在、スクリプト言語と呼ばれているプログラミング言語は、JavaScript, Perl, PHP, Python, Ruby などが有名ですが、その中で Ruby は日本人が設計・開発した唯一のスクリプト言語です。Ruby on Rails (RoR) という Web アプリケーションフレームワークがブレークしたことにより、日本国内にとどまらず全世界に普及しています。

現在、最も普及しているスクリプト言語は Web ブラウザ上で動作する JavaScript になりますが、以前は Perl が絶大な人気を誇っていました。Perl はバージョン 5 からオブジェクト指向やリファレンスなど高度な機能が追加され、汎用のプログラミング言語として様々な分野で使われるようになりました。しかしながら、これらの機能が追加されたことにより、Perl はよりいっそう複雑な言語になってしまったように思います。

これに対し、Ruby は初めからオブジェクト指向言語として設計されているので、すべてのデータをオブジェクトとして統一的に扱うことができます。また、文字列、配列、ハッシュといった高水準のデータだけではなく、モジュール、例外処理、マルチスレッドといった高度な機能がサポートされています。このように多くの機能があるにもかかわらず、Ruby はシンプルでわかりやすいプログラミング言語になっています。

まつもとゆきひろ氏によると Ruby のポリシーは「ストレスなくプログラミングを楽しむこと (enjoy programming)」とのことです。このため、Ruby には楽しくプログラミングできるような機能がたくさん盛り込まれています。また、バージョン 1.9 になると、笹田耕一氏が開発された YARV (Yet Another Ruby VM) が搭載され、遅いといわれていた Ruby の処理速度もずいぶん速くなりました。今後も幅広い分野で Ruby の普及が進むものと思われます。

本稿では、簡単なプログラムを作りながら Ruby の基本からオブジェクト指向機能まで、プログラミング言語としての Ruby の基本的な機能を一通り説明していきたいと思っております。たいしたことはできませんが、お付き合いのほどよろしくお願いいたします。

●Ruby のインストール

Ruby の処理系は公式サイト「オブジェクト指向言語 Ruby (http://www.ruby-lang.org/ja/)」からダウンロードすることができます。Windows 用のバイナリも用意されているので、簡単にインストールすることができます。また、Debian GNU/Linux および Ubuntu 系の OS では下記のコマンドで Ruby 2.3 をインストールすることができます。

sudo apt-get install ruby2.3

Ruby を学ぶ場合、irb (interactive ruby) というツールを使うと大変便利です。irb は Ruby の配布パッケージに含まれています。irb を起動すると、メッセージとプロンプトが表示されて、対話モードで Ruby が起動されます。その状態で Ruby の式を入力して簡単に実行することができます。

$ irb
irb(main):001:0> 1 + 2 * 3
=> 7
irb(main):002:0>

終了する場合は exit と入力してください。本稿は Lubuntu 16.04 on VirtualBox 上で irb を使って Ruby (version 2.3) の基本的な機能を説明します。

●Ruby のプログラム

Ruby は Perl と同じく手続き型のプログラミング言語です。Perl と同様に、Ruby にはプログラムの実行を制御する「文」、データを格納する「変数」、決められた処理を行う「関数」[*1] があります。変数と関数は名前をつけて区別します。名前には、英数字とアンダースコア _ が使えます。英大文字と英小文字は区別されるので、FOO と Foo と foo は異なる名前と判断されます。

Perl は文の最後をセミコロン ( ; ) で区切りますが、Ruby はセミコロンを付ける必要はありません。関数は Ruby にあらかじめ用意されている「組み込み関数」のほかに、私達ユーザが定義することもできます。もちろん「再帰呼び出し」も可能です。

Ruby にはいろいろなデータが用意されています。データの種類を「データ型 (data type)」と呼びます。Ruby の場合、基本的なデータ型には「数」と「文字列」があります。高水準なデータ型として「配列」や「ハッシュ」などがあります。

また、Ruby はオブジェクト指向プログラミングに対応しているので、クラス、メソッド、継承などのオブジェクト指向機能があります。とくに、継承で使われる Mix-in という機能は Ruby の大きな特徴になっています。このほかにも例外処理やマルチスレッドといった高度な機能もサポートされています。

-- note --------
[*1] Ruby の場合、関数はすべてメソッドになります。Ruby は関数のようなメソッドも定義することができるので、ここでは簡単に関数と呼ぶことにします。

●Ruby のデータ型

あらかじめ Ruby に用意されている基本的なデータ型について簡単に説明します。

●数

数の基本は整数 (integer) と浮動小数点数 (floating point number) です。さっそく irb を使って簡単な例を示しましょう。本稿では irb のプロンプトを irb> で表すことにします。

irb> a = 100
=> 100
irb> a
=> 100
irb> print a
100=> nil
irb> puts a
100
=> nil
irb> b = 1.234
=> 1.234
irb> b
=> 1.234
irb> c = 2 ** 100
=> 1267650600228229401496703205376
irb> c
=> 1267650600228229401496703205376

Ruby はC/C++や Java と違い、あらかじめ変数やそのデータ型を宣言する必要はありません。Ruby では変数に値をセットするだけで、その変数を使用することができます。変数に値をセットすることを「代入」といいます。代入には = を使います [*2]。= は代入した値を返します。irb で変数名を入力するとその値が表示されます。また、関数 print() または puts() を使って値を表示することもできます。なお、値をセットしていない変数にアクセスするとエラーになります。

print() と puts() は指定されたデータを画面 (標準出力) へ出力します。データを出力したあと、puts() は改行しますが print() は改行しません。これらの関数は nil を返します。nil は Ruby のデータの一種で、中身がないことを表す特別なデータです。print() や puts() はデータを表示することが本来の目的で、その返り値に意味はありません。このような場合、Ruby では nil を返すことがよくあります。

変数 a には整数 100 を代入し、変数 b には浮動小数点数 1.234 を代入しています。Ruby の整数には桁数の制限がありません。** はべき乗を計算する演算子で、2 の100 乗のような大きな値でも計算することができます。主な算術演算子を表 1 に示します。

表 1 : 算術演算子
演算子操作
-x x の符号を反転
x + y x と y の和
x - y x と y の差
x * y x と y の積
x / y x と y の商
x % y x と y の剰余
x ** y x の y 乗

浮動小数点数はC言語の倍精度浮動小数点数と同じで、範囲は約 1e-307 から 1e+308 までです。このほかに、有理数 (分数) を扱う Rational と、複素数を扱う Complex があります。

-- note --------
[*2] 代入は x = y = z = 0 のように、複数の変数に同じ値を代入することができます。

●文字列

文字列 (string) はシングルクオート ' で囲むか、ダブルクオート " で囲んで表します。

irb> a = "hello, world"
=> "hello, world"

文字列には「エスケープシーケンス」を含めることができます。これは、画面に表示することができない文字を表すのに用いられる方法です。よく使われる記号に改行を表す \n とタブを表す \t があります。

irb> print "abc\ndef"
abc
def=> nil
irb> print "abc\tdef"
abc    def=> nil

ダブルクオートの場合、エスケープシーケンスの解釈が行われますが、シングルクオートの場合は '\\' と '\'' 以外のエスケープシーケンスは解釈されません。また、ダブルクオートでは「式展開」を行うことができます。"#{ }" で囲まれた部分の式は、その式の結果が文字列に埋め込まれます。シングルクオートの場合、式展開は行われません。

irb> "1 + 2 = #{1 + 2}"
=> "1 + 2 = 3"

Ruby では、バッククオート (`) で囲まれた文字列はコマンドとして扱われます。まず最初に変数展開が行われ、その結果がシェルに渡されます。コマンドの実行によって得られた出力が、その文字列の値となります。コマンドの終了ステータスは特殊変数 $? に格納されます。この動作は Perl と同じです。たとえば UNIX 系 OS の場合、print `ls` を実行するとカレントディレクトリの内容が表示されます。

このほかにも、% を使った文字列表現があります。詳細は Ruby のリファレンス %記法 をお読みください。

●ヒアドキュメント

ヒアドキュメント (here-document) とは UNIX 系 OS のシェルにある機能で、<< と次に書かれた記号 (終端記号) を指定すると、次の行から終端記号までの複数行を一つの文字列として扱います。簡単な例を示しましょう。

print <<EOF
hello, world
EOF

画面には hello, world と表示されます。<< と EOF の間には空白を入れてはいけません。空白は空の識別子とみなされ、最初に現れる空行までが文字列として扱われます。また、終端記号だけが現れる行までを文字列として扱うので、終端記号の前後に空白を入れてはいけません。

<< の後ろに定義する終端記号は " や ' や ` で囲むことができます。" で囲まれた場合はヒアドキュメント中で変数展開が行われます。なにも囲まない場合も同じです。' で囲んだ場合は変数展開は行われません。これは文字列の場合と同じです。

` で囲んだ場合は、各行をコマンドとして実行し、その実行結果 (コマンドが標準出力へ出力したデータ) がドキュメントの内容になります。たとえば、UNIX 系 OS でカレントディレクトリの内容を取り込みたい場合は、次のようにすればいいでしょう。

print <<`EOF`
ls *.txt
EOF

これで拡張子が txt のファイル情報を取り込むことができます。

●配列

Ruby の配列 (array) はデータを一列に並べたもので、C/C++の 1 次元配列や Perl の配列と同様のデータ構造です。配列に格納されたデータを要素といいます。配列の要素は整数値で指定します。これを添字といいます。Ruby の場合、Perl やC/C++と同じく添字は 0 から始まります。

配列は角カッコ '[' と ']' で囲み、要素をカンマ ( , ) で区切って表します。[ ] は要素が一つもない空の配列になります。

簡単な例を示します。

irb> ary1 = [10, 20, 30, 40, 50]
=> [10, 20, 30, 40, 50]
irb> a = ary1[0]
=> 10
irb> a
=> 10
irb> ary1[4] = 100
=> 100
irb> ary1
=> [10, 20, 30, 40, 100]
irb> ary1[-1]
=> 100
irb> ary1[-2]
=> 40

C/C++の場合、配列の大きさを宣言する必要がありますが、Ruby の配列は大きさを宣言する必要はありません。配列の大きさは Ruby が自動的に調整してくれます。大きさを自由に変えることができる配列を「可変長配列」といいます。Perl や Python も可変長配列をサポートしています。

配列の要素は角カッコ [ ] を使ってアクセスします。これはC/C++や Perl と同じです。配列の要素を取り出して変数に代入することも、配列の要素を書き換えることもできます。添字に負の整数を指定すると、配列の後ろから数えます。ary1[-1] は最後尾の要素 100 になり、ary1[-2] は後ろから 2 番目の要素 40 になります。

配列の要素には、いろいろなデータ型が混在していてもかまいません。また、要素に式を書くこともできます。

irb> ary2 = ["a", 10, "b", 20, "c", 30]
=> ["a", 10, "b", 20, "c", 30]
irb> ary2[2]
=> "b"
irb> ary2[3]
=> 20
irb> [1 + 2, 3 - 4, 5 * 6]
=> [3, -1, 30]

ary2 の配列は、0, 2, 4 番目の要素が文字列で、1, 3, 5 番目の要素が整数になっています。要素に式を書くと、その式の評価結果が要素になります。

配列は入れ子にすることができます。つまり、配列の要素に配列を入れてもかまいません。これで多次元配列を表すことができます。

irb> ary3 = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
=> [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
irb> ary3[0]
=> [1, 2, 3]
irb> ary3[0][1]
=> 2

ary3 は配列の中に配列を入れることで 2 次元配列を表しています。ary3 の 0 番目の要素は配列 [1, 2, 3] で、その配列の 1 番目の要素は 2 です。この要素は角カッコを 2 つ使って ary3[0][1] とアクセスすることができます。ary3[0] で 0 番目の配列を取り出し、その配列の 1 番目の要素を [1] で取り出します。

●シーケンス

配列と文字列にはデータを一列に並べたデータ構造という共通点があります。このようなデータをシーケンス (sequence) と呼びます。Ruby にはシーケンスに適用できる共通の操作があります。角カッコ [ ] は文字列にも適用できます。

irb> s = "abcdef"
=> "abcdef"
irb> s[0]
=> "a"

Ruby には「文字」を表すデータ型がないので、s[0] の値は文字列 "a" になります。s[0] = "A" のように代入操作 [*3] を行うこともできます。

シーケンスには演算子 + と * を適用することができます。

irb> "abc" + "def"
=> "abcdef"
irb> "abc" * 3
=> "abcabcabc"
irb> [1, 2, 3] + [4, 5, 6]
=> [1, 2, 3, 4, 5, 6]
irb> [0] * 8
=> [0, 0, 0, 0, 0, 0, 0, 0]

演算子 + はシーケンスを連結した新しいシーケンスを作り、演算子 * はシーケンスを指定した回数だけ繰り返した新しいシーケンスを作ります。

Ruby はシーケンスの部分列を簡単に取り出すことができます。この操作を「スライス」と呼びます。表 2 にスライス操作を示します。

表 2 : スライス操作
操作意味
seq[s..e] s から e - 1 までのコピー
seq[s, len] s から s + len - 1 までのコピー
seq[s..e] = seq スライスへの代入
seq[s, len] = seq 同上

seq はシーケンスを表します。スライスによって取り出された部分列は元のシーケンスをコピーしたものです。整数値 s と e で部分列を指定します。s 番目の要素が部分列の先頭になり、e 番目の要素が最後尾になります。また、開始位置 s と長さ len を指定して、部分列を取り出すこともできます。

簡単な例を示します。

irb> ary = [0, 1, 2, 3, 4, 5, 6, 7]
=> [0, 1, 2, 3, 4, 5, 6, 7]
irb> ary[2..6]
=> [2, 3, 4, 5, 6]
irb> ary[2..-1]
=> [2, 3, 4, 5, 6, 7]
irb> str = "abcdefg"
=> "abcdefg"
irb> str[2..4]
=> "cde"
irb> str[0..-1]
=> "abcdefg"

範囲指定を s..-1 にすると、s 番目から最後尾までの部分列を取り出すことができます。

スライスへの代入は強力な操作です。指定した部分列を削除し、その部分に右辺のシーケンスを挿入します。簡単な例を示します。

irb> ary = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
irb> ary[2..2]
=> [3]
irb> ary[2..2] = [100, 200]
=> [100, 200]
irb> ary
=> [1, 2, 100, 200, 4, 5]
irb>ary[2, 0]
=> []
irb> ary[2, 0] = [10, 20]
=> [10, 20]
irb> ary
=> [1, 2, 10, 20, 100, 200, 4, 5]
irb> ary[2..5]
=> [10, 20, 100, 200]
irb> ary[2..5] = []
=> []
irb> ary
=> [1, 2, 4, 5]

ary[2..2] は [3] になりますが、そこに [100, 200] を代入すると、3 が削除されて 100 と 200 が挿入されます。ary[2, 0] は空の配列になりますが、そこに [10, 20] を挿入すると、要素 2 の後ろに 10 と 20 が挿入されます。空の配列を代入すると、要素を削除することができます。同様の操作は mutable (書き換え可) な文字列でも行うことができます。

-- note --------
[*3] Ruby 3.0 になると文字列リテラルは immutable (書き換え不可) なデータになるそうです。Ruby 2.3 の場合、メソッド freeze を実行すると、その文字列は immutable になります。また、ファイルの先頭でマジックコメント # frozen_string_literal: true を記述すると、文字列リテラルは immutable になります。Ruby では # からその行の最後までがコメントになります。

●メソッドの呼び出し方法

Ruby の場合、文字列や配列の操作はメソッドとして定義されています。メソッドは次の形式で呼び出します。

object.method(args, ...)

object はデータのことで、その後ろにドット ( . ) を付けて、メソッド名と引数を続けて書きます。Ruby はオブジェクト指向プログラミング言語なので、すべてのデータをオブジェクトとして扱うことができます。メソッドはオブジェクトを操作する関数のことで、Ruby では多くの操作がメソッドとして定義されています。オブジェクト指向機能は本連載の後半で詳しく説明します。

たとえば、シーケンスに格納されている要素数はメソッド size() または length() で求めることができます。一般に、シーケンスの要素数を「長さ」といいます。簡単な例を示しましょう。

irb> ary = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
irb> ary.size
=> 5
irb> "abcdefg".size
=> 7

Ruby では関数 (メソッド) を呼び出すとき、引数を囲うカッコを省略することができます。

●配列の操作メソッド

配列には便利なメソッドが多数用意されています。主なメソッドを表 3 に示します。

表 3 : 配列を操作する主なメソッド
メソッド操作
ary.push(x) ary の最後尾に要素 x を追加する
ary.pop ary の最後尾から要素を取り出す
ary.unshift(x) ary の先頭に要素 x を追加する
ary.shift ary の先頭の要素を取り出す
ary.index(x) ary の要素 x を探して位置を返す
ary.insert(i,x) ary の i 番目に要素 x を挿入する
ary.delete(x) ary から要素 x をすべて削除する
ary.delete_at(i)ary の i 番目の要素を削除する

ary は配列を表します。これらの操作は要素の探索を除いて、配列をコピーせずに直接書き換えることに注意してください。このような動作を「破壊的に修正する」とか「破壊的な操作」といいます。簡単な例を示します。

irb> ary = [1, 2, 3]
=> [1, 2, 3]
irb> ary.push 4
=> [1, 2, 3, 4]
irb> ary
=> [1, 2, 3, 4]
irb> ary.pop
=> 4
irb> ary
=> [1, 2, 3]
irb> ary.insert 0, 10
=> [10, 1, 2, 3]
irb> ary
[10, 1, 2, 3]
irb> ary.delete 2
=> 2
irb> ary
=> [10, 1, 3]
irb> ary.index 3
=> 2

このように、配列は破壊的に修正されるので、変数 ary の値は書き換えられた配列になります。

●集合演算

Ruby の配列は集合 (set) として用いることもできます。集合はいくつかの要素を集めたものです。一般に、集合は重複した要素を含まず、要素の順番に意味はありません。なお、要素の重複を許す集合は多重集合 (multi set) と呼ばれます。たとえば、集合 {1, 3, 5, 7} は {7, 5, 3, 1} や {5, 3, 1, 7} と表すこともできます。このように、要素は適当に並べてもかまわないのですが、ある規則で要素を整列させておく場合もあります。

配列を集合として扱う演算子を表 4 に示します。

表 4 : 配列の集合演算
演算子操作
ary1 & ary2 ary1 と ary2 の積を求める (積集合)
ary1 | ary2 ary1 と ary2 の和を求める (和集合)
ary1 - ary2 ary2 に現れない ary1 の要素を求める (差集合)

簡単な例を示しましょう。

irb> a = [1, 2, 3, 4]
=> [1, 2, 3, 4]
irb> b = [1, 2, 5, 6]
=> [1, 2, 5, 6]
irb> a | b
=> [1, 2, 3, 4, 5, 6]
irb> a & b
=> [1, 2]
irb> a - b
=> [3, 4]
irb> b - a
=> [5, 6]

このほかにも配列には便利なメソッドが多数ありますが、使うときに詳しく説明することにします。

●ハッシュ

ハッシュ (hashing) は連想配列のことで、Perlでもハッシュ [*4] と呼ばれています。配列が整数値を使って要素を指定するのに対し、ハッシュはキー (key) というデータを使って要素を指定します。一般に、連想配列のキーには文字列が用いられますが、Ruby では他のデータもキーとして用いることができます。

ハッシュは中カッコ '{' と '}' で囲み、要素をカンマで区切って表します。要素は「キー => データ」で指定します。{ } は空のディクショナリを表します。

簡単な例を示します。

irb> h = {"foo"=>10, "bar"=>20}
=> {"foo"=>10, "bar"=>20}
irb> h["foo"]
=> 10
irb> h["foo"] = 100
=> 100
irb> h["foo"]
=> 100
irb> h["baz"] = 30
=> 30
irb> h
=> {"foo"=>100, "bar"=>20, "baz"=>30}

ハッシュのアクセスは配列と同様に角カッコ [ ] を使います。最初に、ハッシュを生成して変数 h にセットします。h["foo"] でキー "foo" のデータを取り出したり、そこにデータを代入すれば、h["foo"] の値を書き換えることができます。また、新しいキー "baz" を追加する場合は、h["baz"] に値を代入すると、ハッシュに "baz" とその値が追加されます。

このほかにも、Ruby にはハッシュを操作するメソッドが用意されています。表 5 に主なメソッドを示します。

表 5 : ハッシュの主なメソッド
メソッド操作
h.size ハッシュの要素数を返す
h.delete(key) キー key を削除する
h.clear ハッシュを空にする
h.keys ハッシュ内のキーを配列に格納して返す
h.values ハッシュ内の値を配列に格納にして返す
h.to_a [キー, 値] を格納した配列を返す

h はハッシュを表します。簡単な使用例を示しましょう。

irb> h = {"foo"=>10, "bar"=>20, "baz"=>30}
=> {"foo"=>10, "bar"=>20, "baz"=>30}
irb> h.keys
=> ["foo", "bar", "baz"]
irb> h.values
=> [10, 20, 30]
irb> h.to_a
=> [["foo", 10]. ["bar", 20], ["baz", 30]]

keys(), values(), to_a() において、データの列挙はキーがハッシュに登録された順番で行われます。

-- note --------
[*4] Perl や Ruby で連想配列をハッシュと呼ぶのは、連想配列を実現するアルゴリズムに「ハッシュ法 (hashing)」が用いられているからです。

●シンボル

Ruby の場合、名前の前にコロン ( : ) をつけると、名前を「シンボル (Symbol)」[*5] という immutable (書き換え不可) なデータに変換します。シンボルは文字列や変数名を特定の数値に変換したものです。同じ内容の文字列は同一のシンボルに変換されます。つまり、Ruby の中で同じ名前のシンボルはひとつしか存在しません。文字列からシンボルを得るにはメソッド intern() や to_sym() を使います。

irb> :foo
=> :foo
irb> :bar
=> :bar
irb> "foo".intern
=> :foo
irb> "bar".to_sym
=> :bar

ハッシュのキーにシンボルを使う場合は次のように指定することができます。

{名前1: 値1, 名前2: 値2, ... } 

{ ... } の中で、名前の後ろにコロン ( : ) を付けると、それをシンボルとして扱います。簡単な例を示しましょう。

irb> h = {foo: 10, bar: 20, baz: 30}
=> {:foo=>10, :bar=>20, :baz=>30}
irb> h[:foo]
=> 10
irb> h[:foo] = 100
=> 100
irb> h
=> {:foo=>100, :bar=>20, :baz=>30}
-- note --------
[*5] Lisp / Scheme にもシンボルというデータ型があります。ここでは名前 (識別子) を表すデータ型と考えてください。

●コラム TMTOWTDI

TMTOWTDI (ティムトアディ) は Perl のポリシーで、There's More Than One Way To Do It (やり方はいくらでもある) という意味です。このポリシーにより、Perl のプログラムは様々なスタイルで記述することができるようになっています。このポリシーは Ruby にも影響を与えていて、あるプログラムを作るのに何通りもの方法が用意されています。はじめのうちはどの方法を使ったらよいか混乱するかもしれませんが、プログラムを作っていくうちに、自分にとって使いやすい方法が見つかると思います。やり方は一通りではないので、自分なりの方法でプログラムを作ってみてください。

●おわりに

今回は Ruby に用意されている基本的なデータ型について簡単に説明しました。次回は Ruby の基本的な制御構造について説明します。


初版 2008 年 10 月 11 日
改訂 2017 年 1 月 15 日

Copyright (C) 2008-2017 Makoto Hiroi
All rights reserved.

[ PrevPage | R u b y | NextPage ]