プログラミング言語/Ruby/Rubyそぞろ歩き
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
検索
|
最終更新
|
ヘルプ
|
ログイン
]
開始行:
[[プログラミング言語/Ruby]]
#contents
----------
* Rubyそぞろ歩き [#q4d49f91]
というわけでPerlの入門書"Learning Perl"(Randal L.Schwar...
順当にパクって行きたいと思います。
これから小規模なプログラムを書きながらRubyの様々な機能...
ですが、Rubyはbeauty Perlたり得てもbeauty Perlそのもので...
入門書として極めて優れたリャマ本にならい、個々の機能に...
簡単に済ませますし、Perlのソースがベースかつ複雑な機能は...
サンプルプログラムにはかなりRuby Wayではない書き方も沢山...
このドキュメントに現れた書き方に決して固執しないようにし...
** 1. "Hello, world"プログラム [#d7f66ce7]
さて、とりあえず何かさせてみましょう。
以下はチュートリアル文書の通過儀礼とでも言うべき例のプロ...
Ruby版です。
#!/usr/bin/ruby
print "Hello, world!\n"
1行目は、このプログラムがRubyプログラムであることをシェ...
Ruby自身にとってはコメントになっています。
多くのシェルやawk、そしてもちろんPerlと同じように、シャー...
がコメントです。
2行目が、このプログラム中に実行される仕事のすべてです。
あらかじめ用意されているprintという機能を呼び出して、コン...
出力を成し遂げます。
PerlやCでは、このような単純文は最後にセミコロン(;)で終...
Rubyではそれが改行に取って代わります。もちろんセミコロン...
Ruby Wayではありません。
この文はBASICな道を通ってきた人には命令のように映るかも...
print(...)のようにカッコをつけると関数のように見えるでし...
正解はどっちでもなくてメソッド呼び出しなのですが、この意...
支障はあまりありません。
** 2. 質問してその答えを覚えておくこと [#a460f61b]
今度は、少しだけ凝ったことをしてみましょう。hello, world...
よそよそしくて他人行儀な感じがします。そこで、相手の名前...
ようにしてみましょう。これを実現するには、
- 名前を尋ねること
- 名前を入力してもらうこと
- 入力された名前を保持すること
が必要になります。
入力された名前のような、値を保持する手段として変数があ...
Rubyにはいくつかの種類がありますが、ここではローカル変数...
"name"のように、素直にアルファベッドで変数を命名してくだ...
次に名前の入力ですが、コンソールプログラムでは、プロン...
入力を要求するような旨の文章や記号を印字して、ユーザーか...
のが一般的です。
この仕事のうち、プロンプトの印字については前回のプログラ...
いると思います。printを使うのです。あとは入力を受け付ける...
これはRuby内で標準入力を表すSTDINというモノに関わります。
Rubyにおけるモノ(=オブジェクト)には、それぞれ様々な機能(=...
いて、それらを呼び出すことによってRubyのプログラムは進行...
今回用いるのは、STDINが持つgetsというメソッドです。これは...
読み込みます。
つまり、キーボードからだとエンターキーで入力を終わるとい...
以上をまとめると、次のようになります。
print "名前は? "
name = STDIN.gets
この時点では、nameの値の末尾には、改行文字(\n)がくっつ...
Vipperと入力したら、nameの値はVipper\nになっています)。
この改行文字を取り除くには、chop!を使います。
入力され、nameに保持された値は当然文字列なので、Ruby内...
モノの種類(=クラス)から生み出されたものです。
chop!は、Stringオブジェクトが持つメソッドです。自らが表し...
内容の末尾を、1字ぶん削ります((1字、というのは正確ではな...
(以下、Stringオブジェクトが持つchop!メソッドをString#chop...
name.chop!
ここまでくれば、あとはHelloと表示して、その後ろに変数na...
だけです。これは、ダブルクォートで囲んだ("...")文字列の中...
「#{...}」という記法(式展開)を使って行います。
シェルやPerlの似たようなそれとは違い、Rubyでは式なら何で...
print "Hello, #{name}!\n"
これらをまとめると次のプログラムが得られます。
#!/usr/bin/ruby
print "名前は? "
name = STDIN.gets
name.chop!
print "Hello, #{name}!\n"
** 3. 選択処理を追加する [#q684bbf8]
ここで、Vipperに対しては特別な挨拶をして、それ以外の人...
ようにしましょう。入力された名前と文字列"Vipper"を比較し...
特別扱いする、という処理を行います。
それらは(キーワードだけはC風の)ifとかelseとかの構文で分岐...
#!/usr/bin/ruby
print "名前は? "
name = STDIN.gets
name.chop!
if name == "Vipper"
print "Hello, Vipper! よく来たな。\n"
else
print "Hello, #{name}!\n" #普通の挨拶
end
演算子"=="は、ここでは文字列を
比較します((左辺のオブジェクトが動作を決めてます))(仮に右...
場合、Rubyが出来る限り文字列に変換しようとしてくれます)。
それら文字列が等しければ(内容の比較)、結果は真になります。
if文は、ifで始まってendで終わります。与えられた式が真な...
実行し、そうでなければelseの後の部分を実行します。
** 4. 合言葉を当てる [#s0f0c778]
さて、名前を入力してもらったところで、プログラムを起動...
当ててもらうことにしましょう。プログラムは、Vipper以外の...
言い当てるまで、合言葉を入力するように繰り返し求めます。...
披露してから説明しましょう。
#!/usr/bin/ruby
secretword = "2ch" #合言葉
print "名前は? "
name = STDIN.gets
name.chop!
if name == "Vipper"
print "Hello, Vipper! よく来たな。\n"
else
print "Hello, #{name}!\n" #普通の挨拶
print "合言葉は? "
guess = STDIN.gets
guess.chop!
while guess != secretword
print "ちょwwおまwwww違うwwwww 合言葉は? "
guess = STDIN.gets
guess.chop!
end
end
まず最初に、もう一つのローカル変数secretwordに、合言葉...
挨拶の後に、Vipper以外の人には、(printによって)合言葉を当...
ユーザが当て推量した言葉を入力すると、演算子"!="によって...
比較します。この場合"!="は文字列同士が違う場合に真を返し...
(この演算子の働きは"=="を用いた場合の論理的反転です)
whileは式の結果によって制御される繰り返しで、式が真である...
続けます。
もちろん、これは決して安全性の高いプログラムではありま...
当て推量するのに飽きたユーザはCtrl+CでRubyと手を切ること...
あるいはソースのsecretwordの代入式の右辺を見てカンニング...
からです。
しかし、私が書いているのはセキュリティシステムではなくて...
猿真似なので、言い訳まで真似だとしても目をつぶってくださ...
** 5. 複数の合言葉を扱う [#ee3a5eb9]
複数の合言葉のうちどれか1つを当てればよいことにするには...
これからそれをお話したいと思います。
これまで学んだ方法を応用するならば、ローカル変数に正解を...
おいて、ユーザの当て推量と次々比較していくような手になる...
しかし、そんなやり方だと、合言葉リストを変更したり、それ...
読み込んだり、曜日に応じて合言葉を変えたりするような仕組...
困難です。
よりスマートな解決策は、配列(Array)と呼ばれるデータ構造に、
許される答えを全て入れておくというものです。
配列の要素はそれぞれがローカル変数のように値をセットしたり
取り出したりすることができます。配列全体に対して、一挙に...
こともできます。
ここでは、次のようにして、配列という種類(クラス)のモノ(オ...
作り、3つの合言葉入れて、wordsというローカル変数にしてお...
words = ["2ch", "vip", "mona"]
Arrayオブジェクトに入れてしまえば、添え字付けによって、...
アクセスすることができます。
ですから、words[0]は2ch、words[1]はvip、words[2]はmonaに...
添え字(インデックス)には式を使うこともできます。例えば
変数idxに2をセットしておけば、words[idx]はmona
になります。
先程の例に戻ると、次のようなプログラムになります。
#!/usr/bin/ruby
words = ["2ch", "vip", "mona"]
print "名前は? "
name = STDIN.gets
name.chop!
if name == "Vipper"
print "Hello, Vipper! よく来たな。\n"
else
print "Hello, #{name}!\n" #普通の挨拶
print "合言葉は? "
guess = STDIN.gets
guess.chop!
i = 0 #最初の合言葉から調べ始める
correct = "maybe" #合言葉が当たったかどうか?
while correct == "maybe" #合言葉を正しく当てるまで繰...
if words[i] == guess #当たった?
correct = "yes" #正解!
elsif i < words.size #他に調べる合言葉があるか?
i = i + 1 #次回は次の合言葉を調べる
else #これ以上合言葉はない、だから間違い
print "ちょwwおまwwww違うwwwww 合言葉は? "
guess = STDIN.gets
guess.chop!
i = 0 #最初の合言葉からチェックをやり直す
end
end #「while 正しく当てるまで」の終わり
end #「if Vipper 以外」の終わり
合言葉を調べている最中なのか、あるいはすでに見つかった...
変数correctを使っています。
このプログラムは、if-else-end文のelsifブロックの使用例...
Cやawkには、これに相当するものは存在しません。
elsifブロックは、elseブロックと新しいif条件を合わせたもの...
もう一組のif-endブロックをネストしないで済みます。
if-elsif-elsif-elseif-elseの連鎖によって、一連の条件を...
とてもPerlらしいやり方らしいです。Rubyではそうでもないで...
case文という、Cのswicth文を幾分高機能にしたような構文があ...
** 6. 1人1人に別の合言葉を割り当てる [#p9b75798]
たった今作ったブログラムでは、通りすがりの人間でも、3つ...
当てれば、成功してしまいます。もし1人1人に別の合言葉を割...
人と合言葉を対応づけるテーブルが必要になります。
|人|合言葉|
|Hiroyuki|umai-bou|
|Boom|fugashi|
|Yaruo|onani|
このようなテーブルを表現するのに最も適した方法は、連想...
データ構造を使うことです。RubyではHashクラスから作ること...
Hashオブジェクトの各要素は、普通の配列と同じように、1個1...
持っています。ただ、Hashの個々の要素にアクセスするには
インデックスではなくキー(key)を使います。このキーには、Ru...
((つまりオブジェクト))ならなんでも用いることができます。
上のようなテーブルを表現するのに、人名を表すStringオフジ...
キーにして、合言葉のStringオブジェクトを格納するには次の...
します。
words = {
"Hiroyuki" => "umai-bou",
"Boom" => "fugashi",
"Yaruo" => "onani",
}
カンマで区切られたリストのそれぞれの、「=>」で区切られ...
Hashのキー1個とそれに対応するオブジェクトを表しています。
この代入文が、継続文字のたぐいを使わずに、数行にまたがっ...
注目しましょう。このような書き方ができるのは、Rubyプログ...
一般の空白文字は意味を持たないからです。
Boomの合言葉を取り出すには、Boomをキーとして、
words["Boom"]といった式によってHashオブジェクトwordsの要...
アクセスする必要があります。先程の配列と同じように、この...
得られる値はfugashiになります。
また、普通の配列(Array)と同様に、キーには任意の式を用い...
personに"Boom"をセットしてから、words[person]を評価すると、
同様にfugashiが得られます。
これらを1つにまとめると次のプログラムが得られます。
#!/usr/bin/ruby
words = {
"Hiroyuki" => "umai-bou",
"Boom" => "fugashi",
"Yaruo" => "onani",
}
print "名前は? "
name = STDIN.gets
name.chop!
if name == "Vipper"
print "Hello, Vipper! よく来たな。\n"
else
print "Hello, #{name}!\n" #普通の挨拶
secretword = words[name] #合言葉を得る
print "合言葉は? "
guess = STDIN.gets
guess.chop!
while guess != secretword
print "ちょwwおまwwww違うwwwww 合言葉は? "
guess = STDIN.gets
guess.chop!
end
end
合言葉を調べる部分に注目しましょう。合言葉が見つからな...
secretwordの値はnilという値になります。なのでnilであるかを
チェックすれば、その他全員に対するデフォルトの合言葉を設...
できます。この処理は次のようになります。
[... プログラムの前の部分を省略 ...]
secretword = words[name] #合言葉を得る
if secretword == nil #おや、見つからない
secretword = "fushianasan" #いいかお前ら、絶対に名...
end
print "合言葉は? "
[... プログラムの残りの部分を省略 ...]
** 7. いろいろな書き方を受け付けるようにする [#o35677d4]
もし、ユーザがVipperの代わりにvipperやVipper Yaruoなど...
その他大勢として扱われてしまうでしょう。なぜなら、==によ...
一致しているかどうかを調べるからです。
これをうまく扱う方法の1つを紹介しましょう。
Vipperそのものの代わりに、Vipperで始まる任意の文字列を...
話を進めましょう。sedやawkやgrepでは、正規表現(regular ex...
これを実現することができます。
正規表現とは、マッチする(一致する)文字列の集合を定義する...
Vipperで始まる任意の文字列にマッチするRubyの正規表現は、s...
同様に、^Vipperと
なります((ほんとはRuby的には\AVipperのほうがジャスティス)...
この正規表現が、nameに入っている文字列とマッチするかを...
マッチ用の演算子を使って次のようにします:
if name =~ /^Vipper/
## はい、マッチしました
else
## いいえ、マッチしませんでした
end
正規表現の前後をスラッシュで囲むことに注意しましょう。...
部分では、文字列の場合と同様に、スペースやその他の空白文...
これでほぼ完璧なのですが、あとvipperを受け付けることと、
Vipperlなどをはねのけることが残されています。vipperを受け...
スラッシュの直後に、''大文字と小文字を区別しない''ことを...
置きます。
Vipperlをはねのけるには、(viやいくつかのバージョンのgrepと
同様な)''単語の境界''を表す特別なマーカー\bを追加します。
これによって、正規表現中のrの直後には、英文字がこないこと...
結局、正規表現は/^vipper\b/iとなりますが、これは「文字列の
先頭((ほんとは「行の先頭」))にvipperがあり、その直後に英...
きてはならない。また、大文字小文字のどちらでもOK」という...
これをプログラムの残りの部分と合わせると、次のようにな...
#!/usr/bin/ruby
words = {
"Hiroyuki" => "umai-bou",
"Boom" => "fugashi",
"Yaruo" => "onani",
}
print "名前は? "
name = STDIN.gets
name.chop!
if name =~ /^vipper\b/i
print "Hello, Vipper! よく来たな。\n"
else
print "Hello, #{name}!\n" #普通の挨拶
secretword = words[name] #合言葉を得る
if secretword == nil #おや、見つからない
secretword = "fushianasan" #いいかお前ら、絶対に名...
end
print "合言葉は? "
guess = STDIN.gets
guess.chop!
while guess != secretword
print "ちょwwおまwwww違うwwwww 合言葉は? "
guess = STDIN.gets
guess.chop!
end
end
ご覧のように、このプログラムを、最初のちっぽけなhello w...
比べると、「よくぞここまで成長したものだ」という感慨にと...
このプログラムは、まだまだ十分に小さく取り扱いも楽な上に...
なかなかの働き者です。これがRuby流のプログラミングなので...
Perlは、UNIXのあらゆる標準ユーティリティ(非標準のものも...
持っている、あらゆる正規表現の機能を提供しています。それ...
Perlの文字列マッチはこの惑星上でほぼ最高速なので、性能は...
…らしいですけど、Rubyではどうなんでしょうね。
1.8系までのRubyの正規表現エンジンは、Emacsでの実装を改良...
したとかいうややこしい歴史を経ていてだいぶブラックボック...
もっと早くしてよーとねだっても難しいかも。
とりあえずPerl5互換なので機能は十分でしょう。
** 8. その他大勢も公平に扱う [#zc00710f]
さて、これでVipperやvipperやVipper Yaruoなどと入力して...
が、Vipper以外のユーザに関してはどうでしょうか? Yaruoは相...
タイプしなければなりません(yaruoの後ろにスペース1つ置くこ...
yaruoに対しても公平であるためには、テーブルを引きにいく...
行の最初の単語を取り出して小文字にする処理が必要です。こ...
メソッドを用います。
String#subは正規表現にマッチするものを探して、それを与え...
置き換えます。
String#downcaseは、文字列を小文字にまとめるのに使います。
まずは、String#subです。私たちがしたいのは、nameの内容...
構成しない最初の文字を探して、そこから文字列の末尾までを...
このために必要な正規表現は/\W.*/です―――\Wは単語を構成しな...
(nonword charactor: 英文字、数字、下線以外のもの)を表し、...
行末までの文字すべてを表します。さて、マッチした文字を削...
して、文字列のうち正規表現にマッチした部分を空文字列で置...
name = name.sub(/\W.*/, "")
第一引数にマッチさせる正規表現、第二引数に置き換える文...
String#subの基本的な使い方です。
String#downcaseは名前通り、文字列をまとめて小文字にして
返します((String#trメソッドというものもあるのですが、マイ...
これらを、残りの部分とドッキングすると、次のプログラム...
#!/usr/bin/ruby
words = {
"hiroyuki" => "umai-bou",
"boom" => "fugashi",
"yaruo" => "onani",
}
print "名前は? "
name = STDIN.gets
name.chop!
original_name = name #挨拶で使うためにとっておく
name = name.sub(/\W.*/, "") #最初の単語より後ろの部分を...
name = name.downcase #全てを小文字にしてしまう
if name == "vipper" #今度はこの比較のやり方でおk
print "Hello, Vipper! よく来たな。\n"
else
print "Hello, #{original_name}!\n" #普通の挨拶
secretword = words[name] #合言葉を得る
if secretword == nil #おや、見つからない
secretword = "fushianasan" #いいかお前ら、絶対に名...
end
print "合言葉は? "
guess = STDIN.gets
guess.chop!
while guess != secretword
print "ちょwwおまwwww違うwwwww 合言葉は? "
guess = STDIN.gets
guess.chop!
end
end
前のバージョンでは、Vipperをチェックするのに正規表現を...
ここでは以前使ったような単純な比較に逆戻りしています。な...
新しく追加した置換と変換処理によって、vipper yaruoもVippe...
なってしまうからです。
そしてBoomやBoom kunはともにboomになり、HiroyukiやHiroyuk...
hiroyukiになる、というようにVipper以外の人たちも公平な扱...
なります。
文を2、3個追加するだけで、プログラムはずいぶんユーザフ...
ちょっとキーを叩くだけで、高度な文字列操作を表現できるの...
利点の一つなのです。…と、LLが流行ってきた昨今となってはあ...
でしょうか。
しかし、比較したりテーブルを調べたりするために名前を加...
入力された名前そのものは壊されてしまいます。そこで、加工...
original_nameに保存しておきます。(C言語の識別子と同様に、...
英文字、数字、下線から構成され、長さはほぼ無制限です。)
こうしておけば、後ほど必要なときにoriginal_nameを参照する...
Rubyには、文字列を調べたり、いじったりする手段がたくさ...
Stringクラスのメソッドはもちろん、Regexp(正規表現)オブジ...
関わってきますし、標準添付ライブラリにStringIOやStringSca...
あります。
** 9. もう少しモジュール性を高める [#p4896a8a]
作成中…
終了行:
[[プログラミング言語/Ruby]]
#contents
----------
* Rubyそぞろ歩き [#q4d49f91]
というわけでPerlの入門書"Learning Perl"(Randal L.Schwar...
順当にパクって行きたいと思います。
これから小規模なプログラムを書きながらRubyの様々な機能...
ですが、Rubyはbeauty Perlたり得てもbeauty Perlそのもので...
入門書として極めて優れたリャマ本にならい、個々の機能に...
簡単に済ませますし、Perlのソースがベースかつ複雑な機能は...
サンプルプログラムにはかなりRuby Wayではない書き方も沢山...
このドキュメントに現れた書き方に決して固執しないようにし...
** 1. "Hello, world"プログラム [#d7f66ce7]
さて、とりあえず何かさせてみましょう。
以下はチュートリアル文書の通過儀礼とでも言うべき例のプロ...
Ruby版です。
#!/usr/bin/ruby
print "Hello, world!\n"
1行目は、このプログラムがRubyプログラムであることをシェ...
Ruby自身にとってはコメントになっています。
多くのシェルやawk、そしてもちろんPerlと同じように、シャー...
がコメントです。
2行目が、このプログラム中に実行される仕事のすべてです。
あらかじめ用意されているprintという機能を呼び出して、コン...
出力を成し遂げます。
PerlやCでは、このような単純文は最後にセミコロン(;)で終...
Rubyではそれが改行に取って代わります。もちろんセミコロン...
Ruby Wayではありません。
この文はBASICな道を通ってきた人には命令のように映るかも...
print(...)のようにカッコをつけると関数のように見えるでし...
正解はどっちでもなくてメソッド呼び出しなのですが、この意...
支障はあまりありません。
** 2. 質問してその答えを覚えておくこと [#a460f61b]
今度は、少しだけ凝ったことをしてみましょう。hello, world...
よそよそしくて他人行儀な感じがします。そこで、相手の名前...
ようにしてみましょう。これを実現するには、
- 名前を尋ねること
- 名前を入力してもらうこと
- 入力された名前を保持すること
が必要になります。
入力された名前のような、値を保持する手段として変数があ...
Rubyにはいくつかの種類がありますが、ここではローカル変数...
"name"のように、素直にアルファベッドで変数を命名してくだ...
次に名前の入力ですが、コンソールプログラムでは、プロン...
入力を要求するような旨の文章や記号を印字して、ユーザーか...
のが一般的です。
この仕事のうち、プロンプトの印字については前回のプログラ...
いると思います。printを使うのです。あとは入力を受け付ける...
これはRuby内で標準入力を表すSTDINというモノに関わります。
Rubyにおけるモノ(=オブジェクト)には、それぞれ様々な機能(=...
いて、それらを呼び出すことによってRubyのプログラムは進行...
今回用いるのは、STDINが持つgetsというメソッドです。これは...
読み込みます。
つまり、キーボードからだとエンターキーで入力を終わるとい...
以上をまとめると、次のようになります。
print "名前は? "
name = STDIN.gets
この時点では、nameの値の末尾には、改行文字(\n)がくっつ...
Vipperと入力したら、nameの値はVipper\nになっています)。
この改行文字を取り除くには、chop!を使います。
入力され、nameに保持された値は当然文字列なので、Ruby内...
モノの種類(=クラス)から生み出されたものです。
chop!は、Stringオブジェクトが持つメソッドです。自らが表し...
内容の末尾を、1字ぶん削ります((1字、というのは正確ではな...
(以下、Stringオブジェクトが持つchop!メソッドをString#chop...
name.chop!
ここまでくれば、あとはHelloと表示して、その後ろに変数na...
だけです。これは、ダブルクォートで囲んだ("...")文字列の中...
「#{...}」という記法(式展開)を使って行います。
シェルやPerlの似たようなそれとは違い、Rubyでは式なら何で...
print "Hello, #{name}!\n"
これらをまとめると次のプログラムが得られます。
#!/usr/bin/ruby
print "名前は? "
name = STDIN.gets
name.chop!
print "Hello, #{name}!\n"
** 3. 選択処理を追加する [#q684bbf8]
ここで、Vipperに対しては特別な挨拶をして、それ以外の人...
ようにしましょう。入力された名前と文字列"Vipper"を比較し...
特別扱いする、という処理を行います。
それらは(キーワードだけはC風の)ifとかelseとかの構文で分岐...
#!/usr/bin/ruby
print "名前は? "
name = STDIN.gets
name.chop!
if name == "Vipper"
print "Hello, Vipper! よく来たな。\n"
else
print "Hello, #{name}!\n" #普通の挨拶
end
演算子"=="は、ここでは文字列を
比較します((左辺のオブジェクトが動作を決めてます))(仮に右...
場合、Rubyが出来る限り文字列に変換しようとしてくれます)。
それら文字列が等しければ(内容の比較)、結果は真になります。
if文は、ifで始まってendで終わります。与えられた式が真な...
実行し、そうでなければelseの後の部分を実行します。
** 4. 合言葉を当てる [#s0f0c778]
さて、名前を入力してもらったところで、プログラムを起動...
当ててもらうことにしましょう。プログラムは、Vipper以外の...
言い当てるまで、合言葉を入力するように繰り返し求めます。...
披露してから説明しましょう。
#!/usr/bin/ruby
secretword = "2ch" #合言葉
print "名前は? "
name = STDIN.gets
name.chop!
if name == "Vipper"
print "Hello, Vipper! よく来たな。\n"
else
print "Hello, #{name}!\n" #普通の挨拶
print "合言葉は? "
guess = STDIN.gets
guess.chop!
while guess != secretword
print "ちょwwおまwwww違うwwwww 合言葉は? "
guess = STDIN.gets
guess.chop!
end
end
まず最初に、もう一つのローカル変数secretwordに、合言葉...
挨拶の後に、Vipper以外の人には、(printによって)合言葉を当...
ユーザが当て推量した言葉を入力すると、演算子"!="によって...
比較します。この場合"!="は文字列同士が違う場合に真を返し...
(この演算子の働きは"=="を用いた場合の論理的反転です)
whileは式の結果によって制御される繰り返しで、式が真である...
続けます。
もちろん、これは決して安全性の高いプログラムではありま...
当て推量するのに飽きたユーザはCtrl+CでRubyと手を切ること...
あるいはソースのsecretwordの代入式の右辺を見てカンニング...
からです。
しかし、私が書いているのはセキュリティシステムではなくて...
猿真似なので、言い訳まで真似だとしても目をつぶってくださ...
** 5. 複数の合言葉を扱う [#ee3a5eb9]
複数の合言葉のうちどれか1つを当てればよいことにするには...
これからそれをお話したいと思います。
これまで学んだ方法を応用するならば、ローカル変数に正解を...
おいて、ユーザの当て推量と次々比較していくような手になる...
しかし、そんなやり方だと、合言葉リストを変更したり、それ...
読み込んだり、曜日に応じて合言葉を変えたりするような仕組...
困難です。
よりスマートな解決策は、配列(Array)と呼ばれるデータ構造に、
許される答えを全て入れておくというものです。
配列の要素はそれぞれがローカル変数のように値をセットしたり
取り出したりすることができます。配列全体に対して、一挙に...
こともできます。
ここでは、次のようにして、配列という種類(クラス)のモノ(オ...
作り、3つの合言葉入れて、wordsというローカル変数にしてお...
words = ["2ch", "vip", "mona"]
Arrayオブジェクトに入れてしまえば、添え字付けによって、...
アクセスすることができます。
ですから、words[0]は2ch、words[1]はvip、words[2]はmonaに...
添え字(インデックス)には式を使うこともできます。例えば
変数idxに2をセットしておけば、words[idx]はmona
になります。
先程の例に戻ると、次のようなプログラムになります。
#!/usr/bin/ruby
words = ["2ch", "vip", "mona"]
print "名前は? "
name = STDIN.gets
name.chop!
if name == "Vipper"
print "Hello, Vipper! よく来たな。\n"
else
print "Hello, #{name}!\n" #普通の挨拶
print "合言葉は? "
guess = STDIN.gets
guess.chop!
i = 0 #最初の合言葉から調べ始める
correct = "maybe" #合言葉が当たったかどうか?
while correct == "maybe" #合言葉を正しく当てるまで繰...
if words[i] == guess #当たった?
correct = "yes" #正解!
elsif i < words.size #他に調べる合言葉があるか?
i = i + 1 #次回は次の合言葉を調べる
else #これ以上合言葉はない、だから間違い
print "ちょwwおまwwww違うwwwww 合言葉は? "
guess = STDIN.gets
guess.chop!
i = 0 #最初の合言葉からチェックをやり直す
end
end #「while 正しく当てるまで」の終わり
end #「if Vipper 以外」の終わり
合言葉を調べている最中なのか、あるいはすでに見つかった...
変数correctを使っています。
このプログラムは、if-else-end文のelsifブロックの使用例...
Cやawkには、これに相当するものは存在しません。
elsifブロックは、elseブロックと新しいif条件を合わせたもの...
もう一組のif-endブロックをネストしないで済みます。
if-elsif-elsif-elseif-elseの連鎖によって、一連の条件を...
とてもPerlらしいやり方らしいです。Rubyではそうでもないで...
case文という、Cのswicth文を幾分高機能にしたような構文があ...
** 6. 1人1人に別の合言葉を割り当てる [#p9b75798]
たった今作ったブログラムでは、通りすがりの人間でも、3つ...
当てれば、成功してしまいます。もし1人1人に別の合言葉を割...
人と合言葉を対応づけるテーブルが必要になります。
|人|合言葉|
|Hiroyuki|umai-bou|
|Boom|fugashi|
|Yaruo|onani|
このようなテーブルを表現するのに最も適した方法は、連想...
データ構造を使うことです。RubyではHashクラスから作ること...
Hashオブジェクトの各要素は、普通の配列と同じように、1個1...
持っています。ただ、Hashの個々の要素にアクセスするには
インデックスではなくキー(key)を使います。このキーには、Ru...
((つまりオブジェクト))ならなんでも用いることができます。
上のようなテーブルを表現するのに、人名を表すStringオフジ...
キーにして、合言葉のStringオブジェクトを格納するには次の...
します。
words = {
"Hiroyuki" => "umai-bou",
"Boom" => "fugashi",
"Yaruo" => "onani",
}
カンマで区切られたリストのそれぞれの、「=>」で区切られ...
Hashのキー1個とそれに対応するオブジェクトを表しています。
この代入文が、継続文字のたぐいを使わずに、数行にまたがっ...
注目しましょう。このような書き方ができるのは、Rubyプログ...
一般の空白文字は意味を持たないからです。
Boomの合言葉を取り出すには、Boomをキーとして、
words["Boom"]といった式によってHashオブジェクトwordsの要...
アクセスする必要があります。先程の配列と同じように、この...
得られる値はfugashiになります。
また、普通の配列(Array)と同様に、キーには任意の式を用い...
personに"Boom"をセットしてから、words[person]を評価すると、
同様にfugashiが得られます。
これらを1つにまとめると次のプログラムが得られます。
#!/usr/bin/ruby
words = {
"Hiroyuki" => "umai-bou",
"Boom" => "fugashi",
"Yaruo" => "onani",
}
print "名前は? "
name = STDIN.gets
name.chop!
if name == "Vipper"
print "Hello, Vipper! よく来たな。\n"
else
print "Hello, #{name}!\n" #普通の挨拶
secretword = words[name] #合言葉を得る
print "合言葉は? "
guess = STDIN.gets
guess.chop!
while guess != secretword
print "ちょwwおまwwww違うwwwww 合言葉は? "
guess = STDIN.gets
guess.chop!
end
end
合言葉を調べる部分に注目しましょう。合言葉が見つからな...
secretwordの値はnilという値になります。なのでnilであるかを
チェックすれば、その他全員に対するデフォルトの合言葉を設...
できます。この処理は次のようになります。
[... プログラムの前の部分を省略 ...]
secretword = words[name] #合言葉を得る
if secretword == nil #おや、見つからない
secretword = "fushianasan" #いいかお前ら、絶対に名...
end
print "合言葉は? "
[... プログラムの残りの部分を省略 ...]
** 7. いろいろな書き方を受け付けるようにする [#o35677d4]
もし、ユーザがVipperの代わりにvipperやVipper Yaruoなど...
その他大勢として扱われてしまうでしょう。なぜなら、==によ...
一致しているかどうかを調べるからです。
これをうまく扱う方法の1つを紹介しましょう。
Vipperそのものの代わりに、Vipperで始まる任意の文字列を...
話を進めましょう。sedやawkやgrepでは、正規表現(regular ex...
これを実現することができます。
正規表現とは、マッチする(一致する)文字列の集合を定義する...
Vipperで始まる任意の文字列にマッチするRubyの正規表現は、s...
同様に、^Vipperと
なります((ほんとはRuby的には\AVipperのほうがジャスティス)...
この正規表現が、nameに入っている文字列とマッチするかを...
マッチ用の演算子を使って次のようにします:
if name =~ /^Vipper/
## はい、マッチしました
else
## いいえ、マッチしませんでした
end
正規表現の前後をスラッシュで囲むことに注意しましょう。...
部分では、文字列の場合と同様に、スペースやその他の空白文...
これでほぼ完璧なのですが、あとvipperを受け付けることと、
Vipperlなどをはねのけることが残されています。vipperを受け...
スラッシュの直後に、''大文字と小文字を区別しない''ことを...
置きます。
Vipperlをはねのけるには、(viやいくつかのバージョンのgrepと
同様な)''単語の境界''を表す特別なマーカー\bを追加します。
これによって、正規表現中のrの直後には、英文字がこないこと...
結局、正規表現は/^vipper\b/iとなりますが、これは「文字列の
先頭((ほんとは「行の先頭」))にvipperがあり、その直後に英...
きてはならない。また、大文字小文字のどちらでもOK」という...
これをプログラムの残りの部分と合わせると、次のようにな...
#!/usr/bin/ruby
words = {
"Hiroyuki" => "umai-bou",
"Boom" => "fugashi",
"Yaruo" => "onani",
}
print "名前は? "
name = STDIN.gets
name.chop!
if name =~ /^vipper\b/i
print "Hello, Vipper! よく来たな。\n"
else
print "Hello, #{name}!\n" #普通の挨拶
secretword = words[name] #合言葉を得る
if secretword == nil #おや、見つからない
secretword = "fushianasan" #いいかお前ら、絶対に名...
end
print "合言葉は? "
guess = STDIN.gets
guess.chop!
while guess != secretword
print "ちょwwおまwwww違うwwwww 合言葉は? "
guess = STDIN.gets
guess.chop!
end
end
ご覧のように、このプログラムを、最初のちっぽけなhello w...
比べると、「よくぞここまで成長したものだ」という感慨にと...
このプログラムは、まだまだ十分に小さく取り扱いも楽な上に...
なかなかの働き者です。これがRuby流のプログラミングなので...
Perlは、UNIXのあらゆる標準ユーティリティ(非標準のものも...
持っている、あらゆる正規表現の機能を提供しています。それ...
Perlの文字列マッチはこの惑星上でほぼ最高速なので、性能は...
…らしいですけど、Rubyではどうなんでしょうね。
1.8系までのRubyの正規表現エンジンは、Emacsでの実装を改良...
したとかいうややこしい歴史を経ていてだいぶブラックボック...
もっと早くしてよーとねだっても難しいかも。
とりあえずPerl5互換なので機能は十分でしょう。
** 8. その他大勢も公平に扱う [#zc00710f]
さて、これでVipperやvipperやVipper Yaruoなどと入力して...
が、Vipper以外のユーザに関してはどうでしょうか? Yaruoは相...
タイプしなければなりません(yaruoの後ろにスペース1つ置くこ...
yaruoに対しても公平であるためには、テーブルを引きにいく...
行の最初の単語を取り出して小文字にする処理が必要です。こ...
メソッドを用います。
String#subは正規表現にマッチするものを探して、それを与え...
置き換えます。
String#downcaseは、文字列を小文字にまとめるのに使います。
まずは、String#subです。私たちがしたいのは、nameの内容...
構成しない最初の文字を探して、そこから文字列の末尾までを...
このために必要な正規表現は/\W.*/です―――\Wは単語を構成しな...
(nonword charactor: 英文字、数字、下線以外のもの)を表し、...
行末までの文字すべてを表します。さて、マッチした文字を削...
して、文字列のうち正規表現にマッチした部分を空文字列で置...
name = name.sub(/\W.*/, "")
第一引数にマッチさせる正規表現、第二引数に置き換える文...
String#subの基本的な使い方です。
String#downcaseは名前通り、文字列をまとめて小文字にして
返します((String#trメソッドというものもあるのですが、マイ...
これらを、残りの部分とドッキングすると、次のプログラム...
#!/usr/bin/ruby
words = {
"hiroyuki" => "umai-bou",
"boom" => "fugashi",
"yaruo" => "onani",
}
print "名前は? "
name = STDIN.gets
name.chop!
original_name = name #挨拶で使うためにとっておく
name = name.sub(/\W.*/, "") #最初の単語より後ろの部分を...
name = name.downcase #全てを小文字にしてしまう
if name == "vipper" #今度はこの比較のやり方でおk
print "Hello, Vipper! よく来たな。\n"
else
print "Hello, #{original_name}!\n" #普通の挨拶
secretword = words[name] #合言葉を得る
if secretword == nil #おや、見つからない
secretword = "fushianasan" #いいかお前ら、絶対に名...
end
print "合言葉は? "
guess = STDIN.gets
guess.chop!
while guess != secretword
print "ちょwwおまwwww違うwwwww 合言葉は? "
guess = STDIN.gets
guess.chop!
end
end
前のバージョンでは、Vipperをチェックするのに正規表現を...
ここでは以前使ったような単純な比較に逆戻りしています。な...
新しく追加した置換と変換処理によって、vipper yaruoもVippe...
なってしまうからです。
そしてBoomやBoom kunはともにboomになり、HiroyukiやHiroyuk...
hiroyukiになる、というようにVipper以外の人たちも公平な扱...
なります。
文を2、3個追加するだけで、プログラムはずいぶんユーザフ...
ちょっとキーを叩くだけで、高度な文字列操作を表現できるの...
利点の一つなのです。…と、LLが流行ってきた昨今となってはあ...
でしょうか。
しかし、比較したりテーブルを調べたりするために名前を加...
入力された名前そのものは壊されてしまいます。そこで、加工...
original_nameに保存しておきます。(C言語の識別子と同様に、...
英文字、数字、下線から構成され、長さはほぼ無制限です。)
こうしておけば、後ほど必要なときにoriginal_nameを参照する...
Rubyには、文字列を調べたり、いじったりする手段がたくさ...
Stringクラスのメソッドはもちろん、Regexp(正規表現)オブジ...
関わってきますし、標準添付ライブラリにStringIOやStringSca...
あります。
** 9. もう少しモジュール性を高める [#p4896a8a]
作成中…
ページ名: