「たのしいRuby 第3版」プログラムリスト

第1章 はじめてのRuby

List 1.1 : helloruby.rb

print("Hello, Ruby.\n")

List 1.2 : put_and_p.rb

puts "Hello,\n\tRuby."
p "Hello,\n\tRuby."

List 1.3 : kiritsubo.rb

print "いづれの御時にか女御更衣あまたさぶらいたまいけるなかに\n"
print "いとやむごとなき際にはあらぬがすぐれて時めきたまふありけり\n"

List 1.4 : area_volume.rb

x = 10
y = 20
z = 30
area = (x*y + y*z + z*x) * 2
volume = x * y * z
print "表面積=", area, "\n"
print "体積=", volume, "\n"

List 1.5 : comment_sample.rb

=begin
 「たのしいRuby 第3版」サンプル
 コメントの使い方の例
    2006/06/16 作成
    2006/07/01 一部コメントを追加
    2009/11/01 第3版用に更新
=end

x = 10     #  横
y = 20     #  縦
z = 30     #  高さ

# 表面積と体積を計算する
area = (x*y + y*z + z*x) * 2
volume = x * y * z

# 出力する
print "表面積=", area, "\n"
print "体積=", volume, "\n"

List 1.6 : bigger_smaller.rb

a = 20
if a >= 10 then
  print "bigger\n"
end
if a <= 9 then
  print "smaller\n"
end

List 1.7 : hello_ruby.rb

def hello
  print "Hello, Ruby\n"
end

hello()

List 1.8 : hello.rb

def hello
  print "Hello, Ruby.\n"
end

List 1.9 : use_hello.rb

require "hello"     # hello.rbの読み込み(「.rb」は必要ない)
hello()             # helloメソッドの起動

第2章 便利なオブジェクト

List 2.1 : fontsize.rb

print "<html><title>font size list</title>"
print "<body>\n<p>\n"
font_table = {:normal => "+0", :small => "-1", :big => "+1"}
font_table.each do |key, value|
  print '<font size="', value, '">',key,'</font><br>',"\n"
end
print "</p></body></html>\n"

コラム「nilとは?」: print_hayasi.rb

names = ["小林", "林", "高野", "森岡"]
names.each do |name|
  if /林/ =~ name
    puts name
  end
end

コラム「ppメソッド」: p_and_pp.rb

require "pp"
v = [
  {
    :key_00 => "「たのしいRuby」",
    :key_01 => "「Rubyレシピブック」",
    :key_02 => "「Railsレシピブック」",
    :key_03 => "「たのしいRuby 第3版」",
  }
]
p v
pp v

第3章 コマンドを作ろう

List 3.1 : print_argv.rb

print "最初の引数: ",  ARGV[0], "\n"
print "2番目の引数: ", ARGV[1], "\n"
print "3番目の引数: ", ARGV[2], "\n"

List 3.2 : happybirthday.rb

name = ARGV[0]
print "Happy Birthday, ", name, "!\n"

List 3.3 : arg_arith.rb

num0 = ARGV[0].to_i
num1 = ARGV[1].to_i

print num0, " + ", num1, " = ", num0 + num1, "\n"
print num0, " - ", num1, " = ", num0 - num1, "\n"
print num0, " * ", num1, " = ", num0 * num1, "\n"
print num0, " / ", num1, " = ", num0 / num1, "\n"

List 3.4 : read_text.rb

filename = ARGV[0]
file = open(filename)
text = file.read
print text
file.close

List 3.5 : read_text_simple.rb

filename = ARGV[0]
text = File.read(filename)
print text

List 3.6 : read_text_simple.rb

print File.read(ARGV[0])

List 3.7 : gets_text.rb

filename = ARGV[0]
file = open(filename)
while text = file.gets
  print text
end
file.close

List 3.8 : simple_grep.rb

pattern = Regexp.new(ARGV[0])
filename = ARGV[1]
 3:
file = open(filename)
while text = file.gets
  if pattern =~ text
    print text
  end
end
file.close

第4章 オブジェクトと変数・定数

List 4.1 : scopetest.rb

$x = 0
 x = 0

require "sub"

p $x  #=> 1
p  x  #=> 0

List 4.2 : sub.rb

$x = 1  ## グローバル変数に代入
 x = 1  ## ローカル変数に代入

第5章 条件判断

List 5.1 : ad2heisei.rb

# 西暦から平成に変換する

ad = ARGV[0].to_i
heisei = ad - 1988
print heisei,"\n"

List 5.2 : if_elsif.rb

a = 10
b = 20
if b > a
  print  "bはaよりも大きい\n"
