解説 9月(2)

今回は前回に引続き基本知識の確認と復習を行います。

前回の解説資料 を参照して、基本知識の確認と復習を行ってください。
すでに終了した方は、以下にあげる追加課題にチャレンジしてみてください。

今回は、誰かと組んで、二人で協力して行っても構いません。その場合は、 報告は二人の名前と学籍番号も明記して提出してください。
ただし、3人以上でのグループ作業は推奨しません。


追加課題

以下の課題をLinuxにログインして行いなさい。 ファイルの作成・編集や、プログラムの実行など、できる限りLinux上で行うこと。 今後のために、今からLinux環境での作業に慣れておく必要があります。
Linux環境ではファイルがEUC-JPという文字コードで保存されます。 pを使って日本語を表示するときは、ファイルの冒頭に $KCODE = "e" と書いておきましょう。

課題1-1

次のファイルに書かれている「いぬ」や「ねこ」の数を数えるプログラム をハッシュを用いて作りなさい。またそのプログラムがどのような手順で作られているかを説明しなさい。 (いぬやねこ以外のものがあった場合も対処できるようにすること)
なお、9月(1)の課題1-3-3をハッシュを使って実現してある場合は、 実質的に同じ課題ですから、ここでは 「プログラムの手順についての説明」を書くだけで結構です。

inu_neko.txt

上のファイルの場合、次のような結果になります。

いぬは全部で6匹います
ねこは全部で9匹います
わには全部で1匹います
...(以下省略)...

動物を表示する順序は同じでなくてもかまいません。正しくカウントできていればよい。

[プログラムを作るための手順書の例]

これはファイル中の「いぬ」「ねこ」の個数を数えるプログラムの手順の 解説書の例です。見て分かるように、「いぬ」の出現数と「ねこ」の出現数を 数えるための変数を使っています。ハッシュを使ったプログラムを 書いた場合は、そのように改変する必要があります。


I. 使う情報とその表現形式

ファイル:ファイルの中に、「いぬ」や「ねこ」が文字列として表現されている。

II.手順

概略

1 ファイルから「いぬ」や「ねこ」の入っている文字列を取り出し配列に格納する。
2 配列のなかから文字列を一つづつ取り出し、その中にある「いぬ」か「ねこ」の数を数えることを繰り返す。
3 数えた結果を表示する。

詳細版

1 ファイルから「いぬ」や「ねこ」の入っている文字列を取り出し配列に格納する。
  1.1  ファイルを開く。
  1.2  ファイルから「いぬ」や「ねこ」などの文字を全部読み込んで配列に格納する。
  1.3  ファイルを閉じる。

2 配列のなかから文字列を一つづつ取り出し、その中にある「いぬ」か「ねこ」の数を数えることを繰り返す。

 2.1 「いぬ」をカウントするための変数と「ねこ」をカウントするための変数を用意しておく。
  2.2  配列の中の要素を順番に一つずつ取り出す。
       (全部取り出したときは3.へ進む。)
  2.3 ねこかいぬか判断しカウントする。 
    2.3.1 取り出したものが「いぬ」かどうか判断する
    2.3.2「いぬ」の場合、犬の変数の数を1増やす。
    2.3.3 取り出したものが「ねこ」かどうか判断する
    2.4.1 ねこ」の場合、ねこの変数の数1増やす。
  2.6  2.2へ戻る。

3.  「いぬ」の数と「ねこ」の数を表示する。

課題1-2

次のファイルに現れた単語とその出現数をすべて表示する プログラムを作りなさい。またそのプログラムがどのような手順で作られているかを説明 しなさい。

black_cat.txt
これは英語の文章のファイルです。英語では単語間には、スペース、 コンマ、ピリオドのいずれかが入って区切られています。

参考: これを「わかち書き」と言います。それに対し日本語の普通の文章は わかち書きされていませんので、時々(特に漢字かな混じりで書かれていない 場合は)、文が読み取りにくい場合があります。
例:「ウラニワニワニワニワニモニワニワトリガイル」、「すもももももももものうち」

閑話休題
ここでは、同じ名詞でも単数形と複数形が違えば「異なる」単語とみなすこと とします。また同様に、同じ動詞でも原形、3人称単数形、過去形、過去分子 形など、形が違えば「異なる」単語とみなすこととします。
したがって、catcatscat'sはみな違う単語と考え ます。また、beamwasarewere もそれぞれ違う単語と考えます。ただし大文字と小文字は区別しないものとします。つまり、CatcatCATも同じ単語とします。

上のファイルの場合、次のような結果になります。

me は全部で 2 個ありました
prevent は全部で 1 個ありました
it は全部で 2 個ありました
observing は全部で 1 個ありました
kind は全部で 1 個ありました
...(以下省略)...

[大文字と小文字を区別しないためには]

CatcatCATも同じ単語とみなすためには、 みんな「すべて大文字」のCATとみなすか、「すべて小文字」のcat とみなせばよいのです。

