久しぶりにrubyで遊ぶ

ruby1.9便利

require 'enumerator'がいらなくて済むし、to_enumとかもいらない。

# 行番号付きprint
def print_with_number io
  io.each_line.with_index{ |x,i| puts("#{i}:#{x}")}
end
print_with_number("test\nfoo\nbar")
# 0:test
# 1:foo
# 2:bar

# deep-reverse
class Array
  def deep_reverse
    self.reverse_each.map{ |e| (e.respond_to?(:map)? e.deep_reverse : e)}
  end
end

a = [1,2,[3,[4]],[5,[6,[7,8]]],9]
a.deep_reverse  # => [9, [[[8, 7], 6], 5], [[4], 3], 2, 1]


# split evens and odds
class Array
  def mypartition
    self.inject([[],[]]) { |re,x| ((yield(x))? re[0] : re[1]).push(x); re}
  end
end

(1..10).to_a.mypartition{ |x| x % 2 == 1} # => [[1, 3, 5, 7, 9], [2, 4, 6, 8, 10]]


# cross-product
def cross_product(*args)
  if args.length == 1
    args.first.map{ |x| [x]}
  else
    cross_product(*args[1..-1]).inject([]) do |re,xs| 
      re.concat args.first.map{ |y| [y,*xs]}
    end
  end
end
cross_product(%w{x y z },[1,2,3], %w{ a b c}) # => [["x", 1, "a"], ["y", 1, "a"], ["z", 1, "a"], ["x", 2, "a"], ["y", 2, "a"], ["z", 2, "a"], ["x", 3, "a"], ["y", 3, "a"], ["z", 3, "a"], ["x", 1, "b"], ["y", 1, "b"], ["z", 1, "b"], ["x", 2, "b"], ["y", 2, "b"], ["z", 2, "b"], ["x", 3, "b"], ["y", 3, "b"], ["z", 3, "b"], ["x", 1, "c"], ["y", 1, "c"], ["z", 1, "c"], ["x", 2, "c"], ["y", 2, "c"], ["z", 2, "c"], ["x", 3, "c"], ["y", 3, "c"], ["z", 3, "c"]]

# powerset
def powerset(s)
  if s.empty?
    [[]]
  else
    child, x = powerset(s[1..-1]), s.first
    child.concat(child.map{ |y| [x,*y]})
  end
end
powerset([1,2,3]) # => [[], [3], [2], [2, 3], [1], [1, 3], [1, 2], [1, 2, 3]]

# permutation
def permutation(arr, n)
  if n == 1
    arr.map{ |x| [x]}
  else
    arr.inject([]) do |re,x|
      re.concat( permutation(arr.reject{ |y| x == y},n-1).map{ |ys| [x,*ys]})
    end
  end
end

permutation([1,2,3,4],2) # => [[1, 2], [1, 3], [1, 4], [2, 1], [2, 3], [2, 4], [3, 1], [3, 2], [3, 4], [4, 1], [4, 2], [4, 3]]
arr = (1..6).to_a
permutation(a,2) == a.permutation(2).map{ |e| e}  # => true

def combination(arr, n)
  case 
  when arr.nil? then []
  when n == 1 then arr.map{ |x| [x]}
  else
    arr.dup.inject([]) do |re,x|
      re.concat( combination(arr.reject!{ |y| x == y},n-1).map{ |ys| [x,*ys]})
    end
  end
end

arr = (1..6).to_a
combination(arr.dup,3) # => [[1, 2, 3], [1, 2, 4], [1, 2, 5], [1, 2, 6], [1, 3, 4], [1, 3, 5], [1, 3, 6], [1, 4, 5], [1, 4, 6], [1, 5, 6]]
combination(arr.dup,2) == arr.combination(2).map{ |e| e}  # => true

combination,permutation,partitionはEnumerableのmethodの中にあるので、defaultで使える。
combinationは、元のArrayを破壊してしまうからあまり綺麗じゃないかもしれない。