google, short code

ちょっと頭の体操がてらコードゴルフ
今回試してみるのは、"google"という問題。
anarchy golf - google
stdinからの入力の数だけ'o'を含む"google"という文字列を出力するのです。
ほとんどそのままのはずのコードで28Byte。
で、現在の最短コードが26Byte。いきなりギリギリのところまできました。
まぁー、そんなに多様性もないからねー。

ふと思ったのだけど、新しく言語を勉強するときにコードゴルフはいいんじゃないかと思った。というか、サンプル集をまとめておいて、それを全て書き換えるところからはじめる、という勉強法。
たとえば、

  • ファイルを読み込み、それを全て標準出力に出力する
  • ファイルを読み込み、それを全て新規ファイルに出力する
  • ファイルを読み込み、行を正規表現でチェックして書き換える
  • 標準入力を読み、全て標準出力に出力する
  • 標準入力を読み、全てファイルに出力する
  • 特定の計算を行う

等。
そうすれば、そのあとはそれをベースにコピペして書きやすくなるし、一通りの言語の特長とか見えてくると思うし。
ちょっと時間をとって考えてみようかな。


以下は28Byteのgoogle Rubyコード。

続きを読む

Dancing Kids, short code #0

なんか面白そうなcode golfないかな、と思い、「anarchy golf」を眺めていて見つけたのが、この "Dancing Kids"
完了したときのビジュアルとかが楽しげでいい感じだし、最短コードを出しているのが一人、というのがなんか可能性を感じてしまう。
問題タイトル"Dancing Kids" とか説明文 "Let's dancing"の英語を見ると、これを考えたのは日本人??変な英語だよね?
とか考えつつ、とりあえず改行ありで短くしようという意識を持たずに書いた普通のコードが以下のような感じ。

i=gets.chomp
if i=='left'
  puts " ('-')/\n /|_|\n  | |"
elsif i=='right'
  puts "\\('-')\n  |_|\\\n  | |"
elsif i=='right&left'
  puts "\\('-')/\n  |_|\n  | |"
end

入力の判定部分と出力の最適化がポイントかな。
今日はもう眠いので、短くするのは明日以降。

配列を整理するコード

配列をユニークにしてその個数とともに返せ - rubyco(るびこ)の日記 より。

配列をユニークにしてその個数とともに返せ。

具体的には、["foo", "bar", "foo", "baz", "bar", "foo"] を、[ ["foo", 3], ["bar", 2], ["baz", 1] ] にする。

golf - babie, you're my home

何も見ずにちょっと試行錯誤して書いた普通のコードがこちら。

s = ["foo", "bar", "foo", "baz", "bar", "foo"]
d = []
s.each{|sv|
  found = false
  d.each{|dv|
    if sv == dv[0] then
      dv[1]+=1
      found = true
      break
    end
  }
  if found == false then
    d << [ sv, 1 ]
  end
}
p d

で、元ページとか見てみた。そうか、ハッシュ使うのか。
ってか、短くするのちょっと疲れたので、これで保留。

FizzBuzz, short code #5 Final

