原です。

>わたなべです。

>while (lines = handles.collect{|h| h.gets}).find{|x| x}
>  print format % lines.collect{|l| l.to_s.chomp}, "\n"
>end
>
>結局2つのときと同じものになってしまった。

というわけで、テキストの縦(横?)マージの正解は、以上まとめると

-----^ merge1.rb
format = (["%-35s"] * ARGV.size).join(" | ")
handles = ARGV.map{|f| open(f)}
while (lines = handles.collect{|h| h.gets}).find{|x| x}
  print format % lines.collect{|l| l.to_s.chomp}, "\n"
end
-----$ merge1.rb

ということになりますかね。


ところで、やっぱりちょっと引っかかるのは、読み終えたファイルハンドルに
対する gets と、ファイルハンドルの close です。そのあたり、Ruby がうま
くやってくれるので、このケースでは考えなくてもいいんですが、思考実験と
して、もうちょっと一般的なストリームっぽいオブジェクトに対しても有効な
アルゴリズムはどうなるかって事も考えて見たい、、、。


で、その解1:

-----^ merge2.rb
format = (["%-35s"] * ARGV.size).join(" | ")
suffixes = 0...ARGV.size
handles = ARGV.map{|f| open(f)}
survivals = suffixes.to_a
until survivals.empty?
  puts format % suffixes.map{|i|
    if !survivals.include?(i)
      ""
    elsif line = handles[i].gets
      line.chomp
    else
      handles[i].close
      survivals.delete(i)
      ""
    end
  }
end
-----$ merge2.rb

解2:

-----^ merge3.rb
class Handle
  attr_reader :alive
  def initialize(h)
    @h = h
    @alive = true
  end

  def next
    if !@alive
      ""
    elsif line = @h.gets
      line.chop
    else
      @h.close
      @alive = false
      ""
    end
  end
end

format = (["%-35s"] * ARGV.size).join(" | ")
handles = ARGV.map{|f| Handle.new(open(f))}
while handles.find{|h| h.alive}
  puts format % handles.map{|h| h.next}
end
-----$ merge3.rb

短くなくても筋が通っていればいいんで、「これが決定版!」みたいのはない
ですかね?