プログラミング準備運動のメニューを決める

トラックバックで準備運動のメニュー案をもらったので、実際にやってみました。
実際にやってみたもの

回文判定
ABCDEFを並べ替えてできる文字列を辞書順で出力する
再帰メソッドでカウントダウン
再帰メソッドでカウントアップ
全ての目が出るまでサイコロを振り続ける
両替をする
ファイルの各行に行番号を右揃えでつけて出力する
標準入力の各行に行番号を右揃えでつけて出力する

回文判定

def palindrome?(seed)
  str=seed.downcase.split(//)
  until str.length <= 1
    return false unless (str.shift == str.pop)
  end
  true
end
p palindrome?("abcdeedcba") #=>true
p palindrome?("abcdeecba")  #=>false
p palindrome?("detartrated")#=>true
#日本語の場合は辞書を作らないとダメかもしれない。

ABCDEFを並べ替えてできる文字列を辞書順で出力する

ABCDEFの並び替えてできる文字列は6!で120通りあるので、ABCDだけにします。

class A
  def initialize seed
    @result = []
    @seed = seed.sort!
  end

  def run
    f([], @seed)
    return @result
  end

  def f(content, rest)
    if rest.size == 1
      @result << (content.concat(rest)).join
      return
    end
    rest.each do |e|
      r = rest.dup
      c = content.dup
      c << (r.delete e)
      self.f(c,r)
    end
  end
  attr_reader :result
end

a=A.new(%w{ A B C D})
a.run
p a.result
#=>["ABCD", "ABDC", "ACBD", "ACDB", "ADBC", "ADCB", "BACD", "BADC", "BCAD", "BCDA", "BDAC", "BDCA", "CABD", "CADB", "CBAD", "CBDA", "CDAB", "CDBA", "DABC", "DACB", "DBAC", "DBCA", "DCAB", "DCBA"

もうちょっときれいに書けそうな気がするんだけど…、分かりません><

再帰メソッドでカウント(アップ/ダウン)

def coutdown(n, to=0)
  return if n <= to
  puts(n)
  coutdown(n-1, to)
end

def coutup(n, to)
  return if n > to
  puts(n)
  coutup(n+1, to)
end

全ての目が出るまでサイコロを振り続ける

これは意外と楽しかったです。(classを作る練習にもなるかもしれません)

class Dice
  def initialize()
    @count = 0
    @result = []
    @content = (1..6).to_a
  end
  
  def roll
    if @game_set
      puts "already the game was end (count=#{@count})"
      return false
    end
    @count += 1
    shuffle
    x = @content.first
    check(x)
    return x
  end
  
  private
  def check(x)
    @result << x unless @result.find{ |e| e == x}
    game_set(x) if @result.size == 6
  end

  def game_set(x)
    print "game-set! last is .."
    @game_set = true
  end

  def shuffle
    @content.each_index do |i| j = rand(i+1)
      @content[i], @content[j] = @content[j], @content[i]
    end
  end
end

s = Dice.new
while x=s.roll
  print "#{x},  "
end
s.roll
s.roll

#=>
# 3,  4,  5,  6,  6,  5,  3,  5,  3,  6,  2,  game-set! last is ..1,  already the game was end (count=12)
# already the game was end (count=12)
# already the game was end (count=12)

両替をする

ハッシュで返した方がいいのかな?

class Exchanger
  def initialize
    @kinds = [10000, 1000, 500, 100, 50, 10, 5, 1]
  end
  
  def exchange(money)
    result = Array.new(@kinds.size,0)
    @kinds.each_with_index do |e, i|
      next if money < e
      result[i] = money / e
      money = money % e
    end
    Hash[*(@kinds.zip result).flatten!]
  end
end
ec = Exchanger.new
p ec.exchange(379)
p ec.exchange(5772234)

#=>
# {5=>1, 500=>0, 50=>1, 1=>4, 100=>3, 10000=>0, 10=>2, 1000=>0}
# {5=>0, 500=>0, 50=>0, 1=>4, 100=>2, 10000=>577, 10=>3, 1000=>2}

各行に行番号を右揃えでつけて出力する

def f str
  seed = str.split("\n")
  max_width = seed.sort_by{ |e| e.length}.last.length
  seed.each_with_index{ |e,i| puts e.concat(" "*(max_width-e.length)).concat(i.to_s)}
end

#=>
# aiueo               0
# kakikukekosasisuseso1
# tatituteto          2
# naninuneno          3
# yayuyo              4

さすがにこれだと効率が悪そう。
(特に最大の文字数をみつける処理に無駄が多すぎ)
ということこんな感じに変更。

def f2 str
  max_width = search_max(str)
  str.each_with_index{ |e, i| puts e.gsub(/\n/){(" "*(max_width - e.length)).concat(i.to_s+$&)}}
end

def search_max(str)
  str.inject(0){ |max, e| max = e.length if max < e.length; max}
end

でも、日本語にすると文字数がおかしくなりそう。(番号の位置がおかしくなる)
意外と難しいです><

追記

日本語じゃなくても番号の位置がおかしくなっている…
右揃えの意味を勘違いしていました><

def f3(str)
  max_length = str.inject(0){ |re, e| re += 1}.to_s.length
  str.each_with_index{|e, i| puts "#{" "*(max_length - i.to_s.length)}#{i}: #{e}"}
end

#=>
# 0: aiueo
# 1: kakikukekosasisuseso
# 2: tatituteto
# 3: naninuneno
# 4: yayuyo

やってみて分かったこと

  • 一度実際に書いてみないと、それが準備運動のメニューに適しているかどうか分からない。
  • 簡単に見えるものでも、意外といろいろなことに気づく。
  • 簡単そうなものでもやってみるとけっこう時間がかかる。
  • メニューは2つに分けられるかもしれない。
    • 言語固有の内容
    • 汎用的な内容(思考能力を問うような問題)

後で付け足す