(9)06/15の授業内容

  1. 正規表現のパターンをキーボードから入力
  2. 正規表現を作るためには,正規表現として扱いたいパターンを//でくくって示します

    /パターン/

    この方法はプログラムの中に直接記述することができるので,特定のパターンのみを検索するプログラムであれば,一番簡単な方法になります.しかし,前回行った課題のように任意のパターンにマッチする文字列を検索するというような場合は,毎回プログラムの内容を書き換えなければならないため面倒です.

    今回はキーボードから正規表現のパターンを入力できるようにプログラムを変更します.そのためには,もう1つの正規表現の指定方法をまず把握する必要があります

    a= Regexp.new("Ruby")

    これはRubyを正規表現のパターンとし,それのパターンをaに代入するという表記方法です.これをまず確認してみよう

    #!/usr/koeki/bin/ruby
    
    a = Regexp.new("Ruby")
    p a

    このプログラムをreg.rbという名前をつけて保存し,実行してみよう.pは配列の格納状況を調べることができるメソッドと説明しましたが,正規表現のパターンがどのようになっているかを調べることもできます.このプログラムを実行すると

    /Ruby/

    と表示されることが確認できましたか? つまりRegexp.new("Ruby")は()内を正規表現のパターンに変更してくれます.ここではRubyというように具体的なパターンを入力していますが,具体的なパターンを入力するかわりに変数を入れることもできます.つまり,キーボードから入力したパターンを読み込み,適当な変数に代入し,ここでRubyとする代わりにその変数名を指定すれば,キーボードからの入力によりパターンを自由に指定することが可能になります.この考えに基づき,前回のプログラムを変更してみましょう(黄色が変更箇所です)

    #!/usr/koeki/bin/ruby
    
    STDERR.print "検索パターンは?:"
    reg = STDIN.gets.chomp!
    expression = Regexp.new(reg, true, "e")
    
    while line=gets
      if expression =~ line
        print line
      end
    end

    続いてプログラムの変更点について解説します

    1. reg = STDIN.gets.chomp!
    2. キーボードから入力された値を改行をとりregという変数に代入します.文字列のままでよいので.to_iはつけません.STDINがついていますが,これは標準入力という意味です.データを読み込む場合,キーボード入力の読み込みと,既存のファイルからの読み込みを考えることができます.標準的にはキーボードの入力が使用されます(標準入力といいます).今回のプログラムでは検索するリストを外部ファイルから読み込み,かつ検索パターンをキーボードから読み込みます.つまり,入力元が2種類あります.Rubyプログラムを実行する場合にファイルを指定すると,getsはそのファイルを読み込んでしまうため,この行が読み込む入力元はファイルではなく,標準入力であるキーボードから読み込むことをあらわすSTDINを入れる必要があります

    3. expression = Regexp.new(reg, true, "e")
    4. 先ほどは()の中にRubyという文字列を入れていましたが,ここではregという変数を入れています.reg以外にtrueやeがありますが,これらの意味は下記のとおりです

      第2引数

      true:大文字と小文字を区別してほしくない場合./パターン/iと同じ意味

      false:大文字と小文字を区別してほしい場合


      第3引数(日本語の検索をしたいときに指定)

      "e":EUCコードで書かれているものとして照合./パターン/eと同じ意味

      "s":Shift-JISコードで書かれているものとして照合./パターン/sと同じ意味


      101/102教室で英数字を検索する場合

      Regexp.new(変数, true)

      Regexp.new(変数, false)

      101/102教室で日本語を検索する場合

      Regexp.new(変数, true, "e")

      Regexp.new(変数, false, "e")

    5. if expression =~ line
    6. /sai?to/iと決め打ちをしていたのが,expressionという変数に置き換えられています.オプションのiは2つ上の行で指定しているのでここでは指定しません

    練習

    このプログラムをkensaku2.rbという名前をつけて保存し,先週の課題で作成したリストを用いて自由に検索をしてみよう

  3. 正規表現の指定方法(追加)
  4. [ぁ-ん]:ひらがな全部.「ぁ」は小文字(laもしくはxaと入力)

    [亜-腕]:漢字全部(ただし第一水準のみ)

  5. 正規表現を用いた成績処理
  6. 下記のような成績リストを考えてみよう.左が英語で右が国語のテストの得点とする.ここから各テストの平均点を算出してみよう

    まず,下記の成績リストをscore.txtとして保存する

    丘本秋子		45		60
    高梁缶		52		34
    仁科牧子 		80		45
    藤嶌楓		71		67
    伊井利郎		66		50
    木之元週一 	90		82
    多村数馬 		49		56
    砂糖真琴		55		70
    藤嶋丸子		82		68
    嶋田多香子	74		49
    齋藤由		63		56
    高橋麹		92		71
    渡部すず		59		48
    渡辺勇次		69		77
    藤島真希		70		65
    

    続いて以下のプログラムをscore.rbとして保存する

    #!/usr/koeki/bin/ruby
    
    #初期設定
    
    name=[]   #名前を代入する配列
    eng=[]    #英語の得点を代入する配列
    jap=[]    #国語の得点を代入する配列
    
    n=0    #配列に値を代入する際に使用するインデックス
    sum_eng=0    #英語の合計得点
    sum_jap=0    #国語の合計得点
    
    #成績の読み込みと合計得点の算出
    
    while line = gets
         if /(\S+)\s+(\d+)\s+(\d+)/ =~ line
    	 name[n] = $1
    	 eng[n] = $2.to_i
    	 jap[n] = $3.to_i
    	 sum_eng += eng[n]
    	 sum_jap += jap[n]
    	 n += 1
         end
    end
    
    #平均値の算出と表示
    
    av_eng = sum_eng/n
    av_jap = sum_jap/n
    printf "英語の平均は%3.1f点,国語の平均は%3.1f点です\n", av_eng, av_jap
    print "\n"
    
    #個人個人の得点の表示
    
    i=0
    print "-- 氏名 -------- 英語 -- 国語 \n"
    print "\n"
    while i < n
      printf "%-10s \t %3d \t %3d\n", name[i],eng[i],jap[i]
      i += 1
    end
    
    

    このプログラムで新しく出てきたのは下記の2行です

    1. if /(\S+)\s+(\d+)\s+(\d+)/ =~ line
    2. \S:空白以外にマッチ

      \s:空白にマッチ

      \d:0から9までの数字とマッチ

      +:1回以上の繰り返しにマッチ

      ():*+?と組み合わせて繰り返しで使うと前回は説明しましたが,ここでは後方参照のために利用しています

      後方参照とは

      後方参照というのは,正規表現でマッチした部分の一部を取り出す方法です.正規表現の中の()でくくられた部分にマッチした文字列を,()の順番に応じて$1,$2といった$数字の形の変数で取り出すことができます

      #!/usr/koeki/bin/ruby
      
      /(.)(.)(.)/ =~ xyz
      first = $1
      second = $2
      third = $3
      
      print first,"\n"     #=> x
      print second,"\n" #=> y
      print third,"\n"    #=> z
      

    3. name[n] = $1, eng[n] = $2.to_i, jap[n] = $3.to_i
    4. $1,$2,$3は上の行の正規表現の3つの()に対応しています(後方参照).1つ目は(\S+)ですから,空白以外の文字の1回以上の繰り返しを表し,ここでは氏名に該当します.そして氏名を文字列として配列nameに代入しています.nameのインデックスは初期値が0のnとし,whileの繰り返しの中でn+=1で値を1ずつ増加しています.

      1つ目と2つ目の間の\s+は空白の1回以上の繰り返しで,その後の(\d+)は数字の1回以上の繰り返しです.これは英語の得点に該当します.英語の得点は.to_iメソッドを使って文字列から整数に変換され,engという配列に代入されます.同様に$3は国語の得点を表し,japという配列に代入されます

