三角暗算其の二

やった三角暗算,もっとシンプルになったので,もう一度考えてみる.

三角暗算シンプル版

名付けて,三角暗算其の二です.今回のは,入力周りとアルゴリズムを簡単にしました.

#! /opt/local/bin/ruby
class Array
  def to_i!
    self.map!{|e| e.to_i}
  end
  
  def sankaku
    if self.size == 1
      self[0]
    elsif self.size > 1
      new_arr = []
      (0 ... (self.size - 1)).each{ |i|
        new_arr[i] = self[i] + self[i + 1]
      }
      new_arr.sankaku
    end
  end
end

puts "Please input numbers by separating with a space"
print ">"
l = gets.chomp
nums = l.split(/\s+/)
if nums[0] =~ /^$/
  nums.shift
end
nums.to_i!

puts "result:#{nums.sankaku}"

=>
$ ruby sankaku2.rb 
Please input numbers by separating with a space
>  1 2  3 4 5 6  7 
result:256

前のアレは,作った当時の心境を回想してみると,

三角暗算と言えば最低でも3つは計算できるよな.てことは,3つの計算方法を先に考えるか.例えばa,b,cとすると,三角暗算するとa+2b+cだ.とりあえずコレをメソッドにして,それだと柔軟性が無いから何個でも計算できるようにするか.

というように,「三角暗算は3つでやるもの」という視界から抜け出せず,挙げ句,三角暗算のアルゴリズムが,もっと簡単であるという事に気づかなかったんですね.

途中経過を表示

折角,一段ずつ計算するので,これを三角暗算っぽく表示すると楽しいかも.とか思いながら,書いてみた.ゴチャゴチャしてるので,読まない方が良いと思う.

#! /opt/local/bin/ruby
class Array
  def to_i!
    self.map!{|e| e.to_i}
  end
  
  def depth
    count = 0
    return lambda{
      count += 1
    }
  end
  
  def sankaku(depth = lambda{0})
    # depth省略可能
    d = depth.call
    (0 ... d).each{
        print " "
    }
    if self.size == 1
      puts self[0]
      self[0]
    elsif self.size > 1
      new_arr = []
      (0 ... (self.size - 1)).each{ |i|
        new_arr[i] = self[i] + self[i + 1]
      }
      self.each{ |e|
        print "#{e} "
      }
      puts
      if (d > 0)
        (0 ... d).each{
          print " "
        }
        (0 ... (self.size - 1)).each{
          print " +"
        }
        puts
      else
        (0 ... (self.size - 1)).each{
          print " +"
        }
        puts
      end
      new_arr.sankaku(depth)
    end
  end
end

puts "Please input numbers by separating with a space"
print ">"
l = gets.chomp
nums = l.split(/\s+/)
if nums[0] =~ /^$/
  nums.shift
end
nums.to_i!

depth = nums.depth # 深さを求める

puts "result:#{nums.sankaku(depth)}"

=>
$ ruby sankaku3.rb 
Please input numbers by separating with a space
>1 2 3 4
 1 2 3 4 
  + + + 
  3 5 7 
   + + 
   8 12 
    + 
    20
result:20

アホみたいにゴチャゴチャしてるなぁ.ちなみに出力結果,4つぐらいならまだマシだけど,5つからズレまくります.

呼ばれた回数を数えたいがために,わざわざlambdaを使いたかったので変に長くなってますが,この辺りは幾らでも改良の余地がありそう*1

で,考えてみた

さっきよりは,まだマシ.

#! /opt/local/bin/ruby
class Array
  def to_i!
    self.map!{|e| e.to_i}
  end
  
  def d_count
    count = 0
    return lambda{count += 1}
  end
  
  def sankaku
    self.to_i!
    d = d_count
    self.u_sankaku(d)
  end
  
  def u_sankaku(depth)
    d = depth.call
    (0 ... d).each{
        print " "
    }
    if self.size == 1
      puts self[0]
      self[0]
    elsif self.size > 1
      new_arr = []
      (0 ... (self.size - 1)).each{ |i|
        new_arr[i] = self[i] + self[i + 1]
      }
      self.each{ |e|
        print "#{e} "
      }
      puts
      (0 ... d).each{
        print " "
      }
      (0 ... (self.size - 1)).each{
        print " +"
      }
      puts
      new_arr.u_sankaku(depth)
    end
  end
end

puts "Please input numbers by separating with a space"
print ">"
l = gets.chomp
nums = l.split(/\s+/)
if nums[0] =~ /^$/
  nums.shift
end

puts "result:#{nums.sankaku}"

=>
$ ruby sankaku4.rb 
Please input numbers by separating with a space
>1 2 3 4
 1 2 3 4 
  + + +
  3 5 7 
   + +
   8 12 
    +
    20
result:20

なんというか,sankakuの下に本体(u_sankaku)を置く事で,インタフェースはそのままに,実装方法(本体を呼ぶ方法)はsankakuで固定されている(引数も渡さなくて良い)ので,ユーザがdepthを定義しないで良いと.さらに,to_i!まで勝手にやるので,さらに楽かな.
俺流・仕様と実装の分離!俺流・カプセル化!考えてみると,上のゴチャゴチャ版は,

depth = lambda{puts "hoge"}

とかワケの分からん事をされたら,ひとたまりもありませんね.危ない.

結果的に

一番上が一番すき.HTMLだと,tableタグとか使えばもう少し綺麗に表示できるかな.そこでerubyですか.

*1:グローバル変数,クラス変数以外で