[プログラミング演習(Ruby) >  変数とデータ入力]

変数とデータ入力

通常,ソフトウェアにはいろいろなデータを入力できて,データによってた処理ができるのが一般的です.たとえば,WWWブラウザの場合,URLをデータとして入力すれば,そのURLに応じたWWWページが表示されます. 入力されるURLの値は,毎回変わる可能性があります.

ここでは,このようにプログラムにデータを入力する方法と値の変わるデータを取り扱う仕組みである変数について解説します. またその中で,オブジェクトとメソッドについて少し詳しく説明します(「メッセージの表示,オブジェクト,メソッド」も参照のこと).

もくじ

  1. 例題
  2. プログラムの仕様
  3. 変数 --- 値が変化するデータの取り扱い
  4. プログラム
  5. データの代入
  6. データの入力
  7. オブジェクトとメソッド再び
  8. 演算子式
  9. 結果の出力
  10. 名前のつけ方
  11. サンプルプログラム

例題

円をドルに換算する,つまり円で金額を入力して,それが何ドルになるかを計算する.

プログラムの仕様

実際にプログラムを書く前に,プログラムがどういうデータから何を計算して,何を結果とするか,例題プログラムの仕様を考えます. 今回の場合は書くまでもないかもしれませんが,例題のプログラムの流れは次のようになるでしょう.

  1. 換算する金額(円)を得る
  2. 得られた金額(円)を換算レートに基づいてドルに換算する.
  3. 換算の結果を表示する

これをみると,プログラムには次の二つがデータとして必要であることが分かります.

換算する金額はプログラム実行の際に入力されるデータであり予め決められません. 換算レートも通常変動しますので予め決められませんが,ここでは換算レートは固定されていることにします(固定相場というわけです). すると,換算する金額が何らかの形で与えられたとき, 換算レート(¥→$)を使って,換算の結果は次のように表すことができます.

  [換算結果($)] = [換算する金額(¥)]÷[換算レート(¥→$)]

プログラムは,ここで得られた換算結果を表示して終了することになります.

変数 --- 値が変化するデータの取り扱い

上で述べた通り,例題のプログラムでは, 換算レート(¥→$)は固定されているとしています. たとえば,「1$=110円」としましょう. すると,換算する金額(円)が与えられたときには,次のように換算を行うことになります.

  [換算結果($)] = [換算する金額(¥)]÷110

さて,ここで「換算する金額」と「換算結果」は,プログラムを実行するたびに, 異なる値になる可能性があります.これらをプログラムではどう扱うのでしょうか.

私たちが自分で計算を行う際には, 「換算する金額」に「770円」などの具体的な値を当てはめて計算を行い, 「換算結果」の値(この場合7$)を得ます. このとき,私たちは,式でデータを表している名前と実際の値を対応させて計算をしているわけです.

そこでプログラムでも,各データに「換算する金額」あるいは「換算結果」のように,それらのデータを区別できる名前をつけておくことが考えられます. プログラムを記述するときには「名前」を使って式を一般的に表現しておいて, プログラムを実行するときには実際に計算に使うデータと「名前」を対応させれば,計算が実行できます. また計算結果として得られたデータにも「名前」をつけて記録しておきます. そうすることで,あとからその名前に対応しているデータを調べて結果を表示したり,あるいは別の計算に利用したりできます. なお,変化するデータだけでなく,一定の値のデータにも名前をつけておくと, そのままデータの値を書いておくよりも,プログラムの意味が分かりやすくなります.

ここで,例題の場合について,名前を使って,どのようにプログラムが実行できるか, 疑似的に記述してみることにします. 以下で,「名前→データ」は名前とデータを対応づけることを意味するものとします.

  1. 予め換算レートにD2Y_RATEという名前をつけておく.
      D2Y_RATE → 110.0
    
  2. 換算する金額が入力されたら,そのデータにyenという名前をつけておく. ここでは,770が入力されたことにする.
      D2Y_RATE → 110.0
      yen → 770
    
  3. 換算を行う.換算の結果にdollarという名前をつけておく.
      D2Y_RATE → 110.0
      yen → 770
      dollar → yen ÷ D2Y_RATE
    
    換算する金額はyen,換算レートはD2Y_RATEという名前をつけてあるので, それらの名前を使って換算を表す式を作っておいて, 名前に対応しているデータを当てはめれば換算できる. ここでdollarを換算式に対応づけて記述している部分は,まず換算を行って, (そのあとで)換算結果をdollarに対応づけることを意味するものとする. つまり,実際にdollarに対応づけられるのは,換算の結果となる.
      D2Y_RATE → 110.0
      yen → 770
      dollar → 7.0
    
  4. 換算結果を表示する. printが使えるものとする.
      D2Y_RATE → 110.0
      yen → 770
      dollar → 7.0
      print dollar
    
    まずdollarに対応するデータを調べて,(そのあとで)その値を表示すればよい.