RubyによるFizzBuzzのshort codingですが、最短コードを見つけてしまったので、これを理解したらとりあえず終了することにします。(これでアップするのは反則な気がするのでしません)
2007-05-09 (via http://d.hatena.ne.jp/yoosaki/20070516/p1
こちらにRubyの最短コード(56Byte)のコードが4つほど載せられています!!

1.upto(?d){|n|puts ["Fizz#{s=[:Buzz][n%5]}"][n%3]||s||n}
1.upto(?d){|n|s=[:Buzz][n%5];puts n%3<1?"Fizz#{s}":s||n}
1.upto(?d){|n|n%3<1&&s=:Fizz;puts n%5<1?"#{s}Buzz":s||n}
1.upto(?d){|n|puts ["Fizz%s"%s=[:Buzz][n%5]][n%3]||s||n}

4つもあるのかよ。すげーなぁー。
で、見てみても、ちょっと分からないところがあったので、内容をばらして考えてみることにしましたが、いくつかのテクニックを知らなかったので、以下にまとめてみました。

?d

まず目に付くのが 1.upto(?d) という表記。
えーっと、"?d"ってなんだ?と思ったけど、そういえば、前にチェックしてたこのサイトにも書かれていたなー、と思い実際のコードで調べてみました。

#--- what is ?d ? ---#
p ?d        #=> 100
p ?d.class  #=> Fixnum
p 100 == ?d #=> true
a=?d
p a         #=> 100

ということで、驚くべきことに ?d は、普通に 整数の100 として機能しています。手元の本を見てもよく分からなかったのだけど、一体この変数(特殊リテラル?)はなんなんだ??
(追記)
id:gan2さん、id:yoosakiさん、に教えていただきました。

?d は、'd'のASCII文字コードを返す数値リテラル、ということらしいです。
なるほど!!

シンボル

次に気になるのが :Fizz という表記。
プログラミング言語 Ruby リファレンスマニュアル」によるとシンボルというやつらしい。変数の名前のようなもの、と思えばとりあえず理解が進む。そういえば、Rubyでクラス変数を定義するときに使っているから、そういうことだ、と思えばいいか。でも、結局、シンボルはシンボル。そういうものとしてそのまま飲み込んでおこう。
手元の書籍によると、次のように書かれています。

Rubyのシンボルは文字列──たいていは名前──に対応する識別子です。ある名前のシンボルを作成するには、その名前の前にコロンを付けます。また、文字列リテラルの前にコロンを付けることで任意の文字列のシンボルを作成することもできます。
(「プログラミングRuby 第2版 言語編」p.280より)

プログラミングRuby 第2版 言語編

プログラミングRuby 第2版 言語編

このシンボルをそのまま使ってしえば、ダブルクォーテーションが要らなくなる、という仕組みが2つ目のショートコーディングテクニック。
ちなみに p ではなく puts を使わないとコロンも出力されてしまいます。

#--- what is Symbol ---#
Fizz=1975
p Fizz.class          #=> Fixnum
p "Fizz".to_sym       #=> :Fizz
p "Fizz".to_sym.class #=> Symbol
p :Fizz.id2name       #=> "Fizz"
p eval(:Fizz.id2name) #=> 1975

p :Fizz    #=> :Fizz
puts :Fizz #=> Fizz

なるほどね。

|| (論理和演算子

論理和演算子を使えば、if分岐を減らすことが出来るテクニックになります。
サンプルを以下に載せておきます。

# before.
s=nil
puts s!=nil ? s:"NOSTR" #=> NOSTR

# after.
s=nil
puts s || "NOSTR"       #=> NOSTR

演算子の左側がtrueであれば右側は処理しない、という仕組みを使っているわけですね。

配列を使いこなす

配列のうち、値が入っていない要素はnilになることを使って、うまく判定に持ち込むテクニックです。

puts ["zero"][0] #=> zero
puts ["zero"][1] #=> nil
puts ["zero"][55%5] #=> zero
puts ["zero"][55%6] #=> nil

文字列の掛け算

もう一つコードを引用します。これは最短までは至らないのですが、ちょっと面白いテクニックが含まれているので。

1.upto(100){|i|s=[[:Fizz][i%3],[:Buzz][i%5]]*'';puts s[1]?s:i}

http://d.hatena.ne.jp/yoosaki/20070510#p1 (via 2007-05-16
文字列に特定の文字列を掛け算すると、Array#joinと同等の働きをする、というもの。
これも「http://codegolf.com/boards/conversation/view/124」に書かれていますね。
テストコードと出力は以下のような感じ。

#--- Array#join ---#
a=["Fizz","Buzz","Foo","Bar"]
puts a.join('-') #=> Fizz-Buzz-Foo-Bar
puts a*'-'       #=> Fizz-Buzz-Foo-Bar
puts a*''        #=> FizzBuzzFooBar

へぇー。
とりあえずこれでFizzBuzzのshort codingは終了しますが、これだけ武器(テクニック)が揃ったら、別のショートコーディングに試してみたくなる!!

とりあえず片っ端からやってみようかな。

FizzBuzz, short code #3

仕事やゲームの合間に、ちょっとshort coding。

step 6: "if"の代わりに"&&"

ここにいくつかRubyのshort codingに関するtipsが載っている。
この中にあった次の1文が面白い。

? : operator as well as using i==0&&j+=7 instead of j+=7 if i==0

三項演算子を使うべし、という説明なのだけど、その後半も結構使える感じで、if文でチェックすると、ifの後ろに空白が一つできるけど、&&なら空白は必要ない、ということなのだ。
昨日トライしたコードの最後のやつでも適用できて、次のような感じになる。

# 元のコード.
1.upto(100){|i|s=i%3==0?'Fizz':'';s+='Buzz'if i%5==0;puts s==''?i:s}
# &&に置き換えて1byte削減!
1.upto(100){|i|s=i%3==0?'Fizz':'';i%5==0&&s+='Buzz';puts s==''?i:s}

そんな感じでコードを見渡してたら、三項演算子を使えそうに見えてきたので、ちょっと考えたらさらに短く出来ました!

1.upto(100){|i|s=i%3==0?'Fizz':'';puts i%5==0?s+'Buzz':s==''?i:s}

これで65byte、23位まで上昇!
anarchy golf - FizzBuzz

参考になりそう

このあたりのリファレンスもshort codingに参考になりそうなので、URLをメモしておく。

FizzBuzz, short code #2

昨日の続き。

step 4: upto

ちょっとイテレータ周りのことを調べていたら upto という繰り返しの構文を発見した。
http://www.namaraii.com/rubytips/?%C0%A9%B8%E6%B9%BD%C2%A4#l9
これでさらに2byte短くなって、73byte。

1.upto(100){|i|puts i%3==0?i%5==0?"FizzBuzz":"Fizz":i%5==0?"Buzz":i.to_s}

anarchy golf - FizzBuzz

順位は変わらず。
あと気になるところは、

  • i%5==0 の判定を2回やっているところ
  • i.to_s
  • 文字列の表示のところ

の3つ。

step 5: putsの引数

よく考えたら、i.to_s は必要ない、ということにようやく気づいて、修正して、68byte達成。

1.upto(100){|i|puts i%3==0?i%5==0?"FizzBuzz":"Fizz":i%5==0?"Buzz":i}

これで、30位にアップ!
anarchy golf - FizzBuzz

あと、他の方法も試してみたけど、これも68byte。

1.upto(100){|i|s=i%3==0?'Fizz':'';s+='Buzz'if i%5==0;puts s==''?i:s}

うーん、どっちの改良に未来があるんだろうか?