通常,ソフトウェアにはいろいろなデータを入力できて,データによって, 異なった処理ができるのが一般的です.たとえば,WWWブラウザの場合, URLをデータとして入力すれば,そのURLに応じて, 対応するWWWページが表示されます. このとき,入力されるデータはURLというものですが, その実際の値は,毎回変わる可能性があります. ここでは,このようにプログラムにデータを入力する方法と 値の変わるデータを取り扱う仕組みである変数について解説します. またその中で, 前回紹介したオブジェクトとメソッドについて, もう少し詳しく説明します.
もくじ
例題
プログラムの仕様
実際にプログラムを書く前に, プログラムがどういうデータから何を計算して,何を結果とするか, 例題プログラムの仕様を考えます. 例題のプログラムの流れは次のようになるでしょう.
- 換算する金額(円)を得る
- 得られた金額(円)を換算レートに基づいてドルに換算する.
- 換算の結果を表示する
これをみると,プログラムには, 次の二つがデータとして必要であることが分かります.
- 換算する金額(円)
- 換算レート($→¥)
換算する金額はプログラム実行の際に入力される情報であり, 予め決められません. 換算レートも通常変動しますので予め決められませんが, ここでは換算レートは固定されていることにします. すると,換算する金額が何らかの形で与えられたとき, 換算レート(¥→$)を使って,換算の結果は次のように表すことができます.
[換算結果($)] = [換算する金額(¥)]÷[換算レート(¥→$)]
プログラムは,ここで得られた換算結果を表示して終了することになります.
変数 --- 値が変化するデータの取り扱い
上で述べた通り,例題のプログラムでは, 換算レート(¥→$)は固定されているとしています. たとえば,「1$=110円」としましょう. すると,換算する金額(円)が与えられたときには,次のように換算を行うことになります.
[換算結果($)] = [換算する金額(¥)]÷110
さて,ここで「換算する金額」と「換算結果」は,プログラムを実行するたびに, 異なる値になる可能性があります.これらをプログラムではどう扱うのでしょうか.
私たちが自分で計算を行う際には, 「換算する金額」を「770円」などの具体的な値に置き換えて計算を行い, 「換算結果」の値(この場合7$)を得ます. このとき,私たちは,式でデータを表している名前と 実際の値を対応させて計算をしているわけです.
そこで,プログラムでも, 異なるデータには,「換算する金額」,「換算結果」のように, それらのデータを区別できる名前をつけておくことが考えられます. プログラムを記述するときには,名前を使って式を一般的に表現しておいて, プログラムを実行するときには,与えられたデータと名前を対応させれば, 具体的な計算ができます. また計算結果のデータに名前をつけて記録しておけば, あとからその名前に対応しているデータを調べることで結果を知ることができます. なお,変化するデータだけでなく,一定の値のデータにも名前をつけておくと, 直接データを書いておくよりも,プログラムの意味が分かりやすくなります.
ここで,例題の場合について, 名前を使って,どのようにプログラムが実行できるか, 疑似的に記述してみることにします. 以下で,「名前→データ」は名前とデータを対応づけることを意味するものとします.
- 予め換算レートにD2Y_RATEという名前をつけておく.
D2Y_RATE → 110.0
- 換算する金額が入力されたら,そのデータにyenという名前をつけておく.
ここでは,770が入力されたことにする.
D2Y_RATE → 110.0 yen → 770
- 換算を行う.換算の結果にdollarという名前をつけておく.
D2Y_RATE → 110.0 yen → 770 dollar → yen ÷ D2Y_RATE
D2Y_RATE → 110.0 yen → 770 dollar → 7.0
- 換算結果を表示する.
前回学んだprintが使えるものとする.
D2Y_RATE → 110.0 yen → 770 dollar → 7.0 print dollar
プログラムの世界では, 名前とデータを対応させるものを一般に 変数(variable)といいます. 変数は,その名前(変数名)とデータを指し示す部分 (上記の例では,「→」に相当する)で構成されています. 変数は「データにくくりつける名前のついた札」であると考えてもよいかもしれません.
変数 → データ
プログラムの中で,変数名を記述することで, データと変数を新たに対応づけたり, すでに変数に対応づけられているデータを調べたりすることができます. 変数にデータを対応づけることを,代入といいます.
なお,上の例の場合のD2Y_RATEのように, 常に一定で変化することのないデータを名前と対応づけるものは, 定数(constant)といいます.
なお,ここまでの説明では,変数,定数をデータに対応づけると書いてきましたが, 前回も説明した通り, Rubyでは,データはオブジェクトですから, Rubyでは,変数あるいは定数に対応づけられるのは,オブジェクトです.
プログラム
例題のプログラムをRubyで記述すると, 以下のようになります. なお,各行の左端の数字は説明のために付けた行の番号で, プログラムには,この行番号は含まれません.
[行番号つきプログラムを別のウィンドウで開く] [行番号なしのプログラム]
このプログラム(ytod.rb)は次のように実行します.
何円を換算しますか: 1200[Enter]
1200円は,10.90909091ドルです
データの代入
3行めで,定数D2Y_RATEに対する数値の代入を行っています. 詳しくは後述しますが, Rubyでは,大文字から始まる名前は定数となります.
2 # ¥→$換算レート(固定)
3 D2Y_RATE=110.0
代入は「=」で表現します. これは等号の記号ですが, ここでは「両辺が等しい」という意味ではなく, D2Y_RATEという名前の定数に, 110.0という数値オブジェクトを対応づけるという意味になります. 数学でも,たとえば「x=5のとき,f(x) > 0」のように書いて 「xを5としたときf(x)は正である」と解釈して,xに5を対応づけるという意味で「=」を使いますね(もちろん,xが5に等しいときとも読めますが).
ところで,プログラムの世界では,次のような代入式も頻繁に現われます.
x = x + 100 # xに100を加える
数学では,このような式を書くことはありません. そもそも「等式」としてみると式自体がおかしなものになりますし, 「対応づける」という意味で使うのであれば, 数学では新しい記号を導入して,次のように書くことでしょう.
x' = x + 100 # x'をx+100とする
プログラムでの代入式は, 「まず右辺の値を計算し,その計算結果を左辺の変数に対応づける」 ものです. たとえば,代入(x=x+100)を行う前の時点で,xの値が1だったとしましょう.
x = 1 # xに1を対応づける
x = x + 100 # xに100を加える
代入式「x=x+100」は次のように解釈されます.
- 右辺(x+100)の値を計算する
- 変数xが現われているので,これに対応するデータを取得する.それは数値1である.
- 数値1に数値100を加える.結果は数値101である.
- 左辺に右辺の値を計算した結果を対応づける.
- 右辺の値は数値101である.
- 左辺の変数はxである.xに101を対応づける.
これにより,結果として,代入の前と比べてxの値が100増えることになります.
変数と定数の定義
ところで,例題のプログラムの3行めの代入は,定数への代入でした.
2 # ¥→$換算レート(固定)
3 D2Y_RATE=110.0
定数に1回目の代入を行うとそれが定数の定義になります. 定義する前に,その定数の値を参照しようとするとエラーとなります. 定数は一定のデータに対応するものですので, 定数に2回目の代入を行うと警告を受けます. 代入自体は認められます.
CONST=1
CONST=2 # ==> warning: already initialized constant CONST
print CONST,"\n" # ==> 2
print FOO,"\n" # ==> エラー (FOOの値は定義されていない)
FOO=0
print FOO,"\n" # ==> 0
変数についても,1回目の代入が定義となります. 定義する前に,その変数の値を参照しようとするとエラーとなります.
a = b + 1 # ==> エラー (bは定義されていない)
b = 2
注意: 代入式
上の説明でも,こっそりと書いていますが,代入式は「式」です. 式であるということは,計算したときに値が得られることを意味します. 代入式の値は,左辺の変数に対応づけられたデータです. 代入が式であることから,次のような式も有効です.
x = y = 0
この式は,xに右辺の式「y = 0」を計算した結果の値を代入すると解釈されます. 右辺の式「y = 0」は,やはり代入式で,yに0が代入されます. この代入式「y = 0」の値は0となります. こうしてxに代入される「y = 0」の計算結果は0と分かりましたので, xに0が代入されます(そして式全体「x=y=0」の値も0となります). 結果として,xとyの両方が0となります.
データの入力
6〜9行めで,データの入力処理を行っています.
5 # 換算する金額(円)を入力する
6 print "円をドルに換算します\n"
7 print "何円を換算しますか: "
8 buff=gets()
9 yen=buff.to_i
まず6,7行めはprintメソッドをつかって,入力をうながすメッセージを表示しています. つぎに8行めで getsメソッドでキーボードからデータを取得しています. ここで,データが入力されるまで,プログラムは先に進みません. 6行めのメッセージを表示しているのは, プログラムの内容を説明するためです. また7行めのメッセージは, ここでプログラムがデータ入力を必要としていることを明示するために表示しています.
getsは,改行までの1行のデータを読みこみます. ただし,このとき得られるのは,文字列オブジェクトです. つまり7行めでは,この文字列オブジェクトを変数buffに代入していることになります. たとえば,次のように入力を行ったものとします.
何円を換算しますか: 1200[Enter]
ここではあたかも数値を入力しているように見えますが, キーボードから入力したのは, あくまでも「1」,「2」,「0」,「0」,そして[Enter] の5文字であって,1200という数値を入力したわけではありません.
実際,buffに対応づけられているオブジェクトを見るために, 次のようにプログラムに1行追加してみて下さい.
5 # 換算する金額(円)を入力する
6 print "円をドルに換算します\n"
7 print "何円を換算しますか: "
8 buff=gets()
9 yen=buff.to_i
p buff # buffに対応するオブジェクトを表示する.
pメソッドは,オブジェクトの内容をそのまま表示するものです. この行を追加したあと,再度プログラムを実行すると,次のようになるはずです.
何円を換算しますか: 1200[Enter]
"1200\n"
1200円は,10.90909091ドルです
ここで3行めに表示されているのが,「p buff」の結果, すなわち変数buffに対応づけられているオブジェクトの内容です. ここで二重引用符に囲まれて表示されている通り,このオブジェクトは, 文字列オブジェクトで最後に「\n」,すなわち改行文字が入っています.
なお,8行目では,gets()と記述していますが, これは引数がない(引数が0個である)メソッドであることを強調するためのもので, ()を省いて,単にgetsと書いても問題ありません.
5 # 換算する金額(円)を入力する
6 print "円をドルに換算します\n"
7 print "何円を換算しますか: "
8 buff=gets #これでもOK
9 yen=buff.to_i
しかし,このように記述すると, buffにgetsという変数の値を代入しているようにも見えますので, 慣れないうちは,「()」をつけた方がよいでしょう. またメソッドの引数は常にカッコでくくるというポリシーでプログラムを書くのも一つの考え方です.
オブジェクトとメソッド再び
上で説明した通り,入力されたデータは文字列ですから, 計算を行うにはこれを数値に変換する必要があります. プログラムでは,この変換を9行めで行っています.
5 # 換算する金額(円)を入力する
6 print "円をドルに換算します\n"
7 print "何円を換算しますか: "
8 buff=gets()
9 yen=buff.to_i
9行めの式は,代入式です. 左辺はyenという変数で,右辺の計算結果をyenに代入することを意味します. ここで右辺に注目して下さい. これは,文字列オブジェクトbuffのメソッドto_iを起動して いることを意味します. その結果が式の値となります.
なお,メソッドの結果の値を,そのメソッドの返り値といいます. たとえば,getsの返り値は,キーボードから読みこんだ文字列オブジェクト, buffのto_iメソッドの返り値は,整数オブジェクトです.
前回, オブジェクトとは,データとそのデータに対する処理の機能を合わせもつ対象であり, またオブジェクトを扱って何らかの処理をする機能がメソッドであるという説明をしました. このオブジェクトの機能というのが,まさにメソッドのことです. ここで,オブジェクトとメソッドの説明をしなおせば, オブジェクトは,データとメソッドを合わせもつもので, メソッドは,オブジェクトのデータに対して,処理を行う機能であるといえます. オブジェクトのメソッドは,しばしば,上の例の通り,次のように表現されます.
オブジェクト.メソッド # 引数がない場合
オブジェクト.メソッド(引数1,引数2,...) # 引数がある場合
すでに説明している通り, 引数をくくるカッコは意味があいまいにならない限り省略できます. ここで,オブジェクトとそのメソッドの使用例を挙げてみます.
p "hello".length # ==> 5
p "こんにちは".sub("にち","ばん") # ==> "こんばんは"
最初の例では,文字列オブジェクト「"hello"」のメソッド lengthを使って,この文字列オブジェクトの長さを調べています. 次の例では,文字列オブジェクト「"こんにちは"」のメソッド subを使って,「にち」を「ばん」に置き換えています. なお先頭のpは,さきほど紹介したpメソッドです (「p オブジェクト」でオブジェクトの内容を表示します).
「オブジェクト.メソッド」とは別の形式で表現されるメソッドもあります.
p 1 + 5 # ==> 6
a = ["This","is","an","array"]
a[2] # ==> "an"
「1 + 5」は,整数オブジェクト1のメソッド「+」を, 整数オブジェクト5を引数として起動しているわけです. 二番目の例は,オブジェクトを並べた配列の中から その要素を取りだしている例です. ここでは配列オブジェクトaのメソッド「[]」で,2番目の要素を取りだしています. 3番目だと思うかもしれませんが,先頭を0と数えるため,2番目となります (最初の要素は先頭から0個離れている,次の要素は先頭から1個離れている, のように考えると分かりやすいでしょう).
オブジェクト指向の世界では,メソッドを起動することを, 「オブジェクトにメッセージを送る」という言い方をします. メッセージ(message)とは起動するメソッドのことです. またメッセージを送られるオブジェクトのことをメッセージのレシーバ(receiver)といいます.
さて,例題のプログラムに戻ると,9行めの代入式の右辺は, 文字列オブジェクトbuffに関して,その内容を整数オブジェクトに変換して 返すメソッド「to_i」を起動しているわけです.
5 # 換算する金額(円)を入力する
6 print "円をドルに換算します\n"
7 print "何円を換算しますか: "
8 buff=gets()
9 yen=buff.to_i
to_iメソッドは,文字列のうち,整数と解釈できる先頭部分を切り出して, それに対応する整数オブジェクトを返します. これにより,変数yenにキーボードから入力したデータが整数として対応づけられます.
関数的メソッド
ここまでで,メソッドがオブジェクトの機能であることを説明しました. ところが,これまでに使ってきたprint,gets,pメソッドにはレシーバがなくて, オブジェクトのメソッドとなっていません(疑問に思ったでしょうか).
これらもメソッドである限りには,何らかのオブジェクトのメソッドなのですが, これらのメソッドでは,そのレシーバが省略されているわけです. Rubyでは,このようなレシーバが省略されるメソッドが少なからずあります. このようなメソッドを関数的メソッドといいます.
演算子式
12行めで換算を行っています.
11 # ¥→$換算
12 dollar = yen / D2Y_RATE
すでに説明した通り,Rubyには,プログラムを見やすくするために, 「オブジェクト.メソッド」ではない形式で起動されるメソッドがあります. 四則演算を始めとする算術演算や論理演算はその例です. ここでは,除算「/」を行っています. プログラミング言語では,「×」と「÷」は,しばしば「*」と「/」と記述されます.
Rubyには,算術演算子として,四則演算の他に「%」(整数を整数で割った余り), 「**」(累乗)が定義されています. また論理演算子として,「&&」あるいは「and」(論理積), 「||」あるいは「or」(論理和),「!」あるいは「not」(否定)が定義されています. 演算子には優先順位がきめられていて, 優先順位の高い方から先に計算が行われます. たとえば,数学と同様に「*」と「/」は「+」と「-」に優先します.
1+2*3+4 # == 1+(2*3)+4 ==> 11
数学と同様に演算の順序を明示するには,カッコでくくります.
整数同士の除算
整数同士の除算は,商(整数)を返しますので注意が必要です. 負の整数と正の整数の除算の場合は,余りが正の整数となるように 商は定められます.
p 3/2 # ==> 1 (商1を返す.1.5を返さない)
p -3/2 # ==> -2 (-3 = -2×2 + 1)
整数データ同士の除算で,整数ではなく, 小数点以下も含めた計算結果を得たい場合には, そのままでは無理なので,整数オブジェクトのto_fメソッドで, 整数を実数(正確には浮動小数点数)に変換する必要があります(どちらか一方を変換するだけで構いません).
浮動小数点数
数には,整数と実数があります. 数学では,整数も実数の集合に含まれるものとして区別しませんが, コンピュータの内部では,実数は,整数とはまったく別に扱われます. 正確にいうと,コンピュータは,実数を扱うことはできません. コンピュータが扱うのは,実数ではなく, 有限桁数の浮動小数点数(floating point number)です. 大雑把にいうと,浮動小数点数とは,実数をある有効桁数までで打ち切った数です. つまり,コンピュータでは,実数が正しく表現されるとは限りません. 実数(浮動小数点)計算,とくに科学技術計算では, つねに誤差を考慮する必要があります.
結果の出力
例題のプログラムでは,最後に換算結果を表示しています.
14 # 結果を表示する
15 print "#{yen}円は#{dollar}ドルです\n"
表示には,前回学んだprintを使います. すでに説明した通り,printは引数をいくつでもとることができます. また文字列以外のオブジェクトを引数とすることもできます.
名前のつけ方
変数,定数などにつける名前にはルールがあります.
- 変数名
1文字めは小文字か「_」(アンダースコア)で, 2文字め以降は,英文字,数字,「_」のいずれか
(例) x, foo, this_is_a_variable, __ok - 定数名
1文字めは大文字で, 2文字め以降は,英文字,数字,「_」のいずれか
(例) X, CONSTANT, Foo, THIS_IS_NOT_A_VARIABLE
なお,Rubyには予約語があり, それらは,名前として使うことはできません. その中には,「begin」,「end」,「next」など, 一般的な名前も含まれますが, これらをプログラムで変数として使うとエラーになりますので, 注意が必要です. なお,emacsでプログラムを編集すると,予約語には色がつきます.
定数の場合, 最初が必ず大文字でなければいけないことに注意して下さい. 逆に,変数に大文字から始まる名前をつけることはできません. 定数の名前には,二文字目以降は,小文字も含めることができますが, プログラムを分かりやすくするために,小文字は含めないことを奨めます.
なお,まだ説明はしていませんが,変数には種類があります. これまでに扱ってきた変数は, 正確にはローカル変数といいます. ローカル変数以外の変数の場合には, 変数名の前に,その種類を表す特別な記号(列)を付加します.