データを配列にしまうには

まとまったデータがある場合、1 つの配列にしまうと便利であった。 配列のデータは

配列 = [データ 1, データ 2, データ 3, ....]

または

配列[0] = データ 1 配列[1] = データ 2 配列[2] = データ 3 :

とプログラムに直接書き込むことができた。 配列の中身には文字列または数値を入れることができる。 新しく配列をつくる場合、

配列名 = Array.new

と new method を使うと、配列を使うことを計算機に伝えることができる。 データを読み込ませて取り込む場合は


while 変数 = gets
if /パターン/ =˜ 変数
$1 の処理 $2 の処理 :
end :
end

となり、実行は次のように行った。

%./プログラム.rb データ.dat

データは一行ずつ読まれ、一行ずつ分解される。

配列を扱うときのまとめ

まとまったデータを処理するには、method を使うことができた。 配列の持つデータの数を知るには

配列.size または 配列.length

配列の要素を小さい順に並べ替えるには

配列.sort

で、数値の小さい順、あるいは文字コードの小さい順に並べ替えることができ た。配列の要素を左から右に並べ替えるには

配列.reverse

とすればよかった。method なので、これらを組み合わせることも可能である。 大きな順に並び替えるには

配列.sort.reverse

とすればよい。

うろ覚えの検索

Google などの検索エンジンでは、 適当なキーワードを入れてもそこそこヒットするページがある。 あるいは携帯電話で、数文字入れると候補が続々と出てくる。 その仕組みを学ぼう。

正規表現

検索に使われている仕組みは、 あいまいな文字列も取り出すことができるようになっている。 あいまいな文字列を取り出す仕組みを、正規表現という。 検索に使用されている文字 (characters) を meta characters と呼ぶ。 meta characters として使用されている文字は、 日常あまり使われない記号文字が使用されている。

何か 1 文字を探したい

アルファベットはヨーロッパでよく使われるが、 とくに地名や食べ物などは、 言語によって綴りが少しずつ違うことが多い。 また、日本人を含めたアジア系の人間は、 記憶に残りやすいように綴りを変えて論文を投稿したりすることもある。 Google などの検索エンジンでは、積極的に似たような綴りの候補を挙げてくる。

.(period) 任意の一文字の候補を取りだす。

yama.aHIT yamaba, yamada, yamaha, yamata, yamasa, yama-a, yama a

? (question mark) 直前の文字が 0 または 1 回出現し、直前の文字は記号も可

o-?kawaHIT okawa, o-kawa, ookawa, no-kawa, nokawa
OUTohkawa, oikawa

何回か繰り返されている文字を探したい

フランス語やドイツ語では、英語のアルファベットしかないときに、 特殊記号をアルファベット 2 文字を使って代用することがある。 しかしその規則は、ドイツ語にかなり似ており、 フランス語から多く語彙を持ち込んだはずのスウェーデン語にはない。 この規則を知らないドイツ人やフランス人が、 コンピュータを使用してスウェーデン旅行の情報を得ようとするかもしれない。 またオランダ語では、繰り返して綴る発音が多いが、発音がかなり英語に近いため、 オランダ語で探すべきものを英語風の綴りで検索してしまうかもしれない。 そのような検索でもヒットするには、次のようにする。

* (asterisk) 出現は何回でもよい場合

stras*eHIT strae, strase, strasse, straek, strassen
+ (plus) 直前の文字が出現しなければならず、その出現は何回でもよい場合

ec+oHIT eco, ecco, ecology, economy, deco, record, pecco

先頭あるいは末尾が分かっている場合

^ (caret) 先頭の文字列のみ探して調べる

^citaHIT cita, citation
OUTpaque de la cita, cinecita

$ (dollar) 末尾の文字列のみ探して調べる

casa$HIT le bois de la casa, micasa
OUTacasaca, casa blanca

いくつかある候補を探したい場合

| (vertical line またはパイプ) 「または」の意味。

milan|milano|miland HIT milan, milano, miland
OUTmiran, milani, melan, mirano
geneve|geneeve|geneva|genf HIT geneve, geneeve, geneva, genf
OUTgenevois, genoa, genova, junebu

( ) (parenthesis) ( ) 内で括られた条件を先に探す。

