ホーム‎ > ‎Ruby入門‎ > ‎

Ruby 基本編

ある程度習熟した人がまとまったプログラムを書く場合はrubyを使った方がデメリットが少ないのですが、ここでは初心者の学習に適したirbを使います。

以下では、命令の前にあるirb(main):***:*や戻り値の前にある=>は特別な理由が無い限り省略します。

基本的な演算
対話型の処理系のいいところは、入力した値がすぐ次の行に出力されることです。例えば、
> 0
0 
といった具合です。このとき、出力されるのは入れた数字ではなく、その行を計算した戻り値なのです。従って、四則演算をすれば計算結果が出力されます。
> 6 + 2 # 足し算
8
> 6 - 2 # 引き算
4
> 6 * 2 # 掛け算
12
> 6 / 2 # 割り算
3
> 6 % 2 # あまり
0

数値型
次の2つの計算結果を見比べてみましょう。
> 7 / 2
3
> 7.0 / 2
3.5
上で見たとおり、"/"は割り算の演算子です。ご存知のとおり7わる2の答えは3.5なので下の結果はより厳密であり、上の結果はその整数部分しか現れていません。これは7の表記が"7"なのか"7.0"なのかに依るものです。
Rubyには数値型として整数と浮動小数点が用意されています。そして、整数同士の演算では整数が出力されることになっているのです。従って、整数"7"を整数"2"で割った値は整数"3"であり、浮動小数点"7.0"を整数"2"で割った値は浮動小数点"3.5"なのです。
"7割る2"という計算を浮動小数点の世界でする方法には、"7"を"7.0"にする他に頭に"1.0"を掛けるという方法もあります。むしろ、あとでやる変数も考えた場合、こちらの方がより一般的な方法です。
> 1.0 * 7 / 2
3.5

文字列
Rubyでは文字列を簡単に処理することができます。
> "abc"
"abc"
文字列の演算子"+"は文字列の結合を行います。
> "abc" + "def"
"abcdef"
数値を文字列にする場合は数値の後ろに".to_s"をつけ、文字列を数値にする場合は文字列の後ろに".to_i"や".to_f"を付けます。
> (6 + 7).to_s
"13"
> "123.5".to_i # 整数部分が数値化される。
123
> "123.5".to_f
123.5

変数
今までの内容だけではirbはただの電卓となんら代わりはありません。勿論、電卓のように手軽に使えるのはC言語やJavaとは違うスクリプト言語の強みですが、この電卓は関数電卓なんかよりずっと多機能です。変数を用いればもっと色々なことができます。
RubyはC言語やJavaとは異なり変数の定義は必要ありません。いくつかの例外を除く文字列を無定義のまま変数としていきなり用いることができます。
例えば、変数"a"に整数"1"を代入してみましょう。
> a = 1
1
以下ではこの"a"は整数1と同じように扱うことができるようになります。
> a + 1 # 1 + 1
2
> a.to_s # 1.to_s
"1"
変数には浮動小数点や文字列も同じように代入することができます。
> a = 1.0 # 浮動小数点
1.0
> a.to_s
"1.0"
> a = "abc" # 文字列
"abc"
> a + "def"
"abcdef"

bool値
プログラミングで登場する"値"には「整数値」「浮動小数点」「文字列」以外に「bool値」があります。bool値は"true"と"false"の2つの値からなります。trueとfalseは予約語であるためこれらを変数として用いることはできません。これは1,2,3のような数字が変数として利用できないのと同じことと考えていただければわかりやすいと思います。
bool値の演算にはand,or,notなどがあります。
> true && false # and
false
> true || false # or
true
> !true # not
false
Rubyではbool値も整数値などと同様に変数の中に入れることができます。
> a = true
true
> a || false
true 
簡単な命題の真偽をbool値として取り出すことができます。プログラミングにおいては、xについての条件P(x)はxを引数として真偽を返す関数と見ます。則ち、「命題2 > 1は真である」という数学における文章は、プログラミング的には「関数>に(2,1)を代入したらtrueが返ってくる。」と解釈されるわけです。
> 2 == 1 # 等しい
false
> 2 != 1 # 等しくない
true
> 2 > 1  # 不等号
true
> 2 >= 1 # ≧
true
ここで注意すべきことは、等号は"=="であって"="ではないということです。プログラミングでは、代入と等号は厳密に区別されます。代入"="は「第一引数として指定された変数に第二引数を代入してから代入した値を返す」関数であり、等号"=="は「第一引数と第二引数を比較して真偽を返す」関数であって、両者は全く違う働きをしているからです。