elsif b == a
  print  "bはaと同じ\n"
else
  print  "aよりも小さい\n"
end

List 5.3 : unless.rb

a = 10
b = 20
unless a > b
  print "aはbより大きくない\n"
end

List 5.4 : case.rb

tags = [ "A", "IMG", "PRE" ]
tags.each do |tagname|
  case tagname
  when "P","A","I","B","BLOCKQUOTE"
    print tagname, " has child.\n"
  when "IMG", "BR"
    print tagname, " has no child.\n"
  else
    print tagname, " cannot be used.\n"
  end
end

List 5.5 : case_class.rb

array = [ "a", 1, nil ]
array.each do |item|
  case item
  when String
    puts "item is a String."
  when Numeric
    puts "item is a Numeric."
  else
    puts "item is something."
  end
end

第6章 繰り返し

List 6.1 : times.rb

4.times do
  print "いちめんのなのはな\n"
end

List 6.2 : times2.rb

5.times do |i|
  print i,"回目の繰り返しです。\n"
end

List 6.3 : times3.rb

5.times do |i|
  print i+1,"回目の繰り返しです。\n"
end

List 6.4 : for.rb

sum = 0
for i in 1..5
  sum = sum + i
end
print sum,"\n"

List 6.5 : for_names.rb

names = ["awk", "Perl", "Python", "Ruby"]
for name in names
  print name,"\n"
end

List 6.6 : while.rb

i = 1
while i<3
  print i,"\n"
  i+=1
end

List 6.7 : while2.rb

sum = 0
i = 1
while i <= 5
  sum += i
  i += 1
end
print sum,"\n"

List 6.8 : while3.rb

sum = 0
i = 1
while sum < 50
  sum += i
  i += 1
end
print sum,"\n"

List 6.9 : until.rb

sum = 0
i = 1
until sum>=50
  sum += i
  i += 1
end
print sum, "\n"

List 6.10 : while_not.rb

sum = 0
i = 1
while !(sum >= 50)
  sum += i
  i += 1
end
print sum, "\n"

List 6.11 : each_names.rb

names = ["awk","Perl","Python","Ruby"]
names.each do |name|
  print name,"\n"
end

List 6.12 : each.rb

sum = 0
(1..5).each do |i|
  sum = sum + i
end
print sum,"\n"

List 6.13 : break_next_redo.rb

print "breakの例:\n"
i = 0
["Perl", "Python", "Ruby", "Scheme"].each do |lang|
  i += 1
  if i == 3
    break
  end
  p [ i, lang ]
end
10:
print "\nnextの例:\n"
i = 0
["Perl", "Python", "Ruby", "Scheme"].each do |lang|
  i += 1
  if i == 3
    next
  end
  p [ i, lang ]
end
20:
print "\nredoの例:\n"
i = 0
["Perl", "Python", "Ruby", "Scheme"].each do |lang|
  i += 1
  if i == 3
    redo
  end
  p [ i, lang ]
end

List 6.14 : ten_lines_grep.rb

pattern = Regexp.new(ARGV[0])
filename = ARGV[1]
max_matches = 10       # 出力する最大数
matches = 0            # マッチした行数
file = open(filename)
while text = file.gets
  if matches >= max_matches
    break
  end
  if pattern =~ text
    matches += 1
    print text
  end
end

List 6.15 : strip.rb

file = open(ARGV[0])
while text = file.gets
  next if /^\s*$/ =~ text   # 空白行
  next if /^#/ =~ text      # シャープで始まる行
  print text
end

List 6.16 : fact.rb

# 10の階乗を求める
ans = 1
for i in 1..10
  ans *= i
end

# 出力する
print "10! = ", ans, "\n"

List 6.17 : striped_fact.rb

ans = 1
for i in 1..10
  ans *= i
end
print "10! = ", ans, "\n"

第7章 メソッド

List 7.1 : hello_with_name.rb

def hello(name)
  print("Hello, ", name, ".\n")
end

hello("Ruby")

List 7.2 : hello_with_default.rb

def hello(name="Ruby")
  print("Hello, ", name, ".\n")
end

hello()            # 引数を省略して呼び出す
hello("Newbie")    # 引数を指定して呼び出す

第8章 クラスとモジュール

List 8.1 : hello_class.rb

class HelloWorld                 # class文
  Version = "1.0"                # 定数の定義

  def initialize(myname="Ruby")  # initializeメソッド
    @name = myname               # インスタンス変数の初期化
  end

  def hello                      # インスタンスメソッド
    print "Hello, world. I am ", @name, ".\n"
  end