(pari)s は、pari という候補を先に検索したのち、 その後に s がついているかどうか調べる。paris, camparis が検索される。

[ ] (braket) [ ]で括られた文字のどれかが一致するものを取りだす。

tok[yi]oHIT tokyo, tokio

どのようにして 1 文字を検索するのだろうか推測せよ。 「文字コード」をキーワードにし、解答せよ。

1 文字違い

1 文字を除いて全て綴りが分かる場合を検索する場合について述べる。

[文字1-文字2](braket) (文字1)から(文字2)の範囲の 1 文字を全て取りだす。
[a-z]小文字全ての中から 1 文字
[0-9]数字の 0 から 9 までの 1 文字
[ぁ-ん]ひらがなの中から 1 文字
[亜-腕]常用漢字の中から 1 文字

電話番号の 1 文字だけが分からない、クロスワードの 1 文字を辞書を引いて探す、 などの場合に使える。

小さいあ「ぁ」は la と打つ。

[^(文字1)-(文字2)] (文字1)-(文字2)の範囲以外の 1 文字を取り出す。

[^a-z] ならば小文字以外の文字列のどれか、という意味である。 大文字、数字、記号のいずれかが当てはまった場合に検索に引っかかる。

文字の種類を判別して探す

meta charcter の pattern matching は Ruby 言語に仕込んで使うことができる。 まずは

\s 空白を探し出す

Space, Tab, 改行は空白文字と見なされる。空白文字の全てを探し出す。

\S空白以外を探し出す

空白文字以外全てを探し出す。

\d [0-9] のこと。

数字が含まれているものは全て探し出される。

\D [^0-9] のこと。

数字以外の文字全てが探し出される。

データからコマンドを使って検索するには

以下のデータファイル station.dat をクリックしてセーブする。 これは、Emacs に駅名、所在地、ひらがな、アルファベットの情報を、 空白を空けながら入力したものである。 駅名から時刻表を検索するときなど、 数文字入れると候補が出現する機能があるものがある。 このしくみをこのデータを使って学んでみよう。

ファイルの中の文字列を調べよう

ファイルの中の文字列を調べるには egrep を使う。酒田と名のつく駅を調べたい。

%egrep "sakata" station.dat
象潟秋田県きさかたKisakata
東酒田山形県ひがしさかたHigashisakata

と出る。しかし酒田駅も存在するので、この方法では不十分である。 オプション -i をつけると、小文字を区別しない。

%egrep -i "sakata" station.dat
酒田山形県さかたSakata
象潟秋田県きさかたKisakata
東酒田山形県ひがしさかたHigashisakata

となる。オプションをつけたことにより、大文字小文字を区別しなくなった。

egrep (-オプション) "文字列" データ

携帯電話の検索はどの meta character を使っているだろうか。 ウェブの検索エンジンではどうだろうか。

正規表現の例

正規表現を使って、データを調べてみよう。

1 文字違いを探したいとき

大石田 (おおいしだ) という地名を検索したい。 ローマ字綴りで oh なのか、o なのか、あるいは shi なのか si なのか、 よくわからない。 このようなときに ? を使う。直前の 1 文字があってもなくてもよい、 というものである。

%egrep "Oh?ish?ida" station.dat
大石田山形県おおいしだOishida

1 文字だけ読みが分らないとき

地元の住民でなければ、地名の読みが分らないことがある。 例えば「遊佐」に対して、 yuza なのか yusa なのか分からないことがある。

%egrep "Yu[sz]a" station.dat
遊佐山形県ゆざYuza

このデータを使って、他の meta character の例を使って、地名検索を試みよ。 予想、実行結果、その説明を答えよ。予想外の実行結果が出たときには、 それはなぜなのか理由をつけよ。

日本語で探すには

日本語で探す方法を知る前に、日本語の漢字コードについて学ぶ。

漢字コード変換

Windows と UNIX は漢字コードが異なる。 自宅で作成した正しいプログラムも漢字コードが異なるため動かなくなる。 UNIX では、作ったデータのコードは EUC で作られている。 Emacs のミニバッファの上のバーの左端に、 [-E:] あるいは [-AあE:] があることを確認する。もしもこれ以外の文字がある場合は、 漢字コードを変換する。 Ctrl-x Ctrl-m f を押し、


