このレッスンでは、これまでにも何度か取り上げてきました数値(数値クラス)について詳しく学んでいきましょう。今回学ぶのは以下のような部分です。FixnumやInteger等をはじめとしたサブクラスを含めた数値クラスの構成についてのレッスンです。
数値を表すクラスは複数の種類に分かれています。そして、プログラムに直接書き込める数値をリテラルといいます。
プログラムの中に数値を直接書き込む際の表記の方法についてです。
*リテラルとはコードに直接書かれている数値や文字列のことです。以下コードであれば「100」がリテラルにあたります。
num = 100
四則演算等の演算についてや、「Math」という数値の計算のためのモジュールについての項目です。
*演算とは計算のことです。
「Float」というクラスから「Integer」クラスへと変換するなど、数値のクラスの変換についての項目です。
ビット演算(計算)を行う際に使う演算子や使い方についての項目です。
ランダムな値を取得するための機能についての項目です。
「Integer」を使用して繰り返し回数の指定を行う方法についての項目です。
では、Numericのクラス構成から学んでいきましょう。
数値クラスの構成は以下表のようになっています。スーパークラスが「Numeric」となっており、「Integer」や「Float」はサブクラスとなります。「Integer」には2つのサブクラスがあり、それが「Fixnum」と「Bibnum」です。
では、各クラスの詳細を見ていきましょう。
「Intger」とは整数を表すクラスです。以下の様なものは全て「Integer」に属する事になります。
1, 10, 0 , -1, -10
そして「Integer」をスーパークラスとする「Fixnum」と「Bignum」です。この2つはどちらも整数なのですが大きさによってどちらに振り分けられるか決まります。ここでいう大きさとはビット数の大きさです。
情報の単位の中で1番小さいものがビットです。(情報の単位は他にもバイトなどがあります)ビットとは「0か1」もしくは「ONかOFF」のどちらかを表現します。このどちらかが組み合わさって数値などのデータを扱っています。
このビットが約31以下であれば「Fixnum」、31以上であれば「Bignum」という事になります。プログラムを組む上で使用するような整数は大体が「Fixnum」となるでしょう。万が一約31ビット以上となっても自動で「Bignum」に変換してくれるようになっているので心配はありません。では、「Fixnum」を使った計算を行ってみましょう。
num = 13 -3
#=> 10
num.class
#=> Fixnum
クラスを調べる「class」メソッドを使ってみると「Fixnum」になっていますね。
「Rational」クラスは有理数を表すクラスです。有理数とは分数に出来る数のことです。ですので、「Rational」オブジェクトは分子と分母という形式のオブジェクトとなります。
では、このような分数(Rationalオブジェクト)を使った計算を行ってみましょう。
bunsu_a = Rational(2, 3)
#=> (2/3)
bunsu_b = Rational(5, 6)
#=> (5/6)
bunsu = bunsu_a + bunsu_b
#=> (3/2)
「Rational」オブジェクトを作成し、分数A+分数Bという計算を行っています。この「Rational」オブジェクトから、分子だけもしくは分母だけを取り出すことや、「Rational」オブジェクトを「Float(浮動小数点数)」オブジェクトに変換する事も出来ます。では、試してみましょう。
p bunsu.numerator
#=> 3
p bunsu.denominator
#=> 2
p bunsu.to_f
#=> 1.5
分子を取り出すのは「numerator」メソッド、分母は「denominator」メソッド、「Folat」への変換は「to_f」というメソッドを使います。
複素数を表すクラスです。複素数とは実数と虚数の組み合わさった数です。「Complex」オブジェクトは実数部、虚数部の形式となります。では、「Complex」オブジェクトを使って計算をしてみましょう。
fukusosu = Complex(1, 3) ** 2
#=> (-8+6i)
複素数の2乗をしていますね。では、実数部と虚数部をそれぞれ取り出してみましょう。
p fukusosu.real
#=> -8
p fukusosu.imaginary
#=> 6
このように「real」メソッドと「imaginary」メソッドを使います。
冒頭で取り上げましたが、リテラルとはプログラムに直接記述されている数値のことです。数値のリテラルには様々な種類が存在します。以下は、数値のリテラルの種類を例を使い表にしたものです。
・12
・0d12 |
10進数(整数) |
0b110 | 2進数 |
・012
・0о12 |
8進数 |
0x12 | 16進数 |
12.3 | 浮動小数点数 |
2r | 有理数(左記例は2/1を表しています) |
12i | 虚数 |
数値リテラルの中には「_」を入れることが出来ます。「_」を入れることにより長い数値などを桁数などで区切って分かりやすく書くことが出来ます。この「_」を書いても特別な意味はなく無視されることになりますので、わかりやすくという目的の為だけに使われます。
p 3_333_000
#=> 3333000
*数値の先頭と末尾に「_」を書くことは出来ません。
数字の表現方法のことです。この進数はcssのカラー指定の際などにも使われています。黄色を16進数で表現すると「#fff000」ですが10進数だと「255, 240, 0」です。同じ値なのですが違うものに見えるのは、表現方法が異なるためです。では、進数の種類を見ていきましょう。
ー 10進数
0~9の数字で表現される数値(整数)と「0d」から始まる数値
ー2進数
「0b」から始まり、かつ0か1の数字で表現される数値
ー8進数
「0」か「0о」から始まり、かつ0~7の数字で表現される数値
ー16進数
「0x」から始まり、かつ0~9、A~Fで表現される数値
数値の計算は「+」や「-」などの演算子を使い行われます。同じクラス同士のインスタンスでないと計算を行えないということはなく、「Fixnum」オブジェクト+「Float」オブジェクトというようなことも可能です。
ただし、「Fixnum」オブジェクト+「Float」オブジェクトとすると答えの値は「Float」オブジェクトとなります。また、「Fixnum」と「Rational」だと答えの値は「Rational」であり、「Fixnum」と「Complex」だと答えの値は「Cmplex」となります。
a = 1 + 1
#=> 2
a.class
#=> Fixnum
b = 5 - 3.0
#=> 2.0
b.class
#=> Float
c = 6 / 2/10r
#=> (3/10)
c.class
#=> Rational
d = (2 + 1i) / 2
#=> ((1/1)+(1/2)*i)
d.class
#=> Complex
上記は各計算例とクラスの確認です。c、dについては数値リテラルを使い計算しています。
割り算を行う為のメソッドは複数存在しています。ではそれぞれ使い方なども含めみていきましょう。
割り算の商を整数で返します。
x.div(y)
10.div(3)
#=> 3
10.div(3.3)
#=> 3
-10.div(-3.3)
#=> 3
-10.div(3.3)
#=> -4
割り算の商を返します。整数÷整数であれば答えの値は「Rational」オブジェクトとなり、整数÷小数であれば「Float」オブジェクトになります。
x.quo(y)
a = 7.quo(3)
#=> (7/3)
a.class
#=> Rational
b = 7.quo(3.3)
#=> 2.121212121212121
b.class
#=> Float
割り算の余りを返します。この「modulo」メソッドと同じ機能を持つ「%」というものが存在します。
x.modulo(y)
x % y
5.modulo(2)
#=> 1
5 % 2
#=> 1
割り算の「商」と「余り」の両方を配列として返します。
x.divmod(y)
5.divmod(2)
#=> [2, 1]
10.divmod(1.5)
#=> [6, 1.0]
10.divmod(-1.5)
#=> [-7, -0.5]
割り算の余りを返します。余りを返すというのは「modulo」と「%」と同じですね。しかしこの「remainder」は返される値(余り)の符号が以下例で言うと「x」の符号と同じになります。
x.remainder(y)
10 % -3
#=> -2
10.remainder(-3)
#=> 1
整数÷整数という割り算で0が含まれている場合にはエラーが返ってきます。しかし、整数と小数の割り算に0が含まれている場合は「Infinity」か「NaN」のどちらかが返ってきます。左辺と右辺がどちらも0(0.0)という割り算であれば「NaN」、1or 0.1以上と0(0.0)という組み合わせの割り算であれば「Infinity」となります。
1 / 0
#ZeroDivisionError: divided by 0
from (irb):6:in `/'
from (irb):6
from /usr/local/rvm/rubies/ruby-2.3.4/bin/irb:11:in `<main>'
0 / 0
#ZeroDivisionError: divided by 0
from (irb):7:in `/'
from (irb):7
from /usr/local/rvm/rubies/ruby-2.3.4/bin/irb:11:in `<main>'
1 / 0.0
#=> Infinity
0 / 0.0
#=> NaN
よく使われる演算の為のメソッドが、この「Math」モジュールにまとめられています。このモジュールによりモジュール関数(メソッド)と定数が使用できます。では、モジュール関数(メソッド)の中から「sqrt(x)」という平方根を求めるメソッドを使ってみましょう。
p Math.sqrt(3)
#=> 1.7320508075688772
include Math
p sqrt(3)
#=> 1.7320508075688772
上記のとおり2種類の呼び出し方が存在します。1つ目は特異メソッドとして呼び出す方法、2つ目はMathモジュールをインクルードしてから呼び出す方法です。また、「Math」モジュールには定数もあります。その中から「PI」という円周率を求める定数を使ってみましょう。
p Math::PI
#=>3.141592653589793
「Integer」オブジェクトを「Float」オブジェクトに変換したり(その逆も)、文字列オブジェクトを数値に変換したりすることが出来ます。では、変換の仕方をみていきましょう。
Floatオブジェクトに変換するには「to_f」メソッドを使用します。
1.to_f #Integer → Float
#=> 1.0
"1".to_f #String → Float
#=> 1.0
Integerオブジェクトに変換するには「to_i」メソッドを使用します。
1.5.to_i #Float → Integer
#=> 1
"1.5".to_i #String → Integer
#=> 1
小数を整数に変換すると小数点以下は切り捨てとなります。
小数点以下を四捨五入したい場合は「round」メソッドを使用します。この「round」メソッドの引数に値を指定すれば小数点以下のどこから四捨五入するかを指定できます。また、引数に負の値を指定すれば整数の部分(小数点より上の桁の部分)でどこから四捨五入するかを指定できます。では例を見てみましょう。
1.1.round
#=> 1
1.9.round
#=> 2
0.123.round(2)
#=> 0.12
1230.round(-3)
#=> 1000
1920.round(-3)
#=> 2000
3つ目は引数で2と指定しているので3は切り捨てとなります。3つ目と4つ目は引数で-3と負の値を指定していますので整数部分の指定場所から四捨五入していますね。
四捨五入ではなくレシーバよりも大きくかつ1番レシーバに近い整数を返してくれるのが「ceil」というメソッドです。
1.1.ceil
#=> 2
-1.1.ceil
#=> -1
1つ目でいうと、四捨五入であれば1となるところが、レシーバよりも大きくという事なので2になっていますね。
この場合は「floor」というメソッドを使います。「ceil」メソッドの逆という事ですね。
1.1.floor
#=> 1
-1.1.floor
#=> -2
この他にも、「Rational」オブジェクトや「Complex」オブジェクト、「String」オブジェクトにも変換する事が出来ます。
1.1.to_r
#=> (2476979795053773/2251799813685248)
1.1.to_c
#=> (1.1+0i)
1.1.to_s
#=> "1.1"
それぞれ「to_」に続くアルファベットが各クラスの頭文字になっていることが分かると思います。
Integerクラスではビット演算子というものを使いビット演算を行う事が出来ます。ビットとは「0か1」もしくは「ONかOFF」のどちらかを表現するものであり、この(0か1など)どちらかが組み合わさって数値などのデータを扱っている。と冒頭で取り上げたと思います。そして本セクションのビット演算とは整数を2進数で表記した場合の各桁(2進数は0か1で表現されるものです)をビットとして考えて行う演算のことです。
・ビット演算子
~ | ビット反転 |
& | ビット積 |
| | ビット和 |
^ | 排他的論理和 |
>> | 右ビットシフト |
<< | 左ビットシフト |
では、左辺と右辺が1の時にのみ1を返す「&」を使ってみましょう。
0 & 0
#=> 0
0 & 1
#=> 0
1 & 0
#=> 0
1 & 1
#=> 1
乱数とは、ランダムな数値のことです。この乱数は何かの暗号や鍵など予測されては困るようなもので使われたりします。乱数を必要とするときは「Random.rand」というメソッドを使います。
Random.rand
#=> 0.20456564340215821
Random.rand
#=> 0.3181247595805714
Random.rand(5)
#=> 3
Random.rand(50)
#=> 24
Random.randと、引数を指定せず呼び出した場合は1未満である数値、つまり小数をランダムで返します。引数を指定した場合は、0~指定した数の間の数値をランダムで返します。
ここまで乱数について学んできましたが実はこの乱数は完璧な乱数ではありません。どういう事かというと、プログラム上では乱数に見えるものを計算で作り出しているのです。このような乱数を「疑似乱数」といいます。
この疑似乱数を作る時、元となる数値が存在しています。これを「乱数の種」と呼びます。「Random.rand」と呼び出したとき、1つの乱数の種だけだと、同じ値が返されてしまうことがあります。完璧な乱数ではないというのはこのような理由からです。以下例で言うと、乱数の種Aは乱数1、乱数2、乱数3と順番に乱数を生成します。
逆に言うと、乱数の種を違うものにして乱数を作っていけば完全なランダムに近づけていくことが出来ます。では、乱数の種の指定の仕方などについてみていきましょう。乱数の種を指定するには、まず乱数生成器というものを初期化する事が必要です。これは「Random.new(乱数の種)」というように行います。では試してみましょう。
a = Random.new(1)
a.rand(10)
#=> 5
a.rand(10)
#=> 8
a.rand(10)
#=> 9
b = Random.new(1)
b.rand(10)
#=> 5
b.rand(10)
#=> 8
b.rand(10)
#=> 9
Random.new(1)と実行しています。これは乱数生成器を初期化し乱数の種を「1」と指定しています。変数「a」も「b」も乱数の種が1の乱数を得ています。同じ数字が同じ順番で返ってきているのは、乱数の種が同じかつ最初に初期化をしているからという事ですね。
また、引数(乱数の種)を指定しなくてもOKです。その場合は「Random.new」とするたびに初期化されるとともに、新しい乱数の種が指定されるので上記例とは異なった結果になります。
a = Random.new
a.rand(10)
#=> 3
a.rand(10)
#=> 0
a.rand(10)
#=> 4
b = Random.new
b.rand(10)
#=> 0
b.rand(10)
#=> 7
b.rand(10)
#=> 6
きちんと異なる乱数の種から生成されていることがわかりますね。
この乱数というものは最初に申し上げたように、暗号や鍵などのセキュリティの部分で使用されることも多いでしょう。このような場合での乱数生成のためのライブラリ「securerandom」というものがあります。
以下2つのメソッドを取り上げてみます。これらのメソッドは引数としてバイト数(8ビット=1バイト)を指定すると、指定バイト数分の長さの文字列を返します。
表示できないような無効な値が含まれた文字列が返ってきます。
このメソッドは英数記号の組み合わされた文字列が返ってきます。
require "securerandom"
SecureRandom.random_bytes(3)
#=> "+\xFE\xE5"
SecureRandom.base64(3)
#=> "t0wG"
では最後に配列の中の要素をランダムに並べ替えるという使い方をしてみましょう。
product = ["コート", "ショルダーバッグ", "パンプス", "ネックレス", "リング"]
print product.sort_by{rand}
#=>["リング", "コート", "パンプス", "ネックレス", "ショルダーバッグ"]
Integerクラスは、処理を実行した回数や、配列内の要素の数を数える際にも使われます。では、処理の実行回数を指定するようなイテレータ(繰り返しのメソッド)を見てみましょう。
指定した回数だけ処理の実行を繰り返します。
2.times do |i|
i = i + 1
puts "#{i}回目の処理実行です"
end
#1回目の処理実行です
#2回目の処理実行です
この指定回数、ブロック変数に代入されている値が数値ですね。
timeメソッドど異なり指定した数値からカウントされていきます。〇(from)回~〇(to)回というように最初と最後の値を指定できます。指定した範囲分だけ処理を繰り返します(最初と最後の値も含めます)。
3.upto(5) do |i|
p i
end
#3
#4
#5
〇回(from)~〇回(to)というように指定した範囲の中で減算してカウントしていきます。uptoとは逆という事ですね。もし「form」の値が「to」よりも小さければ1度も処理を実行せず終わります。
5.downto(3) do |i|
p i
end
#5
#4
#3
「from.step(to, step)」という形式で書き始められます。これは、「from」から始まり「from」に「step」の値を足していきながら繰り返し処理を「to」の値まで実行します。
2.step(5, 1) do |i|
p i
end
#2
#3
#4
#5
timesメソッドで指定できる回数は整数のみです。しかしこのメソッドでは、小数も指定できます。
2.5.step(2.8, 0.1) do |i|
p i
end
#2.5
#2.6
#2.7
#2.8
このようにたくさんのメソッドで数値オブジェクトが使われています。
プログラムの中で浮動小数点数を扱っていると誤差が出てしまい問題となる時があります。では、例を見てみましょう。
a = 0.1 + 0.7
p a
#=> 0.7999999999999999
この計算の答えは0.8になるはずです。しかし、返ってきた値は「0.7999…」です。これは、浮動小数点数が2進数に変換されているのですが、0と1だけでは表現しきれず「01011101100…」と続いてしまい一定の桁で切り捨てられている為です。これを丸め誤差といいます。
*2進数とは0か1で表記する進数です。
このように誤差が出てしまっては困るという場合は「Rational」クラスを使い小数を分数に置き換えて計算すると誤差がなくなります。
*分数に置き換えられない値であればできません。
b = 1 / 10r + 7 /10r
p b
#=> (4/5)
数値を表すクラスは複数の種類に分かれています。そして、プログラムに直接書き込める数値をリテラルといいます。
数値のクラスを他のクラスに変換することができ、ランダムな数値(乱数)を取得するには「Random.rand」というメソッドを使います。
無料ビデオ講座のお知らせ
Skillhub [スキルハブ]では無料の動画講座を多数公開しています。他校だと数万円するような講座が無料で受講できます。