Rubyには文字列に対してアルファベットをすべて大文字にするメソッドとしてupcase、すべて小文字にするメソッドとしてdowncaseが用意されています。この働きは次の例を試してみてください。

str = "AbcDEfg"

p str.downcase
p str.upcase
これを利用して、読み込んだ文字列をすべて大文字か小文字にしてしまえば、この問題は解決できるでしょう。

[文字列を単語に分ける]
"I married early, and was happy to find in my wife a disposition not"
という文字列に対してsplitして単語に分けようとすると、
 ["I", "married", "early,", "and", ...]
となって、コンマが残ったままになってしまいます。これはコンマだけではなく、 ピリオドの場合も同じです。

この問題では、スペース、改行、コンマ、ピリオドのところで文字列を区切る必要があります。それにはsplitを使うやり方とscanを使う方法があります。

ただどちらの方法でも、引数が必要で、引数の意味は、この講義の後の方で予定している 「正規表現」の知識が必要です。ここでは、次の例から使い方を学んでください。

str = "what you want is, as you see, what you've never got it. so forget it."

p str.split(/[,. \n]/)
p str.scan(/[\w']+/)

[手順のヒント]

これはファイル中の「いぬ」「ねこ」の個数を数えるプログラム の応用です。ハッシュを使って、ファイル中に現れる単語とその出現数を 記録しましょう。

I. 使う情報とその表現形式

ファイル:ファイルの中に、英語の単語が「わかち書き」されて文字列として現れている。

II.手順

概略

1 ファイルから一文ずつ文字列を取り出し配列に格納する。
2 配列のなかから文字列を一つづつ取り出し、単語に切り分ける。
  単語に切り分けた結果の配列から単語を一つずつ取り出し、ハッシュに登録されていなければ出現数1として、登録されていれば出現数に+1しておく。
3 数えた結果を表示する。

詳細版

1. ファイルから一文ずつ文字列を取り出し配列に格納する。
  1.1  ファイルを開く。
  1.2  ファイルから一行ずつ読み込み文字列として配列に格納する。
  1.3  ファイルを閉じる。

2. 配列のなかから文字列を一つづつ取り出し、単語に切り分ける。

 2.1  単語登録のためのハッシュ(ここではその名前を「単語帳」とおく)を作っておく。
  2.2  配列から文字列をひとつずつ取り出す(繰り返し文を使う)
       (全部取り出した終わったときは3.へ進む。)
  2.3  文字列に対しsplitを使って単語に切り分ける。splitを使った結果は、単語を表す文字列を要素とする配列となる。
  2.4  単語に切り分けた結果の配列から単語を一つずつ取り出し、ハッシュに登録されていなければ出現数1として、登録されていれば出現数に+1しておく。
     2.4.1 単語を表す文字列を要素とする配列から、単語を一つずつ取り出す
       (全部取り出した終わったときは2.5.へ進む。)
     2.4.2 単語帳からその単語が登録されているかどうかを探索する
     2.4.3 登録されていなければ、その単語をキーとし、値を1としてハッシュに登録する
     2.4.4  登録されていれば、「その値に1を足した結果」を値として登録し直す
     2.4.5 2.4へ戻る。
  2.5  2.2へ戻る。
3. ハッシュ「単語帳」のキーと値を表示する

課題1-3

次のファイルに現れた単語とその出現数をすべて表示する プログラムを作りなさい。ただし、課題1-2と異なり、この課題では、出現数の多い単語 から順に表示するものとします。

black_cat.txt
課題1-2ができていれば、ファイル中の単語と出現数をハッシュに記録するところまではできています。あとは、出現数の多いものから降順にソート(並べ替え)すればよいのです。

[ハッシュの中身をソートする]

ハッシュのkeyでソートするのはsortという組み込みメソッドを使えば簡単です。
data = {"JPN" => 1.2, "USA" => 2.5, "CHN" => 8.5}
p data.sort
[["CHN", 8.5], ["JPN", 1.2], ["USA", 2.5]]
このように、ハッシュのkeyの昇順でソートされた「配列」ができます。

しかし、値でソートするには一筋縄では行きません。 次を試してみてください。

data = {"JPN" => 1.2, "USA" => 2.5, "CHN" => 8.5}
adata = data.sort { |a,b|
	  a[1] <=> b[1] }
p adata
bdata = data.sort { |a,b|
	  b[1] <=> a[1] }
p bdata
その結果は次のようになります。どちらも配列が できますが、最初の実行では値の昇順で、 次の実行では値の降順でソートされていることが分かります。
[["JPN", 1.2], ["USA", 2.5], ["CHN", 8.5]] # adata
[["CHN", 8.5], ["USA", 2.5], ["JPN", 1.2]] # bdata
この仕組みについてはここでは解説しません。是非、解説書やマニュアルを参 照してみてください。