end

bob   = HelloWorld.new("Bob")
alice = HelloWorld.new("Alice")
ruby  = HelloWorld.new

bob.hello

List 8.4 : hello_count.rb

class HelloCount
  @@count = 0           # helloメソッドの呼び出し回数

  def HelloCount.count  # 呼び出し回数を参照するためのクラスメソッド
    @@count
  end
  def initialize(myname="Ruby")
    @name = myname
  end
  def hello
    @@count += 1        # 呼び出し回数を加算する
    print "Hello, world. I am ", @name, ".\n"
  end
end

bob   = HelloCount.new("Bob")
alice = HelloCount.new("Alice")
ruby  = HelloCount.new

p HelloCount.count    #=> 0
bob.hello
alice.hello
ruby.hello
p HelloCount.count    #=> 3

List 8.5 : ext_string.rb

class String
  def count_word
    ary = self.split(/\s+/)   # 自分自身を空白文字で分解する
    return ary.size           # 分解後の配列の要素数を返す
  end
end

str = "Just Another Ruby Newbie"
p str.count_word              #=> 4

List 8.6 : acc_test.rb

class AccTest
  def pub
    puts "pub is a public method."
  end
  public :pub       # pubメソッドをpublicに設定(指定しなくてもよい)
  def priv
    puts "priv is a private method."
  end
  private :priv     # privメソッドをprivateに設定
end

acc = AccTest.new
acc.pub             #=> pub is a public method.
acc.priv            #=> エラー

List 8.7 : point.rb

class Point
  attr_accessor :x, :y  # アクセスメソッドを定義する
  protected :x=, :y=    # x=とy=をprotectedにする

  def initialize(x=0.0, y=0.0)
    @x, @y = x, y
  end

  def swap(other)                     # x, yの値を入れ換えるメソッド
    tmp_x, tmp_y = @x, @y
    @x, @y = other.x, other.y
    other.x, other.y = tmp_x, tmp_y   # 同一クラス内では呼び出すことができる
    return self
  end
end

p0 = Point.new
p1 = Point.new(1.0, 2.0)
p [ p0.x, p0.y ]        #=> [0.0, 0.0]
p [ p1.x, p1.y ]        #=> [1.0, 2.0]

p0.swap(p1)
p [ p0.x, p0.y ]        #=> [1.0, 2.0]
p [ p1.x, p1.y ]        #=> [0.0, 0.0]

p0.x = 10.0             #=> エラー

List 8.8 : ring_array.rb

ring_array.rb
class RingArray < Array    # スーパークラスを指定する
  def [](i)                # 演算子[]の再定義
    idx = i % size         # 新しいインデックスを求める
    super(idx)             # スーパークラスの同名のメソッドを呼ぶ
  end
end

eto = RingArray[ "子", "丑", "寅", "卯", "辰", "巳",
                 "午", "未", "申", "酉", "戌", "亥" ]
p eto[6]     #=> "午"
p eto[11]    #=> "亥"
p eto[15]    #=> "卯"
p eto[-1]    #=> "亥"

List 8.9 : mixin_sample.rb

module MyModule
  # 共通して提供したいメソッドなど
end

class MyClass1
  include MyModule
  # MyClass1 に固有のメソッドなど
end

class MyClass2
  include MyModule
  # MyClass2 に固有のメソッドなど
end

List 8.10 : hello_module.rb

module HelloModule            # module文
  Version = "1.0"             # 定数の定義
  def hello(name)             # メソッドの定義
    print "Hello, ", name, ".\n"
  end
  module_function :hello      # helloをモジュール関数として公開する
end

p HelloModule::Version        #=> "1.0"
HelloModule.hello("Alice")    #=> Hello, Alice.

include HelloModule           # インクルードしてみる
p Version                     #=> "1.0"
hello("Alice")                #=> Hello, Alice.

List 8.11 : fetch_and_downcase.rb

def fetch_and_downcase(ary, index)
  if str = ary[index]
    return str.downcase
  end
end

ary = ["Boo", "Foo", "Woo"]
p fetch_and_downcase(ary, 1)     #=> "foo"

List 8.12 : http_get.rb

  require "net/http"
  url = URI.parse("http://www.ruby-lang.org/ja/")
  http = Net::HTTP.start(url.host, url.port)
  doc = http.get(url.path)
  puts doc

第9章 エラー処理と例外

List 9.1 : wc.rb