Coding System for saving file (default, nil):

と表示されるので


Coding System for saving file (default, nil): euc-jp

とする。すると、ミニバッファの上のバーの左端に、 [-E:] あるいは [-AあE:] と表示される。

学内で作成していても、操作ミスで漢字コードを変換してしまい、 プログラムが動かないことがある。この場合も同じ方法を使って EUC に変換する。

Emacs や Firefox は utf-8 を使っており、ときどき UNIX 内でも漢字コードの問題が発生する。

日本語で検索

Kterm や Firefox 上で日本語入力を行うには、Kinput2 を使う。Kinput2 の ON/OFF の切り替えは Ctrl-o であった。

%egrep "さかた" station.dat

と入力すると日本語検索ができることを確かめよう。

Windows で使われる漢字コードは何か調べよ。 EUC の漢字コードのデータを探す egrep のオプションが egrep -e であるとき、どのようなオプションとなるか予想し、調べた結果を記せ。

ひらがなを検索するには

日本語である範囲にある文字を検索したいときについて学ぶ。

ひらがなを探す

ひらがなを探すには、限定する [ - ] と Kinput2 を組み合わせる。 例えば、駅名がひらがなから始まるものを調べるときには次のように行う。

%egrep "^[ぁ-ん]" station.dat
かみのやま温泉山形県かみのやまおんせんKaminoyamaonsen
さくらんぼ東根山形県さくらんぼひがしねSakuranbohigashine
あつみ温泉山形県あつみおんせんAtsumionsen

小さいあ 「ぁ」 は la とすると入力することができる。

漢字を探す

常用漢字を探すには

%egrep "[亜-腕]" station.dat

とする。特殊な漢字(常用漢字でないもの)に関しては、文字コードの知識が必要である。

漢字を含んだ行たちだけを取り出すことができるようなデータの列を考え、 実行せよ。

行頭や行末で調べたいとき

陸羽西線 のデータで調べてみよう。

%egrep "0$" rikuusaisen.dat
新庄駅0.0
古口駅17.0
余目駅43.0

データの末尾は新庄からの走行距離数を表す数字である。 そのうち、0 で終わるデータ行を取り出した。

%egrep "^新" rikuusaisen.dat
新庄駅0.0

データの行頭は漢字である。「新」で始まるデータ行を取り出した。

この切り出しかたでは、駅名の 2 つ目の文字が新であるものや、 データの整数値部分に 3 があるデータだけを切り出すということができない。

練習問題

次のような場合、データ station.dat からどのようにデータを切り出せばよいだろうか。

駅名にひらがなを含む駅名を探したい。どうすればよいか。 「かた」あるいは「がた」で終わる駅名を知りたい。どうすればよいか。 外国人旅行者が「さわや」で終わる地名の寺を訪れたいという。 この発音は正確ではないかもしれない。どうやって探せばよいか。 「庄」あるいは「荘」の漢字が含まれる地名だがはっきりしない。どうやって探せばよいか。

正解はいくつか考えられるが、 解答例 を示しておく。

練習問題解答例

解答例は次の通り。あっているかどうか知りたければ、個別に質問するとよい。


/^[ぁ-ん]/
/(か|が)た\S+/
/a.a.a$/ 
/[荘|庄]/

他にも方法はある。

実行結果を出力し、検索される理由を記せ。 別の解答を見つけた場合、それも出力し、検索される理由を記せ。

コマンドライン入力で結果を得る

プログラムの実行と同時に値を入力することをコマンドライン入力と呼ぶ。 egrep では 2 つの文字列を指定した。

egrep "探したい文字列" "探すデータ格納先"

コマンドのあとの文字列は引数 (ARGument Value)と呼ばれる。 コマンドを実行するとき、コマンドの後ろに ARGV 配列があり、 ARGV 配列に入ったものを処理する部分がコマンドにあれば、処理をする。

今まで学んだコマンドで後ろに引数を取るものについて同じように分類してみよう。 引数を取らなくてもよいコマンドで、わざわざ引数を適当につけるとどうなるか、 見てみよう。