条件分岐
bool値によってプログラムの進行を変えることができます。
下のプログラムでは、ifの後に書いてあるbool値がtrueの場合はendまでの命令が実行され、ifの後に書いてある命題がfalseの場合はifからendまでの命令が無視されています。
> a = 1
1
> if a == 1 # a == 1 なので問題なく実行される。
> b = 2
> end
nil
> b
2
> if a == 2 # a == 2 ではないので b = 3 は無視される。
> b = 3
> end
nil
> b
2
極論、これだけ知っていれば条件分岐を用いた「正しい」プログラムは書けます。しかし、よく使うような処理には予め命令が用意されているものです。if文の場合には、"else"や"elsif"を用いることができます。
> a = 2
2
> if a == 1 # もし a == 1 だったら・・・。
> b = 1
> else      # もし a == 1 ではなかったら・・・。
> b = 2
> end
2
> b
2
> if a == 1     # もし a == 1 だったら・・・。
> c = 1
> elsif b == 2  # もし a == 1 ではない上に b == 2 だったら・・・。
> c = 2
> else          # もし、上の2つのどちらでもなかったら・・・。
> c = 3
> end
2
> c
2
条件分岐については他にもunlessやcaseといったものが存在しますが、ここでは省略します。

繰り返し
例えば、1から100までの数字を足していく操作をする場合、単に1 + 2 + 3 + … とやっていては書くのが大変です。こういう場合、繰り返しの構文を用います。
> a = 0
0
> for i in 1..100 # iに1から100までの数字を代入する。
> a = a + i
> end
1..100
> a
5050
forからendまでの命令が100回繰り返されます。その間、forの後ろで指定された変数iには1から100までの数字が繰り返される度に順番に入っていきます。
繰り返しの命令にはforの他にwhileも存在します。こちらは、引数のbool値がtrueの間は処理を続けます。
> a = 0
0
> i = 0
0
> while i < 100 # iが100未満の間続ける。
> i = i + 1
> a = a + i
> end
nil
> a
5050
繰り返しから抜けたい場合はbreakを書きます。
> a = 0
0
> b = 1
1
> while true
> a = 1
> break
> b = 2
> end
nil
> a
1
> b # breakの後の b = 2 が飛ばされている。
1


練習問題
84697は素数か?

答え
下のプログラムをprimetest.rbに入力する。
x = 84697
b = true
for i in 2..(x-1)   # iに2からx-1までの数字を代入する。
    if (x % i == 0) # もし x が i で割り切れたら、
        b = false   # b に false を代入して
        break       # 繰り返しを終了する。
    end
end
if b
    puts "prime"
else
    puts "not prime"
end
実行結果は次のようになる。
$ ruby primetest.rb
prime
則ち、84697は素数である。

配列
いくつかの変数を統一的に扱いたい場合があります。例えば、学校などでクラスの成績や特徴を管理する場合、クラス全員をバラバラに覚えておくより、出席番号順に纏めて整理した方が断然効率がいいです。そのようなことが配列を用いれば簡単にできます。
> TAMAM1 = ["Tanimura","Ohori","Takahashi"]
["Tanimura","Ohori","Takahashi"]
> SENMON = ["geometry","algebra","analysis"]
["geometry","algebra","analysis"]
> TAMAM1[0] # 0番目参照
"Tanimura"
> SENMON[0]
"geometry"
こうしておけば、0番は谷村で専門が幾何であることが直ちにわかります。例えば、大堀の番号が知りたい場合には、
> for i in 0..2
> if TAMAM1[i] == "Ohori"
> break
> end
> end
nil
> i
1
とすればわかります。
しかし、この「谷村、大堀、高橋」という順番に特別な意味はありません。3人だけならこれでいいですが、もっと人数が増えてくると何かと不便なことも起きてきます。例えば、アルファベット順に並べ替えた方がより統一的な管理が可能になります。そのためには、次を実行すれば十分です。
> TAMAM1 = TAMAM1.sort
Rubyは文字列に対しても大小関係が定義されているため、このような横着ができるのです。しかし、これでは問題があります。これでTAMAM1の順番を「大堀、高橋、谷村」にしてしまうと、SENMONと辻褄が合わず、これを是正するには大変な面倒が生じてしまいます。大堀くんに幾何学を、高橋くんに代数学を、谷村くんに解析学を勉強させるのも一つの手ですが、そんなことをしなくても次のようにすれば全てが解決します。
> TAMAM1 = []
> TAMAM1[0] = ["Tanimura","geometry"]
> TAMAM1[1] = ["Ohori","algebra"]
> TAMAM1[2] = ["Takahashi","analysis"]
> TAMAM1 = TAMAM1.sort
このように配列を二重構造にすればいいのです。この場合、1番の人の専門だけが知りたい場合には、
> TAMAM1[1][1]
"analysis"
とすればいいです。
配列にはsortの他にも便利な命令が沢山用意されていますが、やはりよく使うのはlengthでしょう。
> a = [2,1,3]
[2,1,3]
> a.length # 配列の長さ
3


