(020) データの扱い(変数, 演算)

Ippei Kishida

Last-modified:2018/03/29 22:55:07.

前回は Ruby の実行環境を整え、簡単な文字列を出力した。 しかし毎回同じ文字列を書き出すだけのプログラムではあまりに使い途がないだろう。 そこでデータに基いて様々な動作を行うプログラミングを覚えていきたい。 まずはそのデータを格納する仕組みを理解していこう。

1 変数: データの格納場所

コンピュータで扱うデータは一般に「変数」に格納される。 実体としてはコンピュータのメモリ(記憶装置)上に置かれるのだが、 「メモリ上の何番地」とかいうことをプログラマが意識しなくても良いように、 適当な名前を付けることができる。 雑駁な喩えを出せば、「甲県乙市丙町1-2-3」のアドレスに「泉さんち」と 名前をつけて覚えるようなものだ。 プログラマが変数に名前をつけ、プログラムが実行されると、 その変数にはメモリ上でまだ使われてない空き地が割当てられ、 それからは自分がつけた名前でその場所を使うことができる。 プログラミング言語によってそのやり方は異なるが、基本的な考え方はみな同じだ。

変数とは(上述の「空き地」に置かれた)箱だと思えば良い。 さて、その箱には何を入れるべきか。 原理的には「何でも入れられる」のだが、 ここではよく使われるモノを例に、具体的なコードで試していこう。

2 文字列

文字列を変数に入れる例として、 以下のような printstring.rb というファイルを作って実行してみよう。

str = "hello, world\n"
print str

1行目で文字列型の変数を作って、 そこに「hello, world\n」という文字列データを格納している。 この「格納」という動作は、プログラミング用語で「代入」と呼ばれる。 またこれに使われる「=」は、「代入演算子」と呼ばれる。 1つのプログラムの中で、同じ変数に対する代入を何度でも行うことができる。

str は文字列型の変数(に付けられた名前)ということになる。 勿論、1つのプログラムの中に変数は何個でも作ることができる。

右辺は 「“」(ダブルクォート)に囲まれている。 このダブルクォートは基本的に2つ組として使い、 その間にある文字列を一塊の文字列データとして扱うことを意味する。

さて、ここでは文字列を変数に格納したが、文字列以外にもデータの種類がある。 他の種類のデータの扱いに移る前に、 このデータの種類を示す言葉を説明しておこう。

3 クラス・型

変数の中には何らかのデータが入れられる。 しかしその変数に入っているデータによって、扱い方を変える必要がある。 変数を箱に喩えれば、「その中に入っているのがボールであるか水であるかで扱い方が変わる」 という言い方ができるだろう。 プログラマは変数の中に入っているモノの種類を意識しなければならない。 この種類を Ruby では「クラス」と呼ぶ。 1

先の例では、「str のクラスは文字列」だと言える。 文字列クラスは String という名前が付けられており、 より厳密には「str は String クラスの変数」というべきだ。

次に String クラス以外のクラスを紹介していこう。

4 数値

数値を格納してみよう。 numeric.rb を作成する。

x=10
print x
print "\n"

y=x*3
print y

ここで代入したデータは、整数(Integer) クラスのデータだ。 整数演算で注意しなければならないのは、除算を行うときである。

x = 5
y = 2
z = x/y
print z

結果は諸君の予想と異なり、「2」と表示されたことだろう。 これはどうしたことか。 コンピュータは正確ではなかっただろうか。 しかし、コンピュータはやっぱり正確な演算を返しているのである。 この演算において分子 x も 分母 y もいずれも整数であったため、 Ruby はその答えを整数として返すのである。 もし答えを小数点を含む形で得たければ、データをそのような形(浮動小数点数クラス, Float) 2 で与える必要があったのだ。

x = 5.0
y = 2.0
z = x/y
print z

期待通り、「2.5」という答えが返されたことだろう。 このように、Ruby をはじめ殆どのプログラミング言語では 数値の整数・浮動小数点数の区別をする。 実数としての計算をしたい場合には、たとえ小数部がゼロの数であっても必ず「5.0」のように 小数点を含めた形で記述すること。 これを怠れば遠からず、「原因不明の不具合」に悩まされることだろう。

ここでおそらく諸君らは、「浮動小数点数の方が厳密なので全部それを使えば良い」 と思ったことだろう。 しかしそうは問屋がおろさない。 この問題についてはまた後の章で解説する予定だ。 しかし今はその実例を示すための知識がまだ足りないのだ。 とはいえこのまま放っておくのも気持ち悪いので、キーワードだけ以下に示しておこう。

4.1 様々な数値演算

数値(Integer と Float) に対して、ここまでで「*」と「/」という演算子を使用した。 よく使われる数値演算の演算子を Table [table20100224a] に示す。

数値演算の演算子
四則演算子 + - * /
剰余演算子 %
自己代入演算子 += -= *= /= %=

剰余演算子は整数演算でその余りを求めるもの。 小学校の算数で習うように、「5わる2は、2あまり1」なので、 5/2 は商の 2 に、5%2 は剰余の 1 になる。

自己代入演算子はプログラミングでよく使われるイディオムを簡潔に書けるようにしたものだ。 変数にある値を加えたものを、改めてその変数の値としたい場合がよくある。

x = 5
x = x + 1

この2行目に自己代入演算子を使って、以下のように書き変えられる。

x = 5
x += 1

5 文字列への数値の埋め込み

