Ruby
簡単入門 第
3
回
コンピュータ系実験
IV
資料
担当:冨永
今回は入力に便利なARGFと、組み込みクラスRegexp,
Symbol,Hashの使い方について学びます。
1
ARGF
ARGFは、コマンド行引数で指定されたファイルを全て
つなげた文字列を表わします1。コマンド行引数が指定さ
れない場合には、標準入力(キーボード入力)になります。
演習1 試してみよう
次のプログラムは、入力ファイルの内容を全て大文字に して出力する。
ARGF.each do |x|
print x.upcase
end
このプログラム(upcase.rbという名前で作ったとす
る)の入力を、
• ファイ ル ひ と つ を 引 数 と し て 指 定 す る(ruby
upcase.rb ファイル名)
• ファイ ル 複 数 を 引 数 と し て 指 定 す る(ruby
upcase.rb ファイル名1 ファイル名2 …)
• 引数にファイルを指定せず、キーボードから入力す
る(ruby upcase.rb)
の3つのやり方で与えて、正しく動くことを確認せよ。
2
正規表現
正規表現regular expressionとは、文字列の集合を表現
するものです。文字列に合うがら柄(パターンpattern)と考
えればよいでしょう。
コンピュータで使われる正規表現にはいろいろな
種類があります。シェルで使われる「?」(任意の 1 文
1ただしARGFのクラスはStringではありません。eachメソッド
の動作がStringに似ているだけです。
字に合う)や「*」(0 文字以上の任意の文字列に合
う)も正規表現の一種です2。これらは「
*.rb」(a.rb, b.rb, foo.rb,test1.rbなどに合う)や「work.?」
(work.h,work.c,work.yなどに合う)のように使わ
れます。
Rubyの正規表現は斜線「/」で囲って「/· · ·/」の形で 表記します。以下に基本的な正規表現の書き方を説明し ます。
• /./ 任意の1文字に合う
合う文字列の例:a b A 2
• /[abc]/ aとbとcに合う
合う文字列の例:a b c
• /[a-j]/ aからjまでの文字1文字に合う
合う文字列の例:a b k h
• /[A-Za-z]/ 英字1文字に合う
• /[0-9]/ 数字1文字に合う
• /[ˆA-Za-z]/ 英字でない文字1文字に合う
• /.../ 3文字に合う
• /[A-Z]../ 英大文字から始まっている3文字に 合う
• | 選択
/.|../ 1文字と2文字に合う
• * 繰り返し、0回以上
/.*/ 任意の0文字以上の文字列に合う
• ˆ 先頭
/ˆ[A-Z]/ 先頭が大文字
• $ 末尾
/\.$/ 末尾がピリオド
• () 括弧でまとめる
/(ab)*/ λ,ab,abab,. . .
3
使い方その
1
:合うかどうか調べる
以下では変数sは文字列を示しているとします。ただ
しs中に改行文字はないものとします3。
if /[A-Z]/ =˜ s
# sに大文字が入っていれば
何かする
end
2ただしピリオド「.」で始まるファイル名には合わないような工夫 がされています。
if /this/ =˜ s
# sにthisという文字列が入っていれば
何かする
end
if /ˆ[A-Z]/ =˜ s
# sが大文字で始まっていれば
何かする
end
if /ˆ[A-Z]*$/ =˜ s
# sが大文字だけでできていれば
何かする
end
if /ˆ[01][01]*$/ =˜ s # sが2進数ならば
何かする
end
• =˜ 合うか
合えば、合った部分の先頭の添字
合わなければnil
• !˜ 合わないか
合わなければtrue、合えばfalse
演習2 正規表現を使ってファイル内文字列検索
入力ファイル内にある「his」という文字列を含む行
を全て表示するプログラムを作れ。入力ファイルはARGF
を使って扱うこと。
4
使い方その
2
:合った部分を取り出す
指定した正規表現に合った部分は変数$&に入ります。
s = "I’m 20 years old."
/[0-9][0-9]*/ =˜ s # [0-9]+ でも同じ puts "Your are #{$&} years old."
括弧がある場合には、それぞれの括弧に合った部分が
$1,$2,. . .に入ります。
s = "Taro is a student."
/([A-Za-z]*) is ([A-Za-z ]*)\./ =˜ s puts "Is #{$1} #{$2}?"
合うところが複数ある場合には、最初に少しでも合う ところからできるだけ長くとります。
/[a-z]*/ =˜ "cowboy" # $& #=> "cowboy"
/[a-z]+/ =˜ "This is a pencil."
# $& #=> "his"
/[a-z]*/ =˜ "This is a pencil." # $& #=> ""
演習3 整数をファイルから取り出す
入力ファイルから整数値を取り出して表示するプログラ
ムを作れ。ただし1行に整数は高々1つしかないと仮定
してよい。例えば次のようなファイルを入力として与え ると、
Hello!
I’m 21 years old.
I bought a pen at a 100-yen shop.
Bye!
以下のように表示されるように。
21
100
5
正規表現を使う便利なメソッド
正規表現を使う便利なメソッドは多くありますが、ここでは2つだけ紹介します。
5.1
文字列置換
substitution
String#subは、第1引数に指定した正規表現に合う
部分を第2引数の文字列で置き換えます。
"I’m Taro.".sub(/[ˆA-Za-z]/, "XYZ")
#=> "IXYZm Taro."
"I’m Taro.".sub(/[ˆA-Za-z]/, "")
String#gsubは合う部分すべてを(globally)それぞ
れ置き換えます。
"I’m Taro.".gsub(/[ˆA-Za-z]/, "XYZ")
#=> "IXYZmXYZTaroXYZ"
"I’m Taro.".gsub(/[ˆA-Za-z]/, "")
#=> "ImTaro"
5.2
語の切り出し
String#scan
String#scanは、指定された正規表現が表わす語を
文字列の中から切り出して配列にまとめます。
"Thank you, Tom!".scan(/[A-Za-z]+/)
#=> ["Thank", "you", "Tom"]
"01011001".scan(/./)
#=> ["0","1","0","1","1","0","0","1"]
演習4 伏せ字化
入力ファイル中の自分の名前を全て「XXXXX」という伏
せ字にするプログラムを作れ。
演習5 整数の取り出し(その2)
入力ファイル中の整数を全て取り出し、その合計を表示
するプログラムを作れ。ただし1行に複数の整数がある
可能性もあるとする。
6
Symbol
クラス
Symbolクラスのインスタンスはシンボルといいます。
シンボルは文字列と1対1に対応するオブジェクトです。
シンボルは:foobarとか:’foobar’などのリテラル
で得られます。
Stringとの大きな違いは、Stringクラスでは複数
のインスタンスが同じ文字列を表すことがあるのに対し て同じ文字列を表すシンボルはひとつしかないことと、シ
ンボルは変更不可immutableであることです。
s = "hello"
t = "hello"
s.equal?(t) #=> false
s[0] #=> "h"
s[0] = "H"
s #=> "Hello"
x = :hello
y = :hello
x.equal?(y) #=> true
x[0] #=> "h"
x[0] = "H" # エラー
同じ文字列なら必ず同じオブジェクトなので安心して
使えます。Rubyでは次に説明するHashクラスにおける
鍵としてよく使います。
シンボルに.to sするとStringクラスのオブジェク
トが得られます。逆にStringクラスのオブジェクトに
.to symするとシンボルが得られます。
7
Hash
クラス
Hashクラスは、鍵keyキ ー と値valueの対(それらへの参
照)を格納するデータ構造です。「辞書」「マップ」「連想 配列」などとも呼ばれます。
どんなクラスのものでも鍵や値になれます。鍵を指定 すると値を取り出すことができます。
h = Hash.new
# 果物名が鍵、値段が値
h["apple"] = 100
h["kiwi"] = 80
h["mango"] = 200
puts h["apple"] # 100 が表示される
a = [] # 果物の名前 h = {} # 値段
ARGF.each do |x|
/([a-z]+) ([0-9]+)/ =˜ x
a << $1
h[$1] = $2.to_i
end
a.sort.each do |f|
puts "#{f} は #{h[f]} 円です" end
h = {} # 空のハッシュ p h["foo"] # nil が表示される
8
Hash
リテラル
h = { "apple" => 100,
"kiwi" => 80,
"mango" => 200 }
h["apple"] #=> 100
シンボルを使うと、
h = { :apple => 100,
:kiwi => 80,
:mango => 200 }
h[:apple] #=> 100
Ruby 1.9以降では、シンボルを鍵にする場合には以下
のように書けます。
h = { apple: 100,
kiwi: 80,
mango: 200 }
9
Hash
のメソッド
• key?:指定したものが鍵として登録されているかど
うかを返します。
• keys:全ての鍵を配列にして返します。
• values:全ての値を配列にして返します。
• to a: 全ての対を配列にして返します。ひとつの要
素が大きさ2の配列(第0要素が鍵、第1要素が値)
です。
• each key:それぞれの鍵についてブロックを実行し ます。
• each value:それぞれの値についてブロックを実行 します。
• each:それぞれの鍵と値の対についてブロックを実
行します。ブロック局所変数は|k,v|のように宣言
します。
h = { "apple" => 100,
"kiwi" => 80,
"mango" => 100 }
h.key?("apple") #=> true
h.key?("papaya") #=> false
h.keys
#=> ["apple", "mango", "kiwi"]
h.values
#=> [100, 100, 80]
h.to_a.each do |x|
puts "#{x[0]} は #{x[1]} 円です" end
h.each do |k,v|
puts "#{k} は #{v} 円です" end
演習6 配列からハッシュを作る
100m走の順位を示した配列がある。例えば以下のよ
うなものである。
a = ["Hanako", "Taro", "Jiro"]
この場合Hanakoが1位、Jiroが3位とする。
このような配列aから、名前を鍵として指定すると
順位を返すようなハッシュhを作るコードを示せ。(つ
まり、h["Taro"]とすると整数の2が得られるように
する。)
演習7 ハッシュと配列
単語とその意味をデータとして保持しておき、入力さ れた単語の意味を表示するプログラムを作ることを考え る。単語の意味のデータはプログラム中に定数(リテラ ル)として与えることにする。例えば以下のようなもの を与える。
単語:"apple"
意味:"round fruit with red or green skin"
単語:"kiwi"
意味:"a flightless bird in New Zealand"
単語:"mango"
意味:"a yellowish-red tropical fruit"
そしてキーボードから「kiwi」と入力すると、「a flightless bird in New Zealand」と表示するものとする。
2.ハッシュを使わずに、配列を使って、このプログラム を実現せよ。
10
インスタンスの複製
インスタンスを複製するにはcloneを使います。
s = "hello!"
t = s.clone # 上の文字列の複製を作る
a = [s, t]
b = a.clone
それぞれのIDを.object idで表示してみましょう。
puts s.object_id
puts t.object_id
puts a.object_id
puts b.object_id
puts a[0].object_id
puts b[0].object_id
cloneは「浅い複製」をします。「深い複製」をしたい
場合には自分でコードを書く必要があります。
演習8 深い複製
aからbへ、配列の要素もそれぞれ複製するような
コードを書け。
11
応用問題
今まで習ってきたRubyの使い方を組み合わせて、少し
高度なプログラムを作ってみましょう。
演習9 データ処理
ある人々の体重の分布を以下のように表わしたファイ ルがある。
30 2
40 4
50 7
60 9
70 6
80 4
90 1
行はそれぞれ、30kg台の人は2人、40kg台の人は4人、
などを表わしている。このようなファイルを与えると、
その階級までの累績人数を3番目の欄として加えて出力
するプログラムを作れ。つまり出力が、
30 2 2
40 4 6
50 7 13
60 9 22
70 6 28
80 4 32
90 1 33
などとなるように。
演習10 ログ処理
あるWWWサーバのアクセス記録ファイルの各行は
次のような形式になっている。
YYYY/MM/DD HH:MM:SS ユーザ名 "アクセスしたファイル"
例えば以下のように。
2007/12/13 23:58:02 taro "/index.html" 2007/12/13 23:59:31 taro "/image/logo.png" 2007/12/13 23:59:42 - "/links/tarotaro.html" 2007/12/14 00:00:39 taro "/links/index.html" 2007/12/14 00:01:06 kotaro "/index.html" 2007/12/14 00:02:25 taro "/index.html"
ユーザ名は英数字から成る。ただしユーザ名「-」は未
登録ユーザを表わすとする。ファイル名には二重引用符 「"」は含まれないとする。
このようなファイルを与えると、ユーザtaroがアク
セスしたファイルの名前とアクセス回数の一覧を次のよ うな形式で出力するプログラムを作れ。
2 /index.html
1 /links/index.html
1 /image/logo.png
演習11 テキスト処理
英文の文章が入っているテキストファイルが与えられ ると、そのファイルにおける各語の出現回数を出力する プログラムを作れ。例えば、
Show your flowcharts and conceal your tables and I will be mystified. Show your tables and your flowcharts will be obvious.
というファイルが与えられたら、
your 4 flowcharts 2 and 3
conceal 1 tables 2 i 1 will 2 be 2
mystified 1 obvious 1