プログラムの世界では, 名前とデータを対応させる仕組みを一般に変数(variable)といいます. 変数は,その名前(変数名)とデータを指し示す部分(上記の例では,「→」に相当する)で構成されています. 変数は「データにくくりつける名前のついた札」であると考えてもよいかもしれません.

  変数名 → データ

プログラムに変数名を記述することで,データと変数を新たに対応づけたり,すでに変数に対応づけられているデータを調べたりすることができます. 変数にデータを対応づけることを代入といいます. なお,上の例の場合のD2Y_RATEのように常に一定で変化することのないデータを名前と対応づけるものは定数(constant)といいます.

ところで,ここまでの説明では,変数,定数をデータに対応づけると書いてきましたが, Rubyでは,データはオブジェクトですから,Rubyで変数あるいは定数に対応づけられるのはオブジェクトです.

プログラム

例題のプログラムをRubyで記述すると以下のようになります. なお,各行の左端の数字は説明のために付けた行の番号で,プログラムにはこの行番号は含まれません.

ytod.rb
[行番号つきプログラムを別のウィンドウで開く] [行番号なしのプログラム]

このプログラム(ytod.rb)は次のように実行します.

$ ruby ytod.rb
何円を換算しますか: 1200[Enter]
1200円は10.9090909090909ドルです
(ただし1ドル=110.0円としています)

データの代入

まず最初に定数D2Y_RATEに対する数値の代入を行っています. 詳しくは後述しますが,Rubyでは大文字から始まる名前は定数となります.


10  # ¥→$換算レート(固定)
11  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」は次のように解釈されます.

  1. 右辺(x+100)の値を計算する
    1. 変数xが現われているので,これに対応するデータを取得する.それは数値1である.
    2. 数値1に数値100を加える.結果は数値101である.
  2. 左辺に右辺の値を計算した結果を対応づける.
    1. 右辺の値は数値101である.
    2. 左辺の変数はxである.xに101を対応づける.

これにより,結果として,代入の前と比べてxの値が100増えることになります.

変数と定数の定義

D2Y_RATEの定義は定数への代入によって行われていたことを思い出してください.


10  # ¥→$換算レート(固定)
11  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となります.

データの入力

13〜17行めでデータの入力処理を行っています.


13  # 換算する金額(円)を入力する
14  print "円をドルに換算します\n"
15  print "何円を換算しますか: "
16  buff=gets()    # キーボードから数字の列を読み込む([Enter]まで)→buffとする
17  yen=buff.to_i  # buff(読み込んだ数字の列)を整数に変換してyenとする.

まず14,15行めはprintメソッドをつかって,入力をうながすメッセージを表示しています. つぎに16行めでgetsメソッドでキーボードからデータを取得しています. ここでデータが入力されるまでプログラムは先に進みません. 14行めのメッセージを表示しているのはプログラムの内容を説明するためです. また15行めのメッセージはここでプログラムがデータ入力を必要としていることを明示するために表示しています.

getsは改行までの1行のデータを読みこみます. ただしこのとき得られるのは,文字列オブジェクトです. つまり16行めでは,この文字列オブジェクトを変数buffに代入していることになります. たとえば次のように入力を行ったものとします.

$ ruby ytod.rb
何円を換算しますか: 1200[Enter]

ここではあたかも数値を入力しているように見えますが, キーボードから入力したのは, あくまでも「1」,「2」,「0」,「0」,そして[Enter] の5文字であって,1200という数値を入力したわけではありません.

実際,buffに対応づけられているオブジェクトを見るために, 次のようにプログラムに1行追加してみて下さい.


13  # 換算する金額(円)を入力する
14  print "円をドルに換算します\n"
15  print "何円を換算しますか: "
16  buff=gets()    # キーボードから数字の列を読み込む([Enter]まで)→buffとする
17  yen=buff.to_i  # buff(読み込んだ数字の列)を整数に変換してyenとする.
    p buff # buffに対応するオブジェクトを表示する.

pメソッドは,オブジェクトの内容をそのまま表示するものです. この行を追加したあと,再度プログラムを実行すると,次のようになるはずです.

$ ruby ytod.rb
何円を換算しますか: 1200[Enter]
"1200\n"
1200円は,10.90909091ドルです