メソッド(関数)
練習問題を思い出してみましょう。
x = 84697
b = true
for i in 2..(x-1)
    if (x % i == 0)
        b = false
        break
    end
end
if b
    print "prime"
else
    print "not prime"
end
これは84697が素数かどうかを判別するプログラムでした。しかし、これが例えば 57781, 62507, 75327, 84687のうち素数でないものはどれか、という問題であった場合、これを4つコピペして並べることになってしまいます。これを避けるために用いるのが関数です。Rubyでは関数のことをメソッドと呼びます。
メソッドをirbの処理中に定義してもいいのですが、一度作ったらいつでも使えるように予めファイルに保存しておいて、必要になったら呼び出すという形式をとるのが普通です。引数xに対してそれが素数かどうかを判定してbool値を返すis_prime?を定義するソースコードは次のようになります。
def is_prime?(x)
    b = true
    for i in 2..(x-1)
        if (x % i == 0)
            b = false
            break
        end
    end
    b                   # 定義の最後の行に書いてあるものが戻り値になる。
end
このソースコードをis_prime.rbに保存しておきましょう。Rubyのソースコードを呼び出すにはloadという命令を使います。
> load "is_prime.rb"
true
> is_prime?(3)
true
> is_prime?(4)
false
さて、通常は定義の最後のものがメソッドの戻り値になるのですが、returnを用いるとメソッドを処理の途中で強制終了させることができます。今回の場合、それを用いたほうがソースコードがすっきりします。
def is_prime?(x)
    for i in 2..(x-1)
        if (x % i == 0)
            return false
        end
    end
    true
end

nil
ここまでで手続き型プログラミングの最低限のことは網羅したつもりです。皆さんも慣れてきたところで、今まで見てみぬフリをしてきた"nil"について解説します。
今まで沢山nilという文字列が出てきました。これは「何もない」という意味です。数字に0があるように、また、集合論に空集合があるように、プログラミングにもnilというものが存在します。例えば、配列の何も入っていない部分にはnilが入っているとみなされます。
> a = []
[]
> a[5]
nil
メソッドprintは入力された文字列を表示してからnilを返すプログラムです。
> print "Hello, world."
Hello, world.=> nil

練習問題
素数の一覧を作成するアルゴリズムとして、エラトステネスのふるいという有名な方法があります。
Wikipedia の記事
これを用いて引数までの素数の一覧を配列にして返すメソッドを作成してください。

答え
def primes(n)
    box = []
    prime = []
    counter = 0
    for i in 2..n
        if box[i] == nil
            prime[counter] = i
            counter = counter + 1
            for j in 2..(n/i)
                box[i * j] = 0
            end
        end
    end
    prime
end

オブジェクト
先述の通り、今までで手続き型プログラミングについての最低限のことはマスターしたと言ってもおそらく大丈夫でしょう。ここからは、とうとうオブジェクト指向プログラミングについて解説しようと思います。今までに比べると抽象度が一段上がって難しいですが、慣れすぎると思考回路がそちらにシフトしてしまって手続き型ってどうやるんだっけと一瞬思ってしまうほどに便利なものです。

オブジェクト指向プログラミングでは「クラス」「オブジェクト」「メソッド」の3つの概念からなります。まずはクラスとオブジェクトについて説明するために、谷村くんを例にとってみましょう。
谷村くんは人類です。この場合、「人類」というのは抽象的な生物の種類名です。生き物図鑑の項目名と言ってもいいでしょう。集合の名前と言ってもいいでしょうか。人類という集合の中に谷村くんという具体的な元が存在します。この文脈において、人類はクラスであり、オブジェクトは谷村くんです。則ち、クラスとは抽象的な名前であり、オブジェクトとは具体的なものです。
人類の特徴として「身長、体重」が挙げられます。人類のこれらの特徴を管理するプログラムを作成してみましょう。homosapiens.rbに人類クラスを作成してみます。
class Homosapiens # クラス名の頭文字は必ず大文字
    attr_accessor("height","weight") # Homosapiensのデータはheightとweightで構成される。
end
これを呼び出して、谷村くんの特徴を管理するオブジェクトを作成しましょう。
> load "homosapiens.rb"
true
> tanimura = Homosapiens.new # 人類「谷村」を管理するオブジェクトを作成する。
<なにかごちゃごちゃ>
> tanimura.height = "high" # 谷村の身長は高い。
"high"
> tanimura.weight = "light" # 谷村は痩せている。
"light"
しかし、登録する度にいちいちhoge.height = piyoなどとやていてはソースコードは長くなってしまいます。それを避けるために、initializeを定義します。
class Homosapiens
    attr_accessor("height","weight")

    def initialize(h,w)
        self.height = h
        self.weight = w
    end