課題

score.rbを改良する.改良の方法は下記の1〜3であり,これらのうち,いずれかを選んで実施する.改良したプログラムおよびその説明,実行結果についてレポートする.なお,改良を行ううえで,1がもっとも易しく,3がもっとも難易度が高い.このため,1を選んだ場合は8点満点,2は9点満点,3は10点満点で採点する

  1. プログラムの最後の個人得点の 表示において,得点のみでなく平均値との差を表示するように改良する. この際の並び順は,英語の得点,平均との差,国語の得点,平均との差と する.この課題では下から3行目のprintfの行のみ書き変えればよい
  2. score.txtの英語,国語の得点の横に数学の得点を入れ(値は適当でよい),3つの課題の得点をもとに,上記1と同様の改良を行う.つまり,プログラムの最後の個人得点の表示において,英語の得点,平均との差,国語の得点,平均との差,数学の得点,平均との差の6つを表示するよう変更する.この課題では正規表現のパターンの変更,数学の個人得点,合計得点,平均用の変数が必要になる
  3. 上記1と同様に,最後の個人得点の表示において平均値の差を表示するのに加え,偏差値も表示するようプログラムを改良する.結果表示の際の並び順は,英語の得点,平均との差,偏差値,国語の得点,平均との差,偏差値とする.この課題を選んだ場合はscore.txtに数学の得点を追加する必要はない.この課題に関してはヒントは出さない.偏差値の計算方法のみ下記に示す
  4. 偏差値の算出方法(偏差値=(得点-平均)/標準偏差×10+50,標準偏差=√((Σ(得点-平均)^2/人数))

    平方根(ルート)の計算は下記を参考にすること(Math::sqrt(数字)の部分で実際の計算を行っている)

    #!/usr/koeki/bin/ruby
    
    x = 9
    
    a = Math::sqrt(x)
    
    print a,"\n"   #=>3.0と表示される
    

作成したプログラム,実行結果をメールでnaoya@e.koeki-u.ac.jp宛に送る.

課題の提出期限は6月18日(土)17:00まで


メール送信時の注意

Tips

emacsについて

Mewについて