どう書く?org 64

どう書く?org の問題「どう書く?org」にチャレンジしたんだけど、キー入力の捕捉の仕方が分からなくて挫折した。


投稿されたプログラムを見てみると、Ruby は mamamoto さんという人が先に投稿していた。このプログラムが(自分にとっては)いろいろと勉強になったのでメモさせてもらう。Curses とかぜんぜん知らなかったし、存在を知れたとしてもリファレンスだけじゃうまく使えなかったかもしれない。

require 'curses'

def inputChecker(n, str)
  Curses.addstr("input(#{str}) => ")
  Curses.refresh

  input = ""
  timeout = false

  until (c = Curses.getch.chr) == "\n"
    timer ||= Thread.new{ sleep(n); timeout = true }
    input += c
  end

  result = timeout ? "TIME OUT" : input == str ? "OK" : "NG"

  Curses.setpos(Curses.stdscr.cury+1, 0)
  Curses.addstr("result => #{result}")
  Curses.refresh

  Curses.setpos(Curses.stdscr.cury+2, 0)
end

if __FILE__ == $0
  Curses.init_screen
  3.times{ inputChecker(5, "ABCDEF") }
  Curses.close_screen
end


このプログラム中で、よく分からなかった部分が2つある。

よく分からなかった部分:1つ目

まず、プログラムの開始部分にある if 文の必要性がわからなかった。

if __FILE__ == $0


調べてみると、


__FILE__ には現在のファイル名、
$0 には現在実行中のトップレベルの Ruby スクリプトの名前。通常はプログラムのファイル名


が設定されているらしい。
したがって、 require によってライブラリとして利用される際は「if __FILE__ == $0 〜 end」の部分は実行されない。


といっておきながらよく分からなかったので実験してみる。

lib_with_sample.rb
class Foo
  def initialize
    puts "foo"
  end
end

p "__FILE__: #{__FILE__}"
p "$0: #{$0}"
if __FILE__ == $0
  Foo.new
end
lib_with_sample_use.rb
require 'lib_with_sample'


上記の2つのファイルをそれぞれ実行して結果を見てみる。

% ruby lib_with_sample.rb
"__FILE__: lib_with_sample.rb"
"$0: lib_with_sample.rb"
foo
% ruby lib_with_sample_use.rb
"__FILE__: ./lib_with_sample.rb"
"$0: lib_with_sample_use.rb"


分かり易く言い直してみると、


__FILE__ はそれ自身が含まれるファイル名(つまり、現在のファイル名)
$0 は実行時に指定したファイル名(つまり、現在実行中のファイル名)


ということだな。これで納得。


ちなみに、__FILE__ と $0 の定義はプログラミングRuby 第2版 言語編の p290, p291 を参考にした。

よく分からなかった部分:2つ目

inputChecker メソッド中の記述で、以下の部分の意味が分からなかった。第一印象は、何でここで OR なんかとってるんだろうと思った。

    timer ||= Thread.new{ sleep(n); timeout = true }


調べてみると、これは初期化時のイディオムの一種らしい。

a ||= 1         # a が偽か未定義ならば1を代入。初期化時のイディオムの一種。


これは、Ruby Reference Manual - るりまの自己代入の項で調べた。