ここで3行めに表示されているのが,「p buff」の結果, すなわち変数buffに対応づけられているオブジェクトの内容です. ここで二重引用符に囲まれて表示されている通り,このオブジェクトは, 文字列オブジェクトで最後に「\n」,すなわち改行文字が入っています.

オブジェクトとメソッド再び

上で説明した通り,入力されたデータは文字列ですから, 計算を行うにはこれを数値に変換する必要があります. プログラムではこの変換を17行めで行っています.


13  # 換算する金額(円)を入力する
14  print "円をドルに換算します\n"
15  print "何円を換算しますか: "
16  buff=gets()    # キーボードから数字の列を読み込む([Enter]まで)→buffとする
17  yen=buff.to_i  # buff(読み込んだ数字の列)を整数に変換してyenとする.

17行めの式は代入式です. 左辺はyenという変数で,右辺の計算結果をyenに代入することを意味します. ここで右辺に注目して下さい. これは,文字列オブジェクトbuffのメソッドto_iを起動していることを意味します. このメソッドを評価した結果が右辺の式の値となります.

注: ここで,「文字列オブジェクトbuff」と書いていますが,正確には,変数buffがオブジェクトなのではなくて,変数buffに対応しているのが文字列オブジェクトです. しかし,いちいち「変数buffに対応している文字列オブジェクト」のように書くとまどろっこしくなりますので,以下では,変数のことをそのままオブジェクトと書くことにします. ただし「変数はオブジェクトを指しているものである」ことは覚えておいて下さい.

なお,メソッドの式としての値をそのメソッドの返り値といいます. たとえば,getsの返り値は,キーボードから読みこんだ文字列オブジェクト,buffのto_iメソッドの返り値は,整数オブジェクトです.

返り値は「戻り値」,「返戻値」などと呼ばれることもあります.

メッセージの表示,オブジェクト,メソッド」で説明した通り, オブジェクトとは,データとそのデータに対する処理の機能を合わせもつ対象であり, またオブジェクトを扱って何らかの処理をする機能がメソッドです. この「オブジェクトの機能」というのが,まさにメソッドのことです. ここで,オブジェクトとメソッドの説明をしなおせば, オブジェクトは,データとメソッドを合わせもつもので, メソッドは,オブジェクトのデータに対して,処理を行う機能であるといえます. オブジェクトのメソッドは,しばしば,上の例の通り,次のように表現されます.

  オブジェクト.メソッド                   # 引数がない場合
  オブジェクト.メソッド(引数1,引数2,...)  # 引数がある場合

すでに説明している通り, 引数をくくるカッコは意味があいまいにならない限り省略できます. ここで,オブジェクトとそのメソッドの使用例を挙げてみます.

"hello".length     # ==> 5
"こんにちは".sub("にち","ばん")   # ==> "こんばんは"

最初の例では,文字列オブジェクト「"hello"」のメソッド lengthを使って,この文字列オブジェクトの長さを調べています. 次の例では,文字列オブジェクト「"こんにちは"」のメソッド subを使って,「にち」を「ばん」に置き換えています. なお先頭のpは,さきほど紹介したpメソッドです (「p オブジェクト」でオブジェクトの内容を表示します).

「オブジェクト.メソッド」とは別の形式で表現されるメソッドもあります.

1 + 5 # ==> 6
a = ["This","is","an","array"]
a[2]    # ==> "an"

「1 + 5」は,整数オブジェクト1のメソッド「+」を, 整数オブジェクト5を引数として起動しているわけです. 2番目の例は,オブジェクトを並べた配列の中からその要素を取りだしている例です. ここでは配列オブジェクトaのメソッド「[]」で,2番目の要素を取りだしています. 3番目だと思うかもしれませんが,先頭を0と数えるため,2番目となります (最初の要素は先頭から0個離れている,次の要素は先頭から1個離れているのように考えると分かりやすいでしょう).

オブジェクト指向の世界では,メソッドを起動することを「オブジェクトにメッセージを送る」という言い方をします. ここでいうメッセージ(message)とは起動するメソッドのことです. またメッセージを送られるオブジェクトのことをメッセージのレシーバ(receiver)といいます.

さて例題のプログラムに戻ると,17行めの代入式の右辺は文字列オブジェクトbuffに関して,その内容を整数オブジェクトに変換して返すメソッド「to_i」を起動しているわけです.


13  # 換算する金額(円)を入力する
14  print "円をドルに換算します\n"
15  print "何円を換算しますか: "
16  buff=gets()    # キーボードから数字の列を読み込む([Enter]まで)→buffとする
17  yen=buff.to_i  # buff(読み込んだ数字の列)を整数に変換してyenとする.

