7つの言語7つの世界 第2章 Ruby 1-3 日目練習問題やってみました

7つの言語 7つの世界

の 7 つの言語の言語の雰囲気を味わえる本です。

7つの言語 7つの世界

7つの言語 7つの世界

この本では、各言語 3 日間分の解説と練習問題が用意されているので、最短 3 x 7 = 21 日で 7 つの言語を味わえます。また、言語ごとに章が割り当てられ、1 日ずつ節に分かれています。各節の最後に「試してみよう」という練習問題が用意されています。しかし、残念ながら解答がありません。

そこで、私の解答例を載せてみることにしました。今回は、第2章 Ruby を読んで、「試してみよう」をやってみました。個人の解答例で絶対正解というわけではありませんが、参考になれば幸いです。また、「こうすればもっといいよ」というご意見・コメントがあれば、大歓迎です。

1 日目 (p.18)

Emacs 上で inf-ruby から irb を利用しました。

  • 文字列 “Hello, world” を出力する.
irb(main):001:0> puts 'Hello , World'
Hello, World
nil
  • 文字列 “Hello, Ruby” の中の “Ruby” という単語のインデックスを検索する.
irb(main):002:0> puts 'Hellow, Ruby.'.index('Ruby') 
8
nil
  • 自分の名前を 10 回出力する
irb(main):003:0> puts "id:passingloop\n" * 10
id:passingloop
id:passingloop
id:passingloop
id:passingloop
id:passingloop
id:passingloop
id:passingloop
id:passingloop
id:passingloop
id:passingloop
nil
  • 文字列 “This is sentence number 1” の 1 を 10 までカウントアップしながら 10 回出力する.
irb(main):001:0> 1.upto(10) { |i| puts "This is sentence number #{i}" }
This is sentence number 1
This is sentence number 2
This is sentence number 3
This is sentence number 4
This is sentence number 5
This is sentence number 6
This is sentence number 7
This is sentence number 8
This is sentence number 9
This is sentence number 10
1
  • ファイルに格納されている Ruby のプログラムを実行する.
irb(main):002:0> load '~/Desktop/foo.rb'
foo
true
  • プレイヤーに数字を選択してもらい,その数字が生成された乱数よりも大きいか小さいかを返す.
irb(main):003:0> puts gets.to_i > rand(10) ? 'high' : 'low'
5
high
nil

2 日目

ひとつめ、

# 最初に,each だけを用いて,16 個の数値と 4 個の数値の配列の中身を同時に出力せよ.

array16 = (1..16).to_a

queue = []
array16.each do |i|
  queue << i
  next if queue.length < 4
  puts queue.join(',')
  queue = []
end

puts '-' * 20

# 次に,同じことを Enumerable の each_slice を用いて実行せよ.

array16.each_slice(4) { |a| puts a.join(',') }

実行結果:

% ruby 2-1.rb 
1,2,3,4
5,6,7,8
9,10,11,12
13,14,15,16
--------------------
1,2,3,4
5,6,7,8
9,10,11,12
13,14,15,16

ふたつめ、

# Tree クラスは面白いクラスだったが,きれいなインタフェースを用いて
# 新しいツリーを指定することはできなかった.そこで,initializer に
# ハッシュと配列が入れ子になった構造体を指定できるようにせよ.

class Tree
  def initialize(tree)
    tree = tree.to_a.first if tree.is_a?(Hash)
    @name = tree[0]
    @children = tree[1].map { |subtree| Tree.new(subtree) }
  end
end

# 具体的には次のツリーを指定できるようにしたい

tree =  {
  'grandpa' => {
    'dad' => {
      'child 1' => [],
      'child 2' => []
    },
    'uncle' => {
      'child3' => [],
      'child4' => []
    }
  }
}
p Tree.new(tree)

実行結果

% ruby 2-2.rb 
#<Tree:0x0000010086c5d0 @name="grandpa", @children=[#<Tree:0x0000010086c4e0 @name="dad", @children=[#<Tree:0x0000010086c440 @name="child 1", @children=[]>, #<Tree:0x0000010086c3c8 @name="child 2", @children=[]>]>, #<Tree:0x0000010086c350 @name="uncle", @children=[#<Tree:0x0000010086c2b0 @name="child3", @children=[]>, #<Tree:0x0000010086c238 @name="child4", @children=[]>]>]>

みっつめ、

#!/usr/bin/env ruby

# ファイル内で,あるフレーズを含むすべての行を出力する簡単な grep を書け.
# 簡単な正規表現でマッチングを行い,ファイルから行を読み出す必要がある
# (この処理は Ruby では驚くほど簡単に書ける).
# 必要なら行番号も出力してみるとよい.

regexp = Regexp.compile(ARGV.shift)
open(ARGV.shift).each_with_index do |line, i|
  next unless regexp =~ line
  puts "#{i}: #{line}"
end

実行結果

% ./2-3.rb ee 2-2.rb 
0: # Tree クラスは面白いクラスだったが,きれいなインタフェースを用いて
4: class Tree
5:   def initialize(tree)
6:     tree = tree.to_a.first if tree.is_a?(Hash)
7:     @name = tree[0]
8:     @children = tree[1].map { |subtree| Tree.new(subtree) }
14: tree =  {
26: p Tree.new(tree)

3 日目 (p.33)

class CsvRow
  def initialize(headers, row)
    @headers = headers
    @row = row
  end

  def method_missing(name, *args)
    @row[@headers.index(name.to_s)]
  end
end

module ActsAsCsv
  def self.included(base)
    base.extend ClassMethods
  end

  module ClassMethods
    def acts_as_csv
      include InstanceMethods
    end end

  module InstanceMethods
    def each
      @csv_contents.each do |row|
        yield(CsvRow.new(@headers, row))
      end
    end

    def read
      @csv_contents = []
      filename = self.class.to_s.downcase + '.txt'
      file = File.new(filename)
      @headers = file.gets.chomp.split(',')
      file.each do |row|
        @csv_contents << row.chomp.split(',')
      end
    end

    attr_accessor :headers, :csv_contents

    def initialize
      read
    end
  end
end

class RubyCsv # no inheritance! You can mix it in include ActsAsCsv acts_as_csv
  include ActsAsCsv
  acts_as_csv
end

csv = RubyCsv.new
csv.each {|row| puts row.one}

実行結果、

% ruby 3.rb
lions