2009年07月01日

久しぶりのゴルフに挑戦。

Rubyist Magazine - るびまゴルフ 【第 6 回】

seq という Unix コマンドがあります。このコマンドを使うと、数値を2つ引数にして、その間にある数を出力することができます。例えば 3 と 5 を引数として実行すると

% seq 3 5
3
4
5

というような実行結果になります。

今回はこのコマンドを実装してみて下さい。入力はコンマで区切られて標準入力から与えられるものとします。例えば

3,5

という入力に対して

3
4
5

と出力するようにして下さい。例えば

a,b=gets.split(',').map{|v|v.to_i}
a.upto(b){|i|puts i}

というようなコードと同じ出力が得られるようにして下さい。パーは 50Bytes とします。

また余力があれば、入力が

3 5

と空白区切りで与えられる場合、 seq のように引数として与えられる場合、つまり ARGV を使う場合も考えてみて下さい。

(以下、私の回答)




3問目の、値がARGV(コマンドライン引数)として与えられる場合から取り組む。

例えば上記の例では、3と5という数から「3 4 5」という一連の数を生成するのに、Integer#uptoメソッドを利用していた。まずはこれを置き換えることを考えた。

upto以外で、両端の値を与えることで一連の数を生成するのに利用できるものとして、Rangeオブジェクトがある。両端が整数のRangeオブジェクトは、配列に変換することで、間の値をすべて明示した配列となる(例:(3..5).to_a # => [3, 4, 5])。

よって、Rangeを利用したコードを愚直に書けばこうなる。

puts ($*[0].to_i..$*[1].to_i).to_a (34bytes)

$*はARGVと等価である。また、putsに配列を与えると、各要素を1行ずつ表示することを利用している。

ただ、to_iを2回も使っているので無駄が多く見える。これは力技で解消できた。

puts eval($*.join"..").to_a (27bytes)

引数から「3..5」のような文字列を生成してしまい、それにevalをかけている。

さらにもう一歩縮められて

puts eval($*.join"..").map (26bytes)

となる。mapはブロックを省略するとmap{ |x| x }とみなされる(すなわちto_aと等価である)ことを利用している。


続いて1・2問目。これは同じ方針を適用する。

標準入力から来る文字列が "3,5" や "3 5" のような形なので、これらの区切り文字を ".." に置換するのを効率よく行いたいのだが、自分が考えた中では愚直な方法以上でよいものが思いつかなかった。

puts eval(gets.sub",","..").map (31bytes)
puts eval(gets.sub" ","..").map (31bytes)

ちなみに "," 区切りの場合についてはこんな方法も思いついたが、1文字増えた。

puts eval("Range.new "+gets).map (32bytes)



maraigue at 12:07コメント(0)トラックバック(0)プログラミング  この記事をクリップ!

トラックバックURL

コメントする

名前
URL
 
  絵文字
 
 
livedoor プロフィール
  • ライブドアブログ