roy > naoya > 基礎プログラミングI > (2)値の型変換・演算子

(2) 値の型変換・演算子

[1] 演算子・値の型についての導入

前回getsメソッドやprintメソッド、printfメソッドを組み合わせたプログラムを作成した。これらを組み合わせるだけでも、ユーザからの入力に応じて計算を行い結果を表示するプログラムを作成することができたが、これだけでは表現しきれないことがいくつかあった。今回は、以下の2つについて話を進める。

  1. 演算子:÷や×はどう書けばよいか。その他にも特殊な演算子があるのか(例:時給と労働時間からアルバイト代を計算する場合、アルバイト代=時給×時間、となるが×がキーボードにない。)。
  2. 値の型:前回作成したプログラムでは、実は小数を含む実数を取り扱うことができなかった(気づいていただろうか)。対応方法はないのか。

[2] Rubyにおける演算子の記法

算術演算子

+や-、×、÷などの記号を演算子とよぶ。いくつかの演算子はキーボードに存在しないため、日常的に使用する演算子とは異なる演算子を使用する。ここでは数値計算を行う際に利用する演算子である算術演算子を示す。

算術演算子 意味
+ 加算2+5(2に5を加える)
- 減算6-3(6から3を減じる)
* 乗算9*2(9に2をかける)
/ 除算12/4(12を4で割る)
% 剰余9%2(9を2で割った余り)
** べき乗2**8(2を8乗する)
  1. 乗算(掛け算)は×ではなく*を使用する(例:2×5→2*5)。
  2. 除算(割り算)は÷ではなく/を使用する(例:20÷4→20/4)。
  3. べき乗はabではなく**を使用する(例:25→2**5)。
  4. 剰余(割り算の余り)は%で表現できる(例:10を3で割った余り→10%3)。
  5. {中括弧}や[大括弧]は使用せず全て(小括弧)で記述する(例:{4*(8+1)}→(4*(8+1)))。
  6. 分数では分母と分子を上下に記述するのではなく1行で記述する。

これらを組み合わせた例を見てみよう。

  • 6×4÷2 → 6*4/2
  • {5÷(4+1)2}2 → (5/(4+1)**2)**2
  •  5  → 5/2
     2  

演算子の優先順位

算数・数学と同様、演算子には優先順位がある。

  • 高:**
  • 中:* / %
  • 低:+ -

したがって、1+2*3は2*3が先に行われ7に、3*2**3は2**3が先に行われ24になる。ただし(括弧)があれば、先に括弧内が計算される。(1+2)*3は9に(3*2)**3は216となる。

算術演算子と演算子の記法のまとめ

  • 数値計算を行う際に用いる演算子を算術演算子という。
  • ×は*、÷は/と書く(例:2*3、6/7)
  • べき乗は**で表現する(例:2**10)
  • 割り算の余り(剰余)は%と書く(例:10%3)
  • 括弧({[は全て(で書く
  • 演算子には優先順位がある。
  • ()をつけた場合は()内の計算が優先される。

[3] 出席課題

以下の式をRubyの記法にしたがって記述しよう。

  • a ÷ b + c × d
  • hogeをpiyoで割った余り+foo
  • {1-(total-0.1)2}2

制限時間は10分。できる範囲で実施し、制限時間内にメールで解答を送信すること。出席点は2点。

提出要領

  • 提出先:課題提出用メールアドレス
  • メールのSubject:ruby02
  • 本文の構成:1行目で学籍番号、氏名を記載する。2行目以降に解答を書く。

[4] 値には型がある

前回作成をしたプログラムでは、小数を扱うことができなかった。この点について、以下の単純なプログラムで確認してみよう。

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

a = 5
b = 2
c = a / b
p c

pメソッドは変数内の値を表示するメソッドである。このプログラムを実行すると、2.5という結果が表示されるような気がする。しかし、実行結果はそうはならない。

sime{c11xxxx}% chmod +x sample.rb[Return]
sime{c11xxxx}% ./sample.rb[Return]
2

結果は2となっている。なぜだろうか?

我々は「123」は整数、「10.5」は実数(整数+小数)、「プログラミング」は文字列であると直感的に理解できる。しかしコンピュータは理解することができない。このため取り扱っている値が整数なのか実数なのか文字列なのかを明確に指示する必要がある。

値の型について

  • コンピュータは値の型を理解することができない。「10」は整数、「1.5」は実数、「Ruby」は文字列であることを示してあげる必要がある。

[5] 初期値を与える場合の値の型の指定

a=10とプログラム中に記載した場合、aに10を代入するという意味になる。ではb=10.0と入力した場合はどうなるだろうか。c=3とし、d=a/ce=b/cとした場合に何が異なるだろうか。予想をしてから以下のプログラム(value.rb)を実行してみよう。

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

a = 10
b = 10.0
c = 3
d = a / c
e = b / c
p d
p e

これを実行すると以下のようになる。

sime{c11xxxx}% chmod +x value.rb[Return]
sime{c11xxxx}% ./value.rb[Return]
3        #->p dの出力結果
3.333333333 #->p eの出力結果

a = 10とした場合、aに代入されている値は整数であると判断される。このため3で割っても小数点以下が切り捨てとなり、答えは3になっている。一方、b = 10.0とするとbに代入されている値は小数点以下を含む実数であると判断される。これにより3で割った答えにも小数点以下の値が含まれる。

初期値を与える際の型指定の方法

  • a = 10の場合整数として扱われる。整数同士の割り算の結果小数点以下がでても表示されない。
  • a = 10.0の場合実数として扱われる。割り算の結果小数点以下がでれば表示される。
  • 計算に用いる値のいずれか1つが実数であれば、計算結果は実数になる。

[6] getsメソッドについて

キーボードからの入力時の[Return]キーの扱い

キーボードからの入力を読み込むメソッドとしてgetsメソッドを使用してきた。前回はgets.chomp!.to_iというように後ろにchomp!.to_iをつけて利用してきたが、実はchomp!もto_iも特定の働きを持つメソッドである。

getsではなくgets.chomp!.to_iとしてきたのは、今回の授業で取り上げた値の型が関連する。getsメソッド、chomp!メソッド、to_iメソッドの働きについて確認するために、まず以下のプログラムを実行してみよう(gets.rb)。

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

print"数字その1を入力して下さい\n"
number1 = gets
print"数字その2を入力して下さい\n"
number2 = gets.chomp!
print"数字その3を入力して下さい\n"
number3 = gets.chomp!.to_i

p number1, number2, number3

これを実行すると以下のようになる。

sime{c11xxxx}% chmod +x gets.rb[Return]
sime{c11xxxx}% ./gets.rb[Return]
数字その1を入力して下さい
100[Return]
数字その2を入力して下さい
100[Return]
数字その3を入力して下さい
100[Return]
"100\n" #->number1
"100"   #->number2
100     #->number3

p number1の結果を確認すると、"100\n"というように、「\n」がついている。chomp!メソッドがついている。

キーボードから値を入力する場合、入力後に[Return]キーを入力するが、これがプログラム内では\nとして扱われる。一方、chomp!メソッドをつけたnumber2number3では\nは取れ、"100"や100になっている。""がついているものは文字列、ついていなければ数字であった。これらを踏まえchomp!メソッドやto_iメソッドの働きについて考えてみよう。

getsメソッド、chomp!メソッド、to_iメソッド

これら3つのメソッドの働きは、次の通りである。予想したとおりだっただろうか。

  • getsメソッド:キーボードからの入力を文字列として取得する
  • chomp!メソッド:改行文字(\n)があれば取り除く
  • to_iメソッド:値の型を整数に変換する

getsメソッドはキーボードからの入力を文字列として取得する。このため、ユーザーが数字の100(ひゃく)のつもりで入力しても文字列の100(いちぜろぜろ)として扱われてしまう。

さらに言えば、キーボードから入力する場合、入力後に確定のために[Return]キーを押す。[Return]キーは改行をあらわす\nとしてコンピュータ内では取り扱われる。このため、数字の100(ひゃく)を入力したつもりでも、コンピュータが受け取るデータは100\n(いちぜろぜろバックスラッシュエヌ)となっている。

そこで、getsメソッドにchomp!メソッドをつけることで、まずバックスラッシュエヌを取り除いている。その後、to_iメソッドをつけることで、文字列を整数に変換している。

getsメソッドについて

getsメソッドはキーボードからの入力を文字列として取得する。入力した値を整数として扱う場合はchomp!.to_iをつける。キーボードから100[Return]を入力した場合に変数に代入される値を確認しよう。

  • a = getsの場合:aには100\n(いちぜろぜろバックスラッシュエヌ)が代入される
  • a = gets.chomp!の場合:aには100(いちぜろぜろ)が代入される
  • a = gets.chomp!.to_iの場合:aには100(ひゃく)が代入される

[7] 値の型変換(1):変換メソッド(to_i、to_f、to_s)

3つの値の型変換メソッドについて

to_iは値の型を整数に変換するメソッドであったが、実数に変換するメソッドや文字列に変換するメソッドもある。

値の型変換メソッド

  • to_i:値の型を整数に変換する。小数点以下を含む値の場合は小数部は切り捨てになる
  • to_f:値の型を実数に変換する。小数点以下を含む数字を取り扱うことができるようになる
  • to_s:値の型を文字列に変換する。30は「さんじゅう」ではなく「さんぜろ」になるので数字の足し算や引き算はできなくなる

a = 10.0のようにプログラム内に具体的に値を記述できる場合は、プログラム作成時に配慮すれば、以後値の型を気にしなくても良いが、キーボードからの入力を受け取る場合はこれらのメソッドを使って値の型を指定しなければ全て文字列扱いとなってしまう。

これらの働きをプログラムの実行を通して確認してみよう(form.rb)。

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

print"1つ目の数字を入力してください:\n"
a = gets.chomp!    #=>to_iがついていないことに注意
print"2つ目の数字の入力してください:\n"
b = gets.chomp!    #=>to_iがついていないことに注意

c = a + b
p c
d = a.to_i + b.to_i
p d
e = a.to_f + b.to_f
p e
f = a.to_s + b.to_s
p f
g = a.to_i + b.to_f   #->一方だけto_f
p g

これを実行すると以下のようになる。

sime{c11xxxx}% chmod +x form.rb[Return]
sime{c11xxxx}% ./form.rb[Return]
1つ目の数字を入力してください:
10.6[Return]
2つ目の数字の入力してください:
2.5[Return]
"10.62.5"
12
13.1
"10.62.5"
12.5

変換メソッドをつけずにa+bとした場合、getsは文字列として値を取り込むため、「10.6(いちぜろてんろく)」と「2.5(にてんご)」の2つの値が入力されたことになり、これらの足し算をすると文字列が結合される。to_iやto_fをつけると数字として扱われることがわかる。また、一方にto_fをつけた場合は実数扱いとなっている。

では、to_fとto_sの組合せやto_iとto_sの組合せにした場合はどうなるだろうか。結果を予想してから試してみよう。

これらの型変換メソッドは、gets.chomp!.to_iのようにgets.chomp!に続けて使う以外に、このプログラムのように変数.to_iのように使用することもできる。

これらのメソッドをどこで使うか

to_iやto_f、to_sの各メソッドはgets.chomp!.to_iのようにgetsメソッドと組み合わせて使用するだけではない。次のプログラム(calculate.rb)で用途を確認しよう。

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

print"数字1を入力:\n"
number1 = gets.chomp!
print"数字2を入力:\n"
number2 = gets.chomp!.to_i

sum = number.to_i + number2 
quotient = number.to_f / number2 

printf("%d+%d=%dです。\n", number1, number2, sum)
printf("%d/%d=%dです。\n", number1, number2, quotient)
p quotient

このプログラムでは、number1には文字列が代入されている。しかし、計算を行う際にnumber1.to_inumber1.to_fのように型変換メソッドをつけることで、整数や実数に変換をしてから和や商を求めている。

ただし、このプログラムを実行してみると、割り算の結果はうまく表示されない。

sime{c11xxxx}% chmod +x calculate.rb[Return]
sime{c11xxxx}% ./calculate.rb[Return]
数字1を入力:
10[Return]
数字2を入力:
3[Return]
10+3=13です。
10/3=3です。
3.333333333 

割り算については、number1.to_f/number2をしており、p quotientの結果を見ても3.333333333となっているが、printfの結果を見ると商は3になっている。この原因を次に考えてみよう。

値の型変換メソッドをつける位置について

  • 変数.to_iのように変数に型変換メソッドをつけることができる。

[8] 値の型変換(2):printfを用いたフォーマット出力

printfは変数内に代入されている値を出力する場合に""内に%dをいれ""の後ろに具体的に変数を指定することで、表示をおこなうメソッドであった。

printfの書式制御文字について

ここで%dとは果たして何を意味しているのであろうか。実は%dは%dに分割することができる。厳密に言うと、%の位置に、""の後ろで指定された変数内の値を入れ込んで表示する。dは表示するときの形式(書式制御文字と呼ぶ)である。dはdecimal(10進数)の頭文字で、%dとした場合は変数内の値を「10進整数」で表示せよという意味になる。

先ほどのプログラムでは、quotientに小数を含む値が代入されていたとしても、printfで表示をする際、%dとしていたために小数点以下を切り捨て、整数で表示していたのである。小数を表示したい場合は%dではなく%fを用いる必要がある。

また、我々が日常的に使用している数字は10進数と呼ばれるもので、0~9の10種類の値を使用する。一方で、コンピュータの世界では0と1のみ使用する2進数、0、1、~、9、a、b、c、d、e、fの16種類の数字を使用する16進数が利用されている。結果を2進数や16進数で表示させることも出来る。この場合も、それぞれ専用の書式制御文字を使用する。主な書式制御文字は以下の通り。

printfの書式制御文字について

  • d:10進整数の文字列に置き換える
  • b:2進整数の文字列に置き換える
  • x:16進整数の文字列に置き換える
  • f:10進小数の文字列に置き換える
  • s:文字列に置き換える

format.rb

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

a = 100

printf("10進整数だと%d\n", a)
printf("2進整数だと%b\n", a)
printf("16進整数だと%x\n", a)
printf("10進小数だと%f\n", a)
printf("文字列だと%s\n", a)
sime{c11xxxx}% chmod +x format.rb[Return]
sime{c11xxxx}% ./format.rb[Return]
10進整数だと100
2進整数だと1100100
16進整数だと64
浮動小数点数だと100.000000
文字列だと100

%fを指定した場合、デフォルトで小数点以下第6位まで出力される。

printfによる桁揃え出力

  • %と書式制御文字の間に出力幅を指定する整数を書くことができる
  • %4dのように「4」と入れると、結果を4桁で表示する
    • 25(2桁)の場合には左側にスペースが2つ空き__25となる
    • 137(3桁)の場合には左側にそれぞれスペースが1つ空き_137となる
  • 整数の左にマイナスをつけて%-4dなどとすると左づめになる
  • 日本語文字列と半角英数が混在するとずれる
    • 「abc」は3文字、「くもり」も3文字であるが、日本語の幅は半角英数字の2倍なのでずれる。例えば%6sで指定すると開始位置は同じだが、後ろがそろわない。
    • ___abc
    • ___くもり
  • fの場合は6.2のように小数で指定する。
    • これは全体の桁数が6桁で、小数点以下が2桁という指定になる。
    • 小数点は1桁と数える(例:3.55は4桁であり%4.2fとなる)
    • %fを桁の指定をせずに使用すると標準の6桁で表示される。
整数を入れると右づめになる
printf ("答えは%6dです\n", a)    #=>___100

整数にマイナスをつけると左づめになる
printf ("答えは%-6dです\n", a)   #=>100___

%fでは全体の桁、小数部の桁の順に指定する
ここでは小数を含めて全体が6桁、小数点以下は2桁になっている
printf ("答えは%6.2fです\n", a)  #=>_12.50

format.rbを実行した際に得られる結果表示を整えることを意識して書き直したプログラム(format2.rb)を示す。

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

a = 100

printf("10進整数だと%10d\n", a)
printf("2進整数だと%11b\n", a)
printf("16進整数だと%10x\n", a)
printf("10進小数だと%10.1f\n", a)
printf("文字列だと%12s\n", a)

これを実行すると、綺麗に整えられた出力が得られる。format.rbの実行結果と比べ、見やすくなっていることがわかる。

sime{c11xxxx}% chmod +x format2.rb[Return]
sime{c11xxxx}% ./format2.rb[Return]
10進整数だと       100
2進整数だと    1100100
16進整数だと        64
10進小数だと     100.0
文字列だと         100

printfによる桁揃え出力

  • %と書式制御文字の間に数字を入れることで桁揃えができる。
  • %5dとすれば右づめで5桁表示される。
  • %-4dとすれば左づめで4桁表示される。
  • %fの場合のみ%5.2fのように実数で指定する。5は全体の桁数、2は小数点以下の桁数をあらわす。

[9] コメントをつける

コメントとは、プログラム中に記述する注釈であり、作者名を残したり、説明を行うために使用する。プログラム中に書かれていてもプログラムとして認識されることはないので、実行する上では特に問題は無い。他人の書いたプログラムはわかりづらいと言われることが多いが、わかりやすさの向上を目指すためにコメントを利用することが望ましい。

コメントをあらわす記号は「#」である。プログラム中に「#」があると、その行の「#」以降の記述はコメントとして扱われる(代入演算や論理演算の項で示したサンプルのプログラムを参照のこと)。行の先頭に「#」があれば、1行全てがコメントになる。

なお、コメントは読み手が意味が分かるような内容にするべきである。悪い例と良い例を示す。

悪い例

total = 0      #totalの初期値を0にする
item = 0     #itemの初期値を0にする
          :
item += 1    #itemを1増加する

良い例

total = 0      #購入金額の合計を表す変数
item = 0     #購入数を表す変数
          :
item += 1    #購入数を1増やす

プログラムにエラーが出てしまい、どこに問題があるかわからない場合に1行まるごと#をつけてコメント扱いにする場合もある。その行を実行しない場合にエラーが出ないならば、コメントをつけた行にエラーがあることになる。

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

print"名前を入力\n"
name = gets.chomp!

#printf("あなたの名前は%dですね\n",name)
先頭に#をつけることでこの行は実行されなくなる
コメントにするとエラーが出ず、#を取るとエラーが出る場合は
その行にエラーがあることになる

[10] レポート課題

課題

2つ以上の数字を入力し、それに基づいて何らかの計算を行って結果を出力するプログラムを作成せよ(report1.rb)(8点満点)。

実行結果の例。身長と体重を入力してもらい、BMI値を計算するプログラム(BMI=体重[kg]/身長[m]の2乗)を実行した場合のイメージを示す。

sime{c11xxxx}% chmod +x report1.rb[Return]
sime{c11xxxx}% ./report1.rb[Return]
身長をm単位で入力:
1.9[Return]
体重をkg単位で入力:
56.7[Return]

身長[m] :   1.95
体重[kg]:  56.70
BMI値   :  14.91
右端をそろえること

2つ以上の数字を入力して計算を行うプログラムは、「走行距離とガソリン量から燃費を計算する」「時速と走行時間から移動距離を計算する」などいろいろある。他の人が考え付かないようなテーマを設定しよう。

作成上の条件

  • 入力する数字について、少なくとも1つは実数(小数を含む値)を用いること
  • 結果は2行以上表示されること。その際に右端の桁ぞろえを行うこと
  • 入力を受け付ける際の値の型や結果を表示する際の値の型に注意すること
  • Webページに掲載されている例にとらわれず、自由にテーマを考えること。他の人と重複しないようなテーマを考えたほうが良い
  • 意味のあるテーマを設定すること。入力した値に0.1を足すだけといった意味のないプログラムは減点対象とする
  • 複雑な計算式を適用したり、様々な算術演算子を用いているほど良い

提出要領

  • 提出先:課題提出用メールアドレス
  • 提出期限:第1提出期限、第2提出期限を設定
  • メールのSubject:report01
  • 本文の構成:1行目で学籍番号、氏名を記載する。2行目以降は以下の構成とする
    1. プログラムの概要(例:身長、体重を入力してBMIを計算するプログラム)
    2. emacsで作成したプログラム
    3. ktermで実行した結果
    4. プログラムの説明
    5. 感想
    6. 参考文献
    7. 添付ファイル(report01.rb)

採点要領

  • 採点基準:期限内提出点(2点)、メールの体裁(1点)、プログラムの間違い(2点)、プログラムの説明(2点)、プログラムの複雑さ(1点)
  • 今回は初めての課題提出であり、長いプログラムではないため、1行ずつどのような処理をしているのかをメソッドの働きについて触れながら説明する。
  • 他人のレポートを丸写しした場合は、写した側、写させた側共に0点とする。
  • わかりにくい説明や、Webページを単にコピー&ペーストしただけの説明は減点する。一度読み直してから提出すること。
  • よくできていたレポートは、他の人の参考になるよう、本人が特定できないような形で掲載する。掲載してほしくない場合はメールでの課題提出時にその旨記載すること。

書きかけのメールの保存

レポート課題は、メール本文に説明を書く必要があるため、ある程度時間がかかる。途中で時間がなくなってしまった場合は、保存をしてあとで続きを書くことができる。

保存はCtrl+x Ctrl+s、保存したメールはdraftという名称のフォルダに保存されるので、次回mew起動時にg draft[Return]でdraftフォルダへ移動し、続きを書きたいメールを選択した上でEをタイプすると、続きから書くことができる。送信は通常と同じ。詳細は、mew利用方法のページを参照のこと

プログラムのメール本文への貼りつけ

2つの方法がある。

  1. emacsを2つ起動して、一方でプログラムを開いておき、他方でmewを起動する。プログラムをマウスで選択してコピーし、mewの本文でマウスの真ん中ボタンクリックで貼りつけ
  2. mewの本文でCtrl+x i(Ctrlとxを同時押しの後、Ctrlを離してi)と入力すると、下部のミニバッファにInsert file:~/と表示される。Insert file:~/program/○○.rbで○○.rbがカーソル位置に挿入される

ktermでの実行結果の貼りつけの際は1番のマウスで選択して真ん中ボタンクリックで貼りつける方法を使用する。