ltotal = 0                           # 行数の合計
wtotal = 0                           # 単語数の合計
ctotal = 0                           # 文字数の合計
ARGV.each do |file|
  begin
    input = open(file)               # ファイルを開く(A)
    l = 0                            # file内の行数
    w = 0                            # file内の単語数
    c = 0                            # file内の文字数
    while line = input.gets
      l += 1
      c += line.size
      line.sub!(/^\s+/, "")          # 行頭の空白を削除
      ary = line.split(/\s+/)        # 空白文字で分解する
      w += ary.size
    end
    input.close                      # ファイルを閉じる
    printf("%8d %8d %8d %s\n", l, w, c, file) # 出力を整形する
    ltotal += l
    wtotal += w
    ctotal += c
  rescue => ex
    print ex.message, "\n"   # 例外のメッセージを出力(B)
  end
end
printf("%8d %8d %8d %s\n", ltotal, wtotal, ctotal, "total")

List 9.2 : catch_and_throw.rb

def test_throw
  throw :test
end

puts "test start"
catch(:test) do
  puts "before test_throw()"
  test_throw()
  puts "after test_throw()"
end
puts "test end"

第10章 数値(Numeric)クラス

ファイル名のあるソースコードはありません。

第11章 配列(Array)クラス

List 11.1 : list.rb

list = ["a", "b", "c", "d"]
for i in 0..3
  print i+1,"番目の要素は",list[i],"です。\n"
end

List 11.2 : sum_list.rb

list = [1, 3, 5, 7, 9]
sum = 0
for i in 0..4
  sum += list[i]
end
print "合計:",sum,"\n"

List 11.3 : sum_list2.rb

list = [1, 3, 5, 7, 9]
sum = 0
list.each do |elem|
  sum += elem
end
print "合計:",sum,"\n"

List 11.4 : list2.rb

list = ["a", "b", "c", "d"]
list.each_with_index do |elem, i|
  print i+1,"番目の要素は",elem,"です。\n"
end

List 11.5 : sum_with_each.rb

ary1 = [1, 2, 3, 4, 5]
ary2 = [10, 20, 30, 40, 50]
ary3 = [100, 200, 300, 400, 500]

i = 0
result = []
while i < ary1.length
  result << ary1[i] + ary2[i] + ary3[i]
  i += 1
end
p result   #=> [111, 222, 333, 444, 555]

List 11.6 : sum_with_zip.rb

ary1 = [1, 2, 3, 4, 5]
ary2 = [10, 20, 30, 40, 50]
ary3 = [100, 200, 300, 400, 500]

result = []
ary1.zip(ary2, ary3) do |a, b, c|
  result <<  a + b + c
end
p result   #=> [111, 222, 333, 444, 555] 

第12章 文字列(String)クラス

ファイル名のあるソースコードはありません。

第13章 ハッシュ(Hash)クラス

List 13.1 : word_count.rb

# 単語数のカウント
 2:
count = Hash.new(0)
 4:
## 単語の集計
while line = gets
  words = line.split
  words.each do |word|
    count[word] += 1
  end
end
12:
## 結果の出力
count.sort{|(k1,v1), (k2,v2)| v1 <=> v2}.each do |key, value|
  print "#{key}: #{value}\n"
end

第14章 正規表現(Regexp)クラス

List 14.1 : scan1.rb

"abracatabra".scan(/.a/) do |matched|
  p matched
end

List 14.2 : scan2.rb

"abracatabra".scan(/(.)(a)/) do |matched|
  p matched
end

List 14.3 : scan3.rb

"abracatabra".scan(/(.)(a)/) do |a, b|
  p a+"-"+b
end

List 14.4 : url_match.rb

str = "http://www.ruby-lang.org/ja/"
%r|http://([^/]*)/| =~ str
print "server address: ", $1, "\n"

第15章 IOクラス

List 15.1 : out.rb

$stdout.print "Output to $stdout.\n" # 標準出力
$stderr.print "Output to $stderr.\n" # 標準エラー出力

List 15.2 : tty.rb

if $stdin.tty?
  print "Stdin is a TTY.\n"
else
  print "Stdin is not a TTY.\n"
end

List 15.3 : read_uri.rb

require "open-uri"

# HTTP経由でデータを読み込む
open("http://www.ruby-lang.org") do |io|
  puts io.read       # Rubyのホームページをコンソールに出力する
end

# FTP経由でデータを読み込む
open("ftp://www.ruby-lang.org/pub/ruby/1.8/ruby-1.8.7.tar.gz") do |io|
  open("ruby-1.8.7.tar.gz", "w") do |f|  # ローカルファイルを開く
    f.write(io.read)
  end
end

List 15.4 : read_uri_ja.rb

require "open-uri"