end
これによって、谷村くんが背が高くて痩せているというのは次のように書くことができます。
> load "homosapiens.rb"
true
> tanimura = Homosapiens.new("high","light")
<何かごちゃごちゃ>
> tanimura.height
"high"

最後に、メソッドについて解説しましょう。こちらは、オブジェクトの関数をクラスの性質として組み込むものです。例えば、人類の身長が高いかどうかを判定するメソッドを組み込んでみましょう。
class Homosapiens
    attr_accessor("height","weight")

    def high?
        self.height == "high" # 引数「自分自身」はselfに代入される。
    end
end
これで大丈夫です。実際、次のようになります。
> load "homosapiens.rb"
true
> tanimura = Homosapiens.new
<なにかごちゃごちゃ>
> tanimura.height = "high"
"high"
> tanimura.weight = "light"
"light"
> tanimura.high?
true
メソッドには引数を渡すこともできます。
class Homosapiens
    attr_accessor("height","weight")

    def heightchange(text)
        self.height = text
    end
end
これを用いると、
> load "homosapiens.rb"
true
> tanimura = Homosapiens.new
<なにかごちゃごちゃ>
> tanimura.heightchange("high")
> tanimura.height
"high"
となります。

「等しい」ということ(ポインタについて)
最後に、少し難しい話をします。配列について次のような現象が起こります。
> a = [1,2,3] # aに配列[1,2,3]を代入する。
[1,2,3]
> b = a       # bにも配列[1,2,3]を代入する。
[1,2,3]
> b[1] = 4    # bだけ1番目を4にする。
4
> a           # すると、aの1番目も4になる!!!
[1,4,3]
なぜこのような現象が起こるのでしょうか?それは、b = a という文はbに配列[1,2,3]を代入しているというより、aそのもの(aのメモリ番号)を代入している為におこる現象なのです。
オブジェクトでも同じことが起こりますが、そちらの方がよりわかりやすいと思います。先程のhomosapiens.rbを例にとって考えてみましょう。
> load "homosapiens.rb"
true
> tanimura = Homosapiens.new("high","light")
<何かごちゃごちゃ>
> ohori = Homosapiens.new("high","light")
<何かごちゃごちゃ>
この場合、谷村と大堀は同じ「背が高くて痩せている」人類ですが、全然別の人類です。この2人がどうやって区別されているかというと、いつも省略しているこの「何かごちゃごちゃ」の部分が違っているのです。この「何かごちゃごちゃ」は実はデータが登録されているメモリの番号であり、両者はこれによって区別されています。
配列でもこれと同じ現象が起きています。つまり、bの1番目だけ変えたければ中身が同じ別の配列を作成しなくてはなりません。そのためにRubyの配列クラスにはcloneというメソッドが用意されています。
> a = [1,2,3]
[1,2,3]
> b = a.clone # aと中身が同じ別の配列を作成する。
[1,2,3]
> b[1] = 4
4
> a
[1,2,3]
このような混乱の原因の一端には"=="があります。一般のオブジェクトの場合、"=="はメモリの等号を表します。先程の例で言えば"tanimura == ohori"はfalseです。しかし、配列の場合には要素さえ一致していればtrueが返されます。
例えば、人類の場合はこれでよかったわけですが、平面上の点のクラスの場合、異なるオブジェクトでも座標が等しければ同じメソッドとして認識させた方がいい場合が多いです。そのためにequalsというメソッドを作るのも一つの手ですが、ここでは演算子オーバーライドという手法を紹介しておきます。則ち、次のようにすればいいのです。
class Point
    attr_accessor("x","y")

    def initialize(n,m)
        self.x = n
        self.y = m
    end

    def ==(other)
        self.x == other.x && self.y == other.y
    end

end
これで、Pointクラスには"=="が実装されました。
> p = Point.new(1,2)
> q = Point.new(1,3)
> p == q
false
> q.y = 2
2
> p == q
true

練習問題
上で定義したPointクラスに次の2つのメソッドを定義してください。
  • 座標が同じで別のPointクラスを作成するclone
  • 足し算+

答え
class Point
    attr_accessor("x","y")

    def initialize(n,m)
        self.x = n
        self.y = m
    end

    def clone
        Point.new(self.x,self.y)
    end

    def +(other)
        Point.new(self.x + other.x,self.y + other.y)
    end
end

Comments