コマンドライン入力を行うプログラムを実際に作ってみよう。 プログラム名を argv_ticket.rb とする。

%./argv_ticket.rb出発地 当着地 特急券の種類 支払い方法

と一度に入力する。これらの情報を元に乗車券や特急券が発券される。

コマンドライン入力で行われていること

計算機はこの情報を配列 ARGV に格納する。

ARGV
ARGV[0] ARGV[1] ARGV[2] ARGV[3]
"新庄" "余目" "自由席" "現金"

実際は、指定席の場合は座席指定などさらなる情報も必要ではあるが、 とりあえず実際に動くかどうか調べてみよう。

特急券発券

コマンドライン入力を使うプログラムの例を見てみよう。 argv_ticket.rb


#!/usr/koeki/bin/ruby
#coding: euc-jp

if ARGV[0] == nil 
STDERR.print("入力に失敗しました\n") STDERR.print("出発地 目的地 特急券の種類 支払い方法 の順に入力\n") exit(1)
end arvl = ARGV[0] dept = ARGV[1] cls = ARGV[2] paym = ARGV[3] printf("発券情報確認\n") printf("券別: %-6s \t \t 支払い: %-6s\n" , cls, paym) printf("出発地: %-6s \t --- \t %-6s\n", dept, arvl)

というプログラムを作っておき、

%./argv_ticket.rb 新庄 余目 自由席 現金

と入力すると、


---- 発券情報確認 ---- 
券別: 自由席     支払い: 現金  
出発地: 余目     ---     新庄  

という出力を得る。問題はファイル出力や印刷出力を行うときに発生する。

操作間違いをユーザに知らせたい場合

STDERR.print という実行文の場合、ユーザが誤った操作をした場合に、 ユーザの使用している画面に標準出力される。例えば、 コマンドライン入力しなければならないプログラムに対して、 入力情報を忘れたユーザに対しては、 プログラムの冒頭にエラーメッセージを伝え、 プログラムを強制終了させるなどの工夫ができる。


if ARGV[0] == nil
  STDERR.print("入力に失敗しました\n")
  STDERR.print("出発地 目的地 特急券の種類 支払い方法 の順に入力\n")
  exit(1)
end

まず配列 ARGV に入っている要素 ARGV[0]nil (無効) になっているかどうか調べ、 STDERR.print で、エラーメッセージをユーザ画面に出力させ、 正しい使いかたを誘導している。

なぜ標準エラー出力 (STDERR) を使うのか

操作間違いは、空欄ばかりではない。 例えば、整数値を計算させたいプログラムに対しては、 文字列が入ってしまった場合も操作間違いである。 あるいは改行のみ入力された場合、 負の数や小数点などが入力された場合も操作間違いである。

プログラムの計算結果を他のファイルに打ち出すような場合、 この STDERRprint と結合していることで、エラーメッセージはファイルに行かず、 ユーザの目にふれる。 例えば窓口業務の場合、特急券を印刷して手渡すが、 このときに印刷の実行するしくみは次のようであった。

% ./argv_ticket.rb > confirm.txt

STDERR なしではエラーメッセージが confirm.txt に書き出され、発券(券に印字されてしまう)まで気づかない。 標準エラー出力を print 文 につけておく理由は、 利用者が操作間違いをしたことが分かる ようにするためである。

操作間違いが起こったので強制終了させたい

Ruby プログラムが思いどおりに実行されたかどうか、 計算機に教えることができる。これを status という。

思いどおりに実行されること 0
何らかのエラー 0 以外の整数

で表す約束になっている。ここではプログラムが強制終了する exit が用いられている。このプログラムでは、 操作間違いを起された場合には、エラーであることを教え (使い方を示し)、強制終了しているのである。 exit(1) で、何らかのエラーによって終了した、という意味である。

exit(1) 異常なときに終了せよ (0 以外の整数であればよい)
exit(0) 正常なときに終了せよ

日常生活において、何かが終了する場合を、 exit(0), exit(1) で分類せよ。

数値をコマンドライン入力する場合

コマンドライン入力でも数値を入力することができる。

変数 = ARGV[要素番号].to_i

などとするとよい。

コマンドライン入力で数値を処理するようなプログラムを作って確かめてみよう。