出力すべき数値がたくさんあるプログラムを考える。 たとえば、多数の数値データを分析するプログラムを作ったとしよう。 そのプログラムを実行した結果

10.2
5.2
2.28

のように出力されたらどれがどんな値を示しているのか分からない。 今そのプログラムを作ったときは分かっていても、 暫く経てばどんな順番で示したか忘れてしまっているだろう。 以下のような出力をするくらいの配慮が欲しいところだ。

平均値 10.2
分散 5.2
標準偏差 2.28

この目的のためには文字列に数値を埋め込む操作をすれば良い。

5.1 ダブルクォート中の文字列展開を使った方法

その方法の一つが以下で示す #{} を使った方法だ。

average = 10.2
variance = 5.2
standard_deviation = 2.28
# ↑ここでは代入しているが、演算によって求めたものと考える。
print "平均値 #{average}\n"
print "分散 #{variance}\n"
print "標準偏差 #{standard_deviation}\n"

print 文のダブルクォートの内側、#{} で囲まれた領域がポイントだ。 この中の変数の値を文字列として展開し、その新しい文字列が出力される。

もっとシンプルな方法もある。

average = 10.2
variance = 5.2
standard_deviation = 2.28
print "平均値 ", average, "\n"
print "分散 ", variance, "\n"
print "標準偏差 ", standard_deviation, "\n"

プログラマはその時々で使い易い方を選べば良い。

5.3 コメント

先のサンプルプログラムでは、「# ↑ここでは代入しているが……」と、 プログラムの実行に関係ない文章が挿入されている。 プログラムコード中で # から行末までは Ruby に無視される。 プログラムの動作という観点では書く必要はないが、 プログラムが読み易くなるように適切に記入する習慣を付けた方が良い。 このようにプログラムに無視される文を埋め込める機構およびその文は「コメント」と呼ばれる。

6 文字列と数値、その変換

以下の string.rb を作り、実行する前にどのような結果が得られるか予想してみよう。

val1 = "12"
val2 = "34"
print val1 + val2

次に実行してみて、結果を確認しよう。 おそらく、予想と異なる結果が出るだろう。

“12” という文字列があったとき、通常人間はすぐに 12 という数値を思い浮かべ、これと同じものと思ってしまう。 しかし実際は “1” と “2” という2個の文字の並びと、12 という数値とは異なるものである。 もうちょっと具体的な話をすると、 テキストで文字列 “12” と書くとメモリやハードディスク上では2バイト占有し [31][32](16進数表記)という形で記録されるが、 数値としての 12 を表現する場合は1バイトで事足り [0C]という形で記録される。

Ruby ではこの「文字列“12”」と「数値 12」の間の変換は簡単にできる。 文字列 に to_i という命令を作用させると整数値として評価され、 to_f という命令を作用させると浮動小数値として評価される。 要は、先のプログラムでは val1, val2 は文字列のまま「+」演算を行ってしまっていたのが 間違いのもとだと言える。 3 正しくは以下の stringtoi.rb のように書くべきである。

val1 = "12".to_i #あるいは val1 = 12
val2 = "34".to_i #あるいは val2 = 34
print val1 + val2

テキストファイルから読み込んだ数値などは文字列として扱われるため、 計算する際には数値型の変数に変換する必要がある。

7 課題

  1. 以下に示すのは 1分、1時間、1日、1月(30日)、1年(365日)、100年(閏年が24回) をそれぞれ秒で表すプログラムの雛形である。 [ ア ] 〜 [ キ ] のそれぞれに適切な数値および演算を、 [ ク ] 〜 [ ス ] のそれぞれに変数を埋めよ。

            minute  = [ ア ] # second
            hour    = [ イ ] * minute
            day     = [ ウ ] * hour
            month   = [ エ ] * day
            year    = [ オ ] * day
            year100 = [ カ ] * year + [ キ ]
    
            print "1分は   ", [ ク ], "秒\n"
            print "1時間は ", [ ケ ], "秒\n"
            print "1日は   ", [ コ ], "秒\n"
            print "1ヶ月は ", [ サ ], "秒\n"
            print "1年は   ", [ シ ], "秒\n"
            print "100年は ", [ ス ], "秒\n"
  2. string.rbstringtoi.rb を実行した結果を示せ。

  3. BMI を算出プログラムを作成し、これを提出せよ。 このプログラムを用いて適当な身長と体重の値から算出した BMI を報告せよ。


  1. 「クラス」はオブジェクト指向プログラミングにおける用語であり、 オブジェクト指向言語である C++, Java といった言語においても「クラス」が使われる。 オブジェクト指向に対応していない C 言語では「クラス」という概念はなく、 より原始的な「型」のみが使われる。 学生諸君は「クラス」と「型」の差をとりあえずは意識せず、 全て「クラス」だと思って演習を進めれば良い。 ただし、実際のプログラミングの現場では「クラス」も「型」もどちらの言葉も使われるので、 言葉としては両方覚えておいた方が良い。

    型はデータのみ定義されており、 クラスはデータとその扱い方の両方が定義に含められたもの、ということができる。 箱に入れる水そのものだけを示しているのが型、 それに汲み出す・入れるといった動作も含めて示しているのがクラス、 という喩えができるが、まあ余談だ。

  2. 何故「浮動小数点数」という長ったらしい名前になっているのかというと、 コンピュータ内部でのデータ処理の仕組みによる。 細かい話でプログラミングそのものにはあまり影響のない話なのでここでは詳説しない。

  3. ちなみに文字列の「+」演算は、それらの文字列を連結する。