情報科学概論
2000.5.23



  1. 本日の作業内容


  2. Ruby入門

    2.12 制御構造(つづき)

    2.12.4 While文、until文

    ある条件が成立する間処理を実行する-->while
    ある条件が成立しない間処理を実行する-->until


    #!/usr/bin/ruby
    #cat.rb
    
    while line = gets()
      print line
    end
    
    上のスクリプトでは、3行目のwhileから始まる行では、まずgetsを利用してファイルを読み込む。ここで、gets([rs])(p.38、表2-1)はrsという文字列を行末としてコマンドラインから引数として与えられたファイルを一行読み込むという動作をするメソッドである。rsが省略された場合には、組み込み変数である$/で与えられる行末区切り(p.51、表2-2)を参照するが、デフォルトでは\nが割り当てられる。すなわち、改行までが一行と見なされる。

    よって、3行目の意味は、引数で与えられたファイルに読み込むべき行がある間は次行の処理である

    print line

    を行う、である。自分の環境で試してみるには、たとえば、

    $ cd script/
    $ xemacs cat.rb &

    などとしてエディタで上記のスクリプトを入力し保存したら、再び「別名で保存」を選び、適当な名前(例えばfile)をつけて保存する。

    $ chmod +x cat.rb

    のように実行権限を付与し、その後、

    $ ./cat.rb file

    などとして、実際の動作を確認すること。

    例によって、

    /virtual/home/nawate/script/5-23/

    に本日の作業で使用するスクリプトを纏めておくので、必要に応じて参照すること。方法は、

    $ less /virtual/home/nawate/script/5-23/cat.rb

    でKterm上にファイルを表示し、マウスの左ボタンで必要な範囲をドラッグして選択(白黒反転状態)、次にエディタ上でマウスの中ボタンをクリックしてペースト、である。lessはファイルの内容を画面に収まる行数ずつ表示するページャと呼ばれるユーティリティであり、スペースキーを押すことにより次の画面を表示させたり、pキーで画面の初めに戻ったりできる。矢印キー(↑↓)で前後に移動することも可能である。終了はキーを押すことにより行う。

    なお、引数のファイル名を与えなかった場合にはコマンドラインからの入力をそのまま印字する。この場合についても動作を確かめよ。(スクリプトの終了はC-dで行える。)


    2.12.5 for文

    for文は範囲や条件が他の条件に依存しないで明確に決まっている場合にwhileなどよりずっと簡単に繰り返し(ループ)を記述できる。


    #!/usr/bin/ruby
    #for.rb
    
    for i in [1,2,3]
      print i, "\n"
    end
    
    では、i1,2,3の場合について、iの値を出力させている。この場合、iの範囲があらかじめ確定しているのでfor文により簡単に記述できる。配列の要素を順に用いる、ある範囲の数に関して実行を行う、などがfor文に適した条件である。

    while文により上記のスクリプトを実行させる例が教科書に載っているが、行数が多く、処理も複雑になっている。


    #!/usr/bin/ruby
    #while.rb
    
    ary=[1,2,3]
    i = 0
    while i < ary.size
      print ary[i], "\n"
      i = i + 1
    end
    
    ここでは、まず、iを代入し配列aryの1番目の要素を出力、次にiに1を加えることでaryの2番目の要素を出力、というように順次要素を出力している。

    前節のwhile文を用いて作成したファイルの中身を表示するスクリプト(cat.rb)はfor文を用いた場合には以下のようになる。


    #!/usr/bin/ruby
    #cat2.rb
    
    for line in ARGF
      print line
    end
    
    ここで出てくるARGFは重要な定数であり、p.52の表2-2に示されているようにコマンドラインからの引数として与えられたファイルの「仮想」ファイルハンドルである。for文により、ARGFに行がある範囲で1行を取り出し、変数lineに代入する。


    2.12.6 break、next、redo

    プログラムの処理の都合でループの正規の終了ではなく途中で抜ける必要がある場合もある。そのようなループを抜けるコマンドとしてbreakがある。(breakにより、ループを終了できる。)nextはその回のループを中断して次の回に回す動作を行い、redoはその回のループの先頭に戻る。


    #!/usr/bin/ruby
    #break.rb
    
    while line = gets()
      line.chomp!
      next if line == "skip"
      break if line == "quit"
      print line, "\n"
    #redo if line == "redo" end
    上記のスクリプトをnextという名前の以下のような内容のファイル

    aaa
    bbb
    ccc
    skip
    ddd
    redo
    eee
    quit
    fff

    に対して実行すると、その結果は以下のように表示される。

    aaa
    bbb
    ccc
    ddd
    redo
    eee

    この動作を考えて見よ。また、redoの行の先頭の#を外すことにより、redoの動作を確認できるが、その際には処理の強制終了のキーコマンドであるC-cを覚えておく必要がある。redoの行をprintの行の上に配置すると実行結果が異なる。どこでループが循環しているかを考えることにより結果の違いを理解できる。


    2.12.7 return文

    return文により戻り値を明示的に与えることに加えてループを実行した元のメソッドの終了を宣言できる。


    #!/usr/bin/ruby
    #return.rb
    
    def print_n(n)
      i = 0
      while true
        print i, "\n"
        return if n <= i
        i += 1
      end
    end
    
    print_n(5)
    
    上のスクリプトでは任意の自然数nをprint_nという関数に与えることにより0,およびn以下の自然数を出力する。ここで、while文の書き方は無数にあるが、returnでprint_nというメソッドを終了させるために、上のような形式となっている。

    また、return文においても多重代入と組み合わせることにより便利な記述が利用できる。


    #!/usr/bin/ruby
    #multi.rb
    
    def multi(n)
      return n, n*2, n*4
    end
    v1, v2, v3 = multi(3)
    print "v1= ", v1, "  v2= ", v2, "  v3= ", v3, "\n"
    

    2.12.8 大域脱出

    省略


    2.13 数値と計算

    他倍長整数

    Rubyでは大きな数だからといって格別意識をしないでも扱うことができ、また、数値が整数か、浮動小数かなども気にしないで計算を行うことができる。以下の例は、階乗を求めるスクリプトである。


    #!/usr/bin/ruby
    #fact.rb
    
    def fact(n)
      return 1 if n ==0
      f = 1
      while n > 0
        f *= n
        n -= 1
      end
      return f
    end
    
    print fact(ARGV[0].to_i), "\n"
    

    ここでは、defにより初めにnの階乗を計算するメソッドを定義しているが、そのメソッドは以下のように場合分けされている。

    n<0のとき(階乗は定義されない)
    n=0のとき(階乗は無条件に1)
    n>0のとき(真の階乗計算)

    実際の階乗計算は

    f *= n   ( --> f = f * n )
    

    の部分であり、次に続く

    n -= 1   ( --> n = n - 1 )
    

    と併せて、n(n-10)(n-2)(n-3)‥1のように大きな方から順に条件が成立する間かけていくことにより実現されている。また、ARGV(p.52)は重要な定数であり、コマンドライン引数を指す。ここでは、fact.rbという名前を付けるとすると、

    $ ./fact.rb hoge

    としたときの、hogeファイルがARGVになる。print文の書き方は、任意のnの階乗を求めるために、引数として入力された数字(その段階では数値ではなく文字列として認識されている。)を第一要素とする配列とし、to_iメソッドにより文字列を数値に変換している。

    文字列の処理が中心であるRubyでも数値計算を行う必要があることもある。そのときに、数学でなじみのある関数をそのまま使用できることが望ましい。Rubyにも基本的な関数はほとんど定義されている。


    #!/usr/bin/ruby
    #pi.rb
    
    include Math
    printf("pi: %f = %f\n", PI, atan2(1,1)*4, "\n")
    printf("log10 = %f\n", log(10), "\n", log(10))

    上の例では、フォーマット付き出力であるprintfを用いて書式を整えている。ここで、atan2(x,y)という関数はあまり見かけない関数である。この関数は、以下のような図を考えると理解できる。



    よって、この関数を利用すると円周率の値を求めることが可能となる。


  3. 文字数計算スクリプト

    先週のエレクトロニクスセミナーAの課題において、文字数を約400字程度と指定した。原稿用紙のような紙ではなく、テキストエディタ上で文字を入力する場合には文字数の把握が直感的にできない。(自分で適宜改行を加えて、一行の文字数をそろえて行けば、行数から文字数がわかるがその場合には編集作業を行うたびに、整形し直さなければならない。)

    Rubyを使えば、文字数の計算も簡単にできる。ただし、日本語は2バイトで一文字であるので、日本語を考慮したスクリプトを書く必要がある。以下に一例を示すので、適宜使用すること。

    #!/usr/bin/ruby
    #count.rb
    
    require "kconv"
    print Kconv.toeuc(ARGF.read).gsub!(/\s+/p,"").scan(/./).size, "\n"
    

  4. 課題

    授業の進み具合により課題を与えるのでアナウンスに注意すること。



目次ページへ戻る