Ruby TIPS
桁区切り数値の記述と出力/文字列を逆順に/文字列の分割/文字列の各文字の利用 ― コーディングミスを防ぐには?(4)
初心者向けにRubyプログラミングの落とし穴を紹介。桁区切り指定で数値リテラルを記述する方法や、桁区切りの数値文字列を出力する方法、文字列を逆順に並べ替える方法、文字列を文字列で分割する方法、文字列を1文字ずつ扱う方法を説明する。
Rubyには他のプログラミング言語にはない便利な機能が数多く備わっている。一方で意外な落とし穴もある。今回は数値リテラルを見やすくする方法や文字列をうまく扱う方法を見ていく。最後にそれらを組み合わせた例として、数値に桁区切りスタイルを適用するためのコードも紹介する。
数値リテラルには桁区切りが指定できる
一般的なプログラムでは、大きな数値リテラルを扱うことは少ないかもしれないが、桁数が増えると位取りが分かりにくくなる。例えば、1000000という値を見ても、百万なのか一千万なのか一瞬で見分けるのは難しいし、0の数を間違って入力してしまうこともある。Rubyでは、_
(アンダーバー)を使って桁区切りを指定できるので、そういう面倒さを避けることができる。
limit = 1_000_000
rest = limit - ARGV[0].to_i
puts rest
|
プログラムの中で桁数の多い数値リテラルを使う例。一目見ただけで百万であることが分かる。
桁区切りの記号として使う_
は単に無視されるだけなので、特に3桁ごとに指定する必要はない。ただし、2つ連続させたり、末尾に付けたりすることはできない。
ところで、3桁ごとに区切るのは「thousand(千)」「million(百万)」「billion(十億)」といった英語の位取りに合わせたものなので、日本語の「万」「億」「兆」という位取りに合わせたい場合には4桁ごとに区切った方が適切といわれている。当然、そういった表し方もできる。例えばリスト1.1の変数limit
に代入する値は、100_0000と書いてもよい。いずれの書き方でもいいが、実行してみると以下のようになる。
$ ruby sample001.rb 123456
876544
$
|
桁区切りの_
は無視されるので、普通の数値として扱われる。当然、加算や減算などの演算子も使えるので、正しく結果が求められる。
桁区切りの_
は10進整数だけでなく、どんな数値リテラルでも使える。例えば以下のように、桁数の多い2進数や16進数を_
で区切ればかなり見やすくなる。
- 10進実数の例: 1_234.56
- 2進数の例: 0xE7_84_A1
- 16進数の例: 0b1110_0111_1000_0100_1010_0001
なお、数値に書式を指定して出力するためにはprintf
メソッドなどを使うが、桁区切りのための書式指定子がないので、3桁ごとにカンマ(,)で区切って出力するといった場合にはちょっとした工夫が必要になる。その方法については、文字列についてのTIPSを紹介した後(=本稿の最後)で、コードの例を示すこととする。
文字列を逆順にするには
文字列を自由に取り扱うには、文字列の性質やString
クラスのメソッドを知っておく必要がある。これまでに公開されたTIPSでも文字列の取り扱い方法をいくつか見てきたが、今回は、
- 文字列を逆順に並べ替えるメソッド
- 文字列を分割するメソッド
- 文字列と配列との関係
を紹介する。まずは、文字列を逆順に並べ替えるreverse
メソッドとreverse!
メソッドの使い方を簡単な例で見てみよう。
str1 = "today"
str2 = str1.reverse # str1を逆順にしたものをstr2に代入
puts "非破壊的メソッドの実行結果"
puts str1, str2
puts "破壊的メソッドの実行結果"
str1.reverse! # str1を破壊的に逆順にする
puts str1
|
String
クラスのreverse
メソッドは非破壊的なメソッドなので文字列そのものの内容は変更されない。一方、String
クラスのreverse!
メソッドは破壊的なメソッドなので文字列そのものが変更される。
非破壊的なメソッドでは元の文字列は変更されず、変更された新しい文字列が作られる。一方、破壊的なメソッドでは元の文字列そのものが変更される。それを踏まえた上で、結果がどうなるか予想してみてほしい。予想ができたら、実行例で結果を確認してみよう。
$ ruby sample002.rb
非破壊的メソッドの実行結果
today
yadot
破壊的メソッドの実行結果
yadot
$
|
前半では変数str1
が参照する文字列"today"は変更されず、逆順にされた"yadot"が新しく作られ、その参照が変数str2
に代入された。後半では変数str1
が参照する文字列そのものが"yadot"に変更された。
文字列を分割するには
"abc.txt"を"."の前後で"abc"と"txt"に分割する、というように、文字列を特定の文字列で分割したいときにはString
クラスのpartition
メソッドが使える。簡単な例で見てみよう。
filename = "abc.txt"
fname, sp, ext = filename.partition(".")
puts fname, sp, ext
|
String
クラスのpartition
メソッドは、引数に指定した文字列を使って、元の文字列を3分割し、「引数より前の部分」「引数の文字列」「引数より後ろの部分の文字列」をそれぞれ返す。
partition
メソッドには、セパレーターとして使う文字列を引数に指定する。このメソッドが返す値は、下記の3つがあることに注意しよう。
- 分割した文字列の前の部分
- 引数として指定した文字列(セパレーター)
- 分割した文字列の後ろの部分
元の文字列にセパレーターが含まれない場合は、2つ目と3つ目の返り値は空文字列となる。Rubyでは複数の変数に複数の値を代入できることを思い出そう。実行例は以下の通り。
$ ruby sample003.rb
abc
.
txt
|
セパレーターに"."を指定すれば、ファイル名を名前の部分と拡張子とに分割できる。
ただし、「abc.def.txt」のように文字列に複数の「.」が含まれる場合、ファイル名の拡張子を得るという目的に、この方法は使えない。その場合は「abc」「.」「def.txt」の3つの部分に分割される。なお、File
クラスのextname
メソッドを使えば、「.」を含めたファイル名の拡張子が返されるので、例えば、File.extname("abc.def.txt")
とすれば、「.txt」が取り出せる。したがって、File.extname("abc.def.txt").partition(".")
とすれば、3つ目の返り値として拡張子の「txt」が返せる。
文字列は配列と同じように扱える
文字列に含まれる各文字は、配列と同じようにインデックスを指定すれば利用できる。この性質は文字列を取り扱うのにとても便利だ。こちらも簡単な例で見てみよう。
str1 = "ruby"
for i in 0..str1.length - 1
puts str1[i]
end
str1[3] = "l"
str1[4] = "e"
puts str1
|
文字列の各文字は0から始まるインデックスを指定して参照したり、代入したりできる。元の文字列の長さを超えるようなインデックスを指定すれば文字を追加することもできる。
実行例は以下の通り。for
文により1文字ずつ出力した後、文字列の一部を変更して出力する。
$ ruby sample004.rb
r
u
b
y
ruble
$
|
"ruby"という文字列の0文字目から3文字目を出力した後、3文字目を"l"に、4文字目を"e"に変更して出力した。
文字列に含まれる文字を順に利用するには
リスト1.4の前半のように文字列の全ての文字を1つずつ処理する場合は、for
文の代わりに、String
クラスのeach_char
メソッドを使うと便利だ。インデックスを使わないので、コードが簡潔になり、変数名のスペルミスなどの誤りも防げる。
str1 = "ruby"
str1.each_char{|c|
puts c
}
|
each_char
メソッドでは、文字列を| |
で囲まれた変数に1文字ずつ代入ながらその後の{ }
の中の文を繰り返し実行する。
実行結果は実行例1.4の前半と同じなので省略する。String
クラスの性質やメソッドには他にも取り上げるべきことが数多くあるが、今回はこれぐらいにとどめておく。最後に、桁数の多い10進整数を3桁ごとにカンマで区切って出力するための関数の例を示しておこう。
数値に桁区切りスタイルを適用して文字列に変換するには
数値を何桁かずつに区切るにはさまざまな方法(例えば正規表現を使って1行で記述する方法など)があるが、今回はRuby初心者向けに分かりやすいシンプルな方法でやってみよう。
まず、数値を文字列に変換する。続いて、末尾から1文字ずつ取り出し、3文字ごとにカンマを挿入する。ここでは繰り返し処理の流れが見やすくなるように、末尾から1文字ずつ取り出さず、文字列を逆順に変換してから、先頭から1文字ずつ取り出そう。ここまでに説明してきたメソッドや、文字列を配列として取り扱う方法を応用すれば実現できる。
def addcomma(num, sep)
temp = num.to_s.reverse
result = ""
for i in 0..temp.length - 1
if i % sep == 0 and i != 0
result = "," + result
end
result = temp[i] + result
end
return result
end
puts(addcomma(1_000_000,3))
|
ここで作成したaddcomma
関数には数値を第1引数に、カンマを挿入する桁数を第2引数に指定する。addcomma
関数の中では、reverse
メソッドを使って文字列を逆順にしてから1文字ずつ取り出す。3文字ごとにカンマを挿入して、その文字を前につないでいけばよい。
実行例は以下の通り。
$ ruby sample006.rb
1,000,000
$
|
ここでは3桁ごとにカンマを入れてみた。addcomma
関数の第2引数に4を指定すれば4桁ごとに区切ることができる。ぜひ試してみてほしい。
リスト1.6のaddcomma
関数では骨格となる部分を作っただけなので、整数しか取り扱えない。そこで、すでに見たpartition
メソッドを使って、整数部と小数部に分けて処理を行い、小数点のある数値にも桁区切りスタイルを適用できるようにしよう。なお、文字列を逆順にしなくても、文字列を後ろから取り出していけば同じことができる。そのような処理を行う例を示しておく。
def addcomma(num, sep)
temp = num.to_s # 逆順にしない。単に文字列に変換するだけ
# "."で3分割する
intpart, p, fracpart = temp.partition(".")
# 整数部にカンマを挿入する
result = ""
for i in 0..intpart.length - 1
idx = intpart.length - i - 1 # iが増えればidxは減る
if i % sep == 0 and i != 0
result = "," + result
end
result = intpart[idx] + result
end
# 整数部と小数部を連結して返す
if p == "."
return result + p + fracpart
else
return result
end
end
puts(addcomma(1_000_000.12,3))
|
String
クラスのpartition
メソッドを使って、文字列を整数部と小数部に分ける。整数部の処理はリスト1.6と同じだが、インデックスの指定方法に注意。整数部の桁数(=intpart.length
)が7であるとすると、変数i
の値は「0, 1, 2, ..., 6」と増えていくが、idx
の値は「6, 5, 4, ..., 0」と減っていく。このようにして文字列を後ろから処理する。最後に、小数部がある場合は、整数部に小数点と小数点以下を連結して返す。
実行例は以下の通り。
$ ruby sample007.rb
1,000,000.12
$
|
小数点以下があっても正しく桁区切りができるようになった。リスト1.7のaddcomma
メソッドにさまざまな数値を指定して試してみるといい。
今回は比較的小さな技をいくつか取り上げた後、それらを組み合わせることによって実用的なプログラムを作成する例を示した。次回もいくつかの小技とそれらの合わせ技を紹介したいと思う。
まとめ
Rubyでは、数値リテラルの桁区切り記号として_
が使える。_
は実行時には無視されるので、桁数の大きな数値を見やすくするのに役に立つ。文字列を逆順に変換するメソッドには、非破壊的なreverse
メソッドと破壊的なreverse!
メソッドがある。partition
メソッドを使えば、文字列を「前半」「セパレーター」「後半」といった3つの部分に分割できる。また文字列は、配列と同じようにインデックスを指定することにより各文字を取り扱える。
API:reverseメソッド|reverse!メソッド|partitionメソッド|each_charメソッド カテゴリ:Stringクラス
API:printf カテゴリ:Kernelモジュール
※以下では、本稿の前後を合わせて5回分(第4回~第8回)のみ表示しています。
連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。
4. Rubyをインストール/アップデートするには?(Windows編)
Windows上でのRubyプログラミングを始める入門者向けに、Ruby環境の構築方法、複数バージョンのインストール方法、バージョンのアップデート方法を説明する。
5. 代入により決まる変数のデータ型/丸め誤差が発生する浮動小数点数の比較― コーディングミスを防ぐには?(3)
初心者向けにRubyプログラミングの落とし穴を紹介。代入する値により変数のデータ型が決まることに関する注意点と、浮動小数点数の比較における丸め誤差の問題と回避方法について説明する。
6. 【現在、表示中】≫ 桁区切り数値の記述と出力/文字列を逆順に/文字列の分割/文字列の各文字の利用 ― コーディングミスを防ぐには?(4)
初心者向けにRubyプログラミングの落とし穴を紹介。桁区切り指定で数値リテラルを記述する方法や、桁区切りの数値文字列を出力する方法、文字列を逆順に並べ替える方法、文字列を文字列で分割する方法、文字列を1文字ずつ扱う方法を説明する。
8. if修飾子/unless文/case文 ― ちょっと便利な条件分岐の構文とは?
Rubyには、if文のような一般的なもの以外にも、if修飾子/unless文/unless修飾子/case文といった便利な条件分岐の構文が用意されている。その基本的な使い方を解説。