to_iメソッドは,文字列のうち,整数と解釈できる先頭部分を切り出して,それに対応する整数オブジェクトを返します. これにより,変数yenにキーボードから入力したデータが整数として対応づけられます.

関数的メソッド

ここまでで,メソッドがオブジェクトの機能であることを説明しました. ところが,これまでに使ってきたprint,gets,pメソッドにはレシーバがなくて, オブジェクトのメソッドとなっていません(疑問に思ったでしょうか).

これらもメソッドである限りには,何らかのオブジェクトのメソッドなのですが, これらのメソッドでは,そのレシーバが省略されているわけです. Rubyでは,このようなレシーバが省略されるメソッドが少なからずあります. このようなメソッドを関数的メソッドといいます.

演算子式

20行めで換算を行っています.


19  # ¥→$換算
20  dollar = yen / D2Y_RATE

すでに説明した通り,Rubyにはプログラムを見やすくするために「オブジェクト.メソッド」ではない形式で起動されるメソッドがあります. 四則演算を始めとする算術演算や論理演算はその例です. ここでは,除算「/」を行っています. プログラミング言語では,「×」と「÷」は,しばしば「*」と「/」と記述されます.

Rubyには,算術演算子として,四則演算の他に「%」(整数を整数で割った余り), 「**」(累乗)が定義されています. また論理演算子として,「&&」あるいは「and」(論理積), 「||」あるいは「or」(論理和),「!」あるいは「not」(否定)が定義されています. 演算子には優先順位がきめられていて,優先順位の高い方から先に計算が行われます. たとえば,数学と同様に「*」と「/」は「+」と「-」に優先します.

  1+2*3+4 # == 1+(2*3)+4 ==> 11

数学と同様に演算の順序を明示するには( )でくくります.

整数同士の除算

Rubyでは整数同士の除算は商(整数)を返しますので注意が必要です. なお負の整数と正の整数の除算の場合は余りが正の整数となるように商が定められます.

   3/2  # ==> 1  (商1を返す.1.5を返さない)
  -3/2  # ==> -2 (-3 = -2×2 + 1)

整数データ同士の除算で,整数ではなく, 小数点以下も含めた計算結果を得たい場合には, そのままでは無理なので,整数オブジェクトのto_fメソッドで, 整数を実数(正確には浮動小数点数)に変換する必要があります(どちらか一方を変換するだけで構いません).

浮動小数点数

我々がよく使う数として,整数と実数があります. 数学では,実数をあつかうときに整数はその一部に含まれるものとして区別しませんが, コンピュータの内部では,実数は,整数とはまったく別に扱われます. 正確にいうとコンピュータでは実数を扱うことはできません. コンピュータが扱うのは,実数ではなく,有限桁数の浮動小数点数(floating point number)です. 大雑把にいうと「浮動小数点数」とは,実数をある桁数までで打ち切った数で,指数形式で表現されます(正負の符号,仮数部,指数部で構成されます). つまり,コンピュータでは,実数が正しく表現されるとは限りません. 実数(浮動小数点)計算,とくに科学技術計算ではいつも誤差を考慮する必要があります.

結果の出力

例題のプログラムでは,最後に換算結果を表示しています.


22  # 結果を表示する(最後の「\n」は改行することを意味する)
23  print "#{yen}円は#{dollar}ドルです\n"
24  print "(ただし1ドル=#{D2Y_RATE}円としています)\n"

表示には,前回学んだprintを使います. すでに説明した通り,printは引数をいくつでもとることができます. 文字列以外のオブジェクトを引数とすることもできます.

なおここで示したように"…"の中に「#{式}」という表現をいれると, 式の値を計算してから表示を行います.

名前のつけ方

変数,定数などにつける名前にはルールがあります.

Rubyには予約語があります. それらは名前として使うことはできません. その中には,「begin」,「end」,「next」など一般的な名前も含まれます. これらの名前をプログラムで変数として使うとエラーになりますので注意が必要です. なおemacsでRubyプログラムを編集すると予約語には色がつきます.

定数の場合,最初が必ず大文字でなければいけないことに注意して下さい. 逆に,変数に大文字から始まる名前をつけることはできません. 定数の名前には,二文字目以降は,小文字も含めることができますが, プログラムを分かりやすくするために,小文字は含めないことを奨めます.

なお,まだ説明はしていませんが,変数には種類があります. これまでに扱ってきた変数は,正確にはローカル変数といいます. ローカル変数以外の変数の場合には,変数名の前に,その種類を表す特別な記号(列)を付加します.

サンプルプログラム

サンプルプログラムをいくつか紹介します.

[プログラミング演習(Ruby) >  変数とデータ入力]