options = {
  "Accept-Language" => "ja, en;q=0.5",
}
open("http://www.ruby-lang.org", options){|io|
  puts io.read
}

List 15.5 : stringio_puts.rb

require "stringio"

io = StringIO.new
io.puts("A")
io.puts("B")
io.puts("C")
io.rewind
p io.read #=> "A\nB\nC\n"

List 15.6 : stringio_gets.rb

require "stringio"

io = StringIO.new("A\nB\nC\n")
p io.gets #=> "A\n"
p io.gets #=> "B\n"
p io.gets #=> "C\n"

List 15.7 : stdout_put.rb

$stdout.puts "foo", "bar", "baz"

List 15.8 : stdout_putc.rb

$stdout.putc(82)  # 82は「R」のASCIIコード
$stdout.putc(?R)
$stdout.putc("\n")

List 15.9 : test_buffering1.rb

$stdout.print "out1 "
$stderr.print "err1 "
$stdout.print "out2 "
$stdout.print "out3 "
$stderr.print "err2\n"
$stdout.print "out4\n"

List 15.10 : test_buffering2.rb

$stdout.print "out1 "; $stdout.flush
$stderr.print "err1 "
$stdout.print "out2 "; $stdout.flush
$stdout.print "out3 "; $stdout.flush
$stderr.print "err2\n"
$stdout.print "out4\n"

List 15.11 : test_buffering3.rb

$stdout.sync = true        # 出力の同期を取る
$stdout.print "out1 "
$stderr.print "err1 "
$stdout.print "out2 "
$stdout.print "out3 "
$stderr.print "err2\n"
$stdout.print "out4\n"

List 15.12 : simple_grep_gz.rb

pattern = Regexp.new(ARGV[0])
filename = ARGV[1]
if /.gz$/ =~ filename
  file = IO.popen("gunzip -c #{filename}")
else
  file = File.open(filename)
end
while text = file.gets do
  if pattern =~ text
    print text
  end
end

第16章 FileクラスとDirクラス

List 16.1 : traverse.rb

def traverse(path)
  if FileTest.directory?(path) # ディレクトリの場合
    dir = Dir.open(path)
    while name = dir.read
      next if name == "."      # ※
      next if name == ".."     # ※
      traverse(path + "/" + name)
    end
    dir.close
  else
    process_file(path)         # ファイルに対する処理
  end
end
def process_file(path)
  puts path                    # ひとまず出力するだけ
end

traverse(ARGV[0])

List 16.2 : traverse_by_glob.rb

def traverse(path)
  Dir.glob(["#{path}/**/*", "#{path}/**/.*"]).each do |name|
    unless FileTest.directory?(name)
      process_file(name)
    end
  end
end

List 16.3 : listdir.rb

require 'find'

IGNORES = [ /^\./, /^CVS$/, /^RCS$/ ]

def listdir(top)
  Find.find(top) do |path|
    if FileTest.directory?(path)     # pathがディレクトリならば
      dir, base = File.split(path)
      IGNORES.each do |re|
        if re =~ base                # 無視したいディレクトリの場合
          Find.prune                 # それ以下の検索を省略する
        end
      end
      puts path                      # 出力する
    end
  end
end

listdir(ARGV[0])

第17章 TimeクラスとDateクラス

ファイル名のあるソースコードはありません。

第18章 Ruby落ち穂ひろい

List 18.1 : s_opt_sample.rb

puts "$foo: #{$foo}"
puts "$bar: #{$bar}"
puts "$baz: #{$baz}"

List 18.2 : warning_sample.rb

class WarningTest
  def initialize
    @test = "test."
  end
  def test
    print @tset,"\n"     ##「@test」を「@tset」と書いている!
  end
end

sample_test = WarningTest.new
sample_test.test

List 18.3 : backquote_sample.rb

dirlist = `dir`
dirlist.each{|line|
  if line =~ /.rb$/i
    print line
  end
}

List 18.4 : data_sample.rb

data = DATA.read
print data

__END__
ここに書かれている文字は,スクリプトとして解釈
されずに,そのままデータとして使用されます。

List 18.5 : lib_with_sample.rb

# クラスの定義
class Foo
  def initialize
    puts "foo!!"
  end
end

if __FILE__ == $0
  Foo.new   # サンプルコード
end

List 18.6 : param_grouping.rb

hash = {:a=>100, :b=>200, :c=>300}
hash.each_with_index do |(key, value), index|
  p [key, value, index]
end

List 18.7 : local_scope.rb

var = 1            # ファイル内のvar
class Foo
  var = 2          # クラス定義内のvar
  def meth
    var = 3        # メソッド定義内のvar
  end
