たのしいRuby第1版練習問題回答 そのに

第12章 Stringクラス

(3)StringIOはめんどくさかったので割愛。

(1)kan2num.rb

#!ruby -Ks

def kan2num(string)
  kan_digit_tbl = {
    '' => 1,
    '' => 2,
    '' => 3,
    '' => 4,
    '' => 5,
    '' => 6,
    '' => 7,
    '' => 8,
    '' => 9
  }
  factor_tbl = {
    '' => 1000,
    '' => 100,
    '' => 10
  }
  n = 0
  str = string.strip
  until str.empty?
    case str
    when /\A(.*?)(#{factor_tbl.keys.join('|')})/o
      n += kan_digit_tbl[$1.empty? ? '' : $1] * factor_tbl[$2]
    when /\A.*?\Z/
      n += kan_digit_tbl[$&]
    end
    str = $'
  end
  n
end


if __FILE__ == $0
  p kan2num('七千百二十三')
end

(2)num2astrisk.rb

def num2astrisk(num)
  tbl = {
    '0' => '',
    '1' => '*',
    '2' => '**',
    '3' => '***',
    '4' => '****',
    '5' => '*****',
    '6' => '******',
    '7' => '*******',
    '8' => '********',
    '9' => '*********'
  }
  buf = ''
  String(num).split(//).each do |n|
    buf = #carry-up
      buf + buf + buf + buf + buf + buf + buf + buf + buf + buf
    tbl.has_key?(n) or break
    buf += tbl[n]
  end
  buf
end


if __FILE__ == $0
  puts '@' * 32
  puts num2astrisk(32)
end

OrderedHash

Hashクラスの章の練習問題から。テストしてないけどほぼ本家Hashと同じ
インタフェースを揃えてみた。

Hashの章の練習問題なのにalistで実装したらダメじゃん、という問題には
書き終わった後に気づいた。

# Ordered Hash class
#  for Ruby1.8 forward
#

class OrderedHash
  include Enumerable
  
  Upper19 = Object.const_defined?('KeyError')
  
  def initialize(ifnone=nil, &block)
    @body = []
    if block_given?
      ifnone.nil? or raise ArgumentError, 'wrong number of argumrnts.'
      @default_proc = block
    end
    @default = ifnone
  end
  
  attr_reader :body
  protected :body
  
  
  private
  
  def ifnone(key)
    if default_proc
      default_proc.call(self, key)
    else
      @default
    end
  end
  
  def fetch_none(key, default, &block)
    if block_given?
      default.nil? or
        warn 'warning: block superdedes default value argument'
      yield(key)
    else
      unless default.nil?
        klass = Upper19 ? KeyError : IndexError
        raise klass, 'key not found'
      end
      default
    end
  end
  
  
  public
  
  # Indexer
  
  def [](key)
    body.assoc(key)[1] or ifnone(key)
  end
  
  def []=(key, value)
    hit = body.assoc(key)
    if hit
      hit[1] = value
    else
      body << [key, value]
    end
  end
  
  def fetch(key, default=nil, &block)
    body.assoc(key)[1] or fetch_none(key, default, &block)
  end
  
  alias store []=
  
  
  # Element
  
  attr_reader :default_proc
  
  def default(key=nil)
    if default_proc
      key.nil? ? nil : default_proc.call(self, key)
    else
      @default
    end
  end
  
  def default=(v)
    @default_proc = nil
    @default = v
  end
  
  def keys
    body.map{|k, v| k }
  end
  
  def values
    body.map{|k, v| v }
  end
  
  def key(value)
    hit = body.rassoc(value) ? hit[0] : nil
  end
  
  def index(value)
    warn 'warning: Hash#index is deprecated; use Hash#key' if Upper19
    key(value)
  end
  
  def values_at(*keys)
    keys.map{|key| self[key] }
  end
  
  def indexes(*keys)
    warn 'warning: Hash#indexes is deprecated; use Hash#values_ati'
    values_at(*keys)
  end
  
  def indices(*key)
    warn 'warning: Hash#indices is deprecated; use Hash#values_ati'
    values_at(*keys)
  end
  
  def invert
    h = self.class.new
    each do |k, v|
      h[v] = k
    end
    h
  end
  
  def size
    body.size
  end
  
  alias length size
  
  
  # Iterator
  
  def each
    body.each{|k, v| yield(k, v) }
  end
  
  alias each_pair each
  
  def each_key
    body.each{|k, v| yield(k) }
  end
  
  def each_value
    body.each{|k, v| yield(v) }
  end
  
  
  # Distincter
  
  def empty?
    body.empty?
  end
  
  def key?(k)
    body.assoc(k) ? true : false
  end
  
  alias has_key?  key?
  alias include?  key?
  alias member?   key?
  
  def value?(v)
    body.rassoc(v) ? true : false
  end
  
  alias has_value? value?
  
  
  # Converter
  
  def to_a
    body.dup
  end
  
  def to_hash
    if default_proc
      hsh = Hash.new{|h, k|
        default_proc.call(h, k)
      }
    else
      hsh = Hash.new(@ifnone)
    end
    each{|k, v| hsh[k] = v }
    hsh
  end
  
  
  # HandleInner
  
  def rehash
    self
  end
  
  def replace(other)
    @body = other.body.dup
    self
  end
  
  def clear
    body.clear
    self
  end

  def shift
    empty? ? ifnone(nil) : body.shift
  end
  
  def delete(key)
    idx = nil
    body.each_with_index do |entry, i|
      if key == entry[0]
        idx = i ; break
      end
    end
    if idx
      body.delete_at(idx)
    else
      block_given? ? yield(key) : nil
    end
  end
  
  def delete_if(&block)
    reject!(&block)
    self
  end
  
  def reject!
    targets = []
    body.each_with_index do |entry, i|
      yield(entry[0], entry[1]) and targets << i
    end
    unless targets.empty?
      targets.each{|i| body.delete_at(i) }
      self
    end
  end
  
  def update(other)
    other.each do |k, v|
      self[k] = block_given? ? yield(k, self[k], v) : v
    end
  end
  
  alias merge! update
  
  def merge(other)
    dup.merge!(other)
  end
end



if __FILE__ == $0
  [Hash, OrderedHash].each do |klass|
    h = klass.new
    h['hoge'] = 'piyo'
    h[:foo] = :bar
    h['ruby'] = :wonderful
    h.each do |k, v|
      p k, v
    end
    puts '-'*60
  end
  #puts(Hash.instance_methods - OrderedHash.instance_methods)
end