end

=== ローカル変数の初期化

 ローカル変数は,最初に代入されたときに初期化されます。初期化されていないローカル変数を参照しようとすると例外(NameError)が発生します。

//cmd{
> 【ruby -e 'puts a'】
-e:1: undefined local variable or method `a' for main:Object (NameError)

List 18.8 : local_scope_test.rb

def local_scope_test(n)
  if n > 0
    positive = true
  elsif n < 0
    negative = true
  else
    zero = true
  end
  return [positive, negative, zero]
end
p local_scope_test(1)
p local_scope_test(0)
p local_scope_test(-1)

List 18.9 : local_and_block.rb

x = 1                # xを初期化
ary = [1, 2, 3]

ary.each do |x|      # ブロック変数としてxを使用する
  # …
end

p x                  # xの値を確認する

List 18.10 : local_and_block_new.rb

x = y = z = 0            # xとyとzを初期化
ary = [1, 2, 3]

ary.each do |x; y|   # ブロック変数x,ブロックローカル変数yを使用
  y = x                 # ブロックローカルyを代入
  z = x                 # ブロックローカルでないzを代入
  p [x, y, z]           # ブロック内のxとyの値を確認する
end

p [x, y, z]             # xとyの値を確認する

第19章 演算子

List 19.1 : vector.rb

class Vector
  attr_reader :x, :y
  def initialize(x=0, y=0)
    @x, @y = x, y
  end
  def inspect         # 表示用
    "(#{@x}, #{@y})"
  end
  def +(other)
    Vector.new(@x + other.x, @y + other.y) # x, y のそれぞれを足す
  end
  def -(other)
    Vector.new(@x - other.x, @y - other.y) # x, y のそれぞれを引く
  end
end

vec0 = Vector.new(3, 6)
vec1 = Vector.new(1, 8)

p vec0          #=> (3, 6)
p vec1          #=> (1, 8)
p vec0 + vec1   #=> (4, 14)
p vec0 - vec1   #=> (2, -2)

第20章 ブロック

List 20.1 : print_times.rb

5.times do
  print "<br>\n"
end

List 20.2 : print_no_times.rb

print "<br>\n"
print "<br>\n"
print "<br>\n"
print "<br>\n"
print "<br>\n"

List 20.3 : sum_each.rb

sum = 0
(1..5).each do |i|
  sum += i
end
print "合計: ",sum,"\n"

List 20.4 : sum_no_each.rb

sum = 0
sum += 1
sum += 2
sum += 3
sum += 4
sum += 5
print "合計: ",sum,"\n"

List 20.5 : print_fruit.rb

fruits = ['リンゴ', 'バナナ', 'パイナップル']
fruits.each do |elem|
  print elem,"\n"
end

List 20.6 : hash_each.rb

sum = 0
outcome = {"参加費"=>1000, "ストラップ代"=>1000,"懇親会会費"=>4000}
outcome.each do |item, price|
  sum += price
end
print "合計: ",sum,"\n"

List 20.7 : hash_each2.rb

sum = 0
outcome = {"参加費"=>1000, "ストラップ代"=>1000,"懇親会会費"=>4000}
outcome.each do |pair|
  sum += pair[1]   # 値を指定している
end
print "合計: ",sum,"\n"

List 20.8 : file_each.rb

f = File.open("sample.txt")
f.each do |line|
  print line
end
f.close

List 20.9 : file_each2.rb

File.open("sample.txt") do |f|
  f.each_line do |line|
    print line
  end
end

List 20.10 : book.rb

class Book
  attr_accessor :title, :author, :genre
  def initialize(title, author, genre=nil)
    @title  = title
    @author = author
    @genre = genre
  end
end

List 20.11 : booklist.rb

require 'book'

class BookList
  ## 初期化
  def initialize()
    @booklist = Array.new
  end
  ## 新しい本を加える
  def add(book)
    @booklist.push(book)
  end
  ## 本の冊数を返す
  def length
    @booklist.length
  end
  ## n番目に格納されている本を別の本にする
  def []=(n,book)
    @booklist[n] = book
  end
  ## n番目に格納されている本を返す
  def [](n)
    @booklist[n]
  end
  ## 本をリストから削除する
  def delete(book)
    @booklist.delete(book)
  end
end

List 20.12 : booklist_test.rb

require 'book'
require 'booklist'

# 本のリストを作る。最初はリストは空になる
booklist = BookList.new
# リストに挿入したい本を用意する
b1 = Book.new("せめて,本格らしく","城平京")
b2 = Book.new("Neo Aqua III","Neo Aqua")
# リストに本を追加する
booklist.add(b1)
booklist.add(b2)
# リストの本を出力する
print booklist[0].title, "\n"
print booklist[1].title, "\n"

List 20.14 : proc1.rb

pr = Proc.new{
  p "a"
}
p "b"
pr.call()

List 20.15 : proc2.rb

def foo(a, b, &block)
  block.call(a, b)
end

foo("a1", "b1"){|a, b|
  p a
  p b
}

第21章 Mix-in

List 21.1 : book_cmp.rb

class Book
  include Comparable

  def <=>(other)
    t = @genre.to_s <=> other.genre.to_s  # ジャンルを比較する
    return t if t != 0                # 違うジャンルならそのまま返す
    return @title <=> other.title     # タイトルを比較した結果を返す
  end

  attr_accessor :title, :author, :genre

  def initialize(title, author, genre=nil)
    @title  = title
    @author = author
    @genre = genre
  end
end

List 21.2 : book_test.rb

require 'book_cmp'

ary = []
ary << Book.new("Software", "Rucker", "SF")
ary << Book.new("BABEL-17", "Delany", "SF")
ary << Book.new("Programming Perl",   "Wall",  "Computer")
ary << Book.new("Programming Pearls", "Bentley", "Computer")

ary.sort.each{|book|
  printf "%-10s %-20s %s\n", book.genre, book.title, book.author
}

List 21.3 : booklist_enum.rb

require "booklist"

class BookList
  include Enumerable
  def each
    @booklist.each{|book|
      yield(book)
    }
  end
end

List 21.4 : booklist_enum_test.rb

require "booklist_enum"

booklist = BookList.new
booklist.add(Book.new("Software", "Rucker", "SF"))
booklist.add(Book.new("BABEL-17", "Delany", "SF"))
booklist.add(Book.new("Programming Perl",   "Wall",
                      "Computer"))
booklist.add(Book.new("Programming Pearls", "Bentley",
                      "Computer"))
titles = booklist.collect{|book|
  book.title
}
p titles

第22章 HTMLやRSSの解析

List 22.1 : open-uri-sample.rb

require 'open-uri'
open("http://www.ruby-lang.org/ja/") do |f|
  5.times do
    print f.gets
  end
end

List 22.2 : nokogiri-h3.rb

require 'rubygems'
require 'open-uri'
require 'nokogiri'

doc = Nokogiri::HTML(open("http://www.ruby-lang.org/ja/"),nil,'utf-8')
doc.css('h3').each do |h3|
  puts h3.text
end

List 22.3 : nokogiri-h3-local.rb

require 'rubygems'
require 'open-uri'
require 'nokogiri'

filename = ARGV[0]

doc = Nokogiri::HTML(open(filename),nil, 'utf-8')
doc.css('h3').each do |h3|
  puts h3.text
end

List 22.4 : nokogiri-a.rb

require 'rubygems'
require 'open-uri'
require 'nokogiri'

filename = ARGV[0]
doc = Nokogiri::HTML(open(filename),nil,'utf-8')
doc.css('a').each do |a|
  url = URI.parse(a["href"])
  if url.host 
    puts "#{a.text}:#{a["href"]}"
  end
end

List 22.5 : rss-parse.rb

require 'rss'

url = 'http://www.ruby-lang.org/ja/feeds/news.rss'
rss = RSS::Parser.parse(url)
puts rss.channel.title

List 22.6 : rss-items.rb

require 'rss'

url = 'http://www.ruby-lang.org/ja/feeds/news.rss'
rss = RSS::Parser.parse(url)

rss.items.each do |item|
  print item.pubDate.strftime("%Y/%m/%d"),":",item.title,"\n"
end

List 22.7 : nokogiri-detectrss.rb

require 'rubygems'
require 'open-uri'
require 'nokogiri'
require 'rss'

filename = ARGV[0]
doc = Nokogiri::HTML(open(filename),nil,'utf-8')
doc.css('link').each do |link|
  if link['type'] == 'application/rss+xml' && link['rel'] == 'alternate'
    href = link['href']
    url = URI.join(filename, href)
    puts "detect: #{url}"

    rss = RSS::Parser.parse(url)

    rss.items.each do |item|
      print item.pubDate.strftime("%Y/%m/%d"),":",item.title,"\n"
    end
    
  end
end

第23章 HTTPサーバのアクセスログ解析

文中で使用しているsample-access.logははこちらから取得してください。→sample-access.log

List 23.1 : accesslog_count.rb

count = 0                   # 行数を初期化する
File.open(ARGV[0]) do |io|  # ファイルを開く
  io.each_line do |line|    # 行ごとに処理する
    count += 1              # 行数を更新する
  end
end
puts count                  # 行数を表示する

List 23.2 : accesslog_parse.rb

CLF_REGEXP = /
  \A                        (?# 行頭)
  (\S+)\s                   (?# 1 address)
  (\S+)\s                   (?# 2 ident)
  (\S+)\s                   (?# 3 user)
  \[([^\]]+)\]\s            (?# 4 time)
  "(\S+)\s(\S+)\s(\S+)"\s   (?# 5 6 7 method url version)
  (\d+)\s                   (?# 8 status)
  (\d+|-)\s                 (?# 9 bytes)
  "([^"]*)"\s               (?# 10 referer)
  "([^"]*)"                 (?# 11 user_agent)
  \Z                        (?# 行末)
/x

count = 0                   # 行数を初期化する
File.open(ARGV[0]) do |io|  # ファイルを開く
  io.each_line do |line|    # 1行ごとに処理する
    if CLF_REGEXP =~ line   # 正規表現にマッチしたら
      p $~.captures         # キャプチャした部分を表示する
    end
    count += 1              # 行数を更新する
  end
end
puts count                  # 行数を表示する

List 23.3 : access_log.rb

module AccessLog
  CLF_REGEXP = /
    \A                        (?# 行頭)
    (\S+)\s                   (?# 1 address)
    (\S+)\s                   (?# 2 ident)
    (\S+)\s                   (?# 3 user)
    \[([^\]]+)\]\s            (?# 4 time)
    "(\S+)\s(\S+)\s(\S+)"\s   (?# 5 6 7 method url version)
    (\d+)\s                   (?# 8 status)
    (\d+|-)\s                 (?# 9 bytes)
    "([^"]*)"\s               (?# 10 referer)
    "([^"]*)"                 (?# 11 user_agent)
    \Z                        (?# 行末)
  /x

  Entry = Struct.new(   # 解析結果を保存するためのクラス
    :address, :ident, :user, :time,
    :method, :url, :version, :status, :byte,
    :referer, :user_agent
  )

  module_function

  def each_entry(file)
    file.each_line do |line|
      if entry = parse(line)
        yield(entry)
      end
    end
  end

  def parse(line)
    if m = CLF_REGEXP.match(line)
      return Entry.new(*m.captures)
    end
    $stderr.puts("parse failure: #{line.dump}")
    return nil
  end
end

List 23.4 : accesslog_parse2.rb

require "access_log"        # access_log.rbを読み込む

count = 0
File.open(ARGV[0]) do |io|
  AccessLog.each_entry(io) do |entry|
    p entry.to_a            # エントリを表示する
    count += 1
  end
end
puts count

List 23.5 : Rakefile

require "access_log"

entries = []
task :load do
  logfile = ENV["LOGFILE"] || "access.log"  # ログファイル名
  puts "loading #{logfile}."             # メッセージを表示する
  File.open(logfile) do |log|            # ログファイルを開いて
    AccessLog.each_entry(log) do |entry| # すべてのエントリを読み込む
      entries << entry
    end
  end
end

desc "時間帯別のアクセス数を集計する"
task :time => :load do
  hour_count = Hash.new(0)              # 集計用のハッシュ
  entries.each do |entry|               # エントリを順に処理する
    times = entry.time.split(/[:\/ ]/)  # 時刻を分割する
    hour_count[times[3]] += 1           # 「時」のカウントを追加する
  end
  hours = hour_count.keys.sort          # 「時」の一覧を取得する
  hours.each do |h|                     # 集計結果を順に表示する
    printf("%s: %s\n", h, "#" * (hour_count[h]/3))
  end
end

desc "URL別にアクセス数を集計する"
task :url => :load do
  url_count = Hash.new(0)              # 集計用のハッシュ
  entries.each do |entry|              # エントリを順に処理する
    url_count[entry.url] += 1          # URLのカウントを追加する
  end
  ranking = url_count.sort_by{|url, count| -count }
         # アクセス数の降順になるようにハッシュの要素をソート
  ranking.each do |url, count|         # 集計結果を順に表示する
    printf("%d: %p\n", count, url)
  end
end

desc "エラーになったアクセスを表示する"
task :error => :load do
  entries.each do |entry|       # エントリを順に処理する
    if /^[45]/ =~ entry.status  # ステータスが4xxか5xxなら表示する
      printf("%p %p %p\n", entry.time, entry.status, entry.url)
    end
  end
end

task :default => [:time, :error, :url]