google, short code
ちょっと頭の体操がてらコードゴルフ。
今回試してみるのは、"google"という問題。
anarchy golf - google
stdinからの入力の数だけ'o'を含む"google"という文字列を出力するのです。
ほとんどそのままのはずのコードで28Byte。
で、現在の最短コードが26Byte。いきなりギリギリのところまできました。
まぁー、そんなに多様性もないからねー。
ふと思ったのだけど、新しく言語を勉強するときにコードゴルフはいいんじゃないかと思った。というか、サンプル集をまとめておいて、それを全て書き換えるところからはじめる、という勉強法。
たとえば、
- ファイルを読み込み、それを全て標準出力に出力する
- ファイルを読み込み、それを全て新規ファイルに出力する
- ファイルを読み込み、行を正規表現でチェックして書き換える
- 標準入力を読み、全て標準出力に出力する
- 標準入力を読み、全てファイルに出力する
- 特定の計算を行う
等。
そうすれば、そのあとはそれをベースにコピペして書きやすくなるし、一通りの言語の特長とか見えてくると思うし。
ちょっと時間をとって考えてみようかな。
続きを読む
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さん、に教えていただきました。
シンボル
次に気になるのが :Fizz という表記。
「プログラミング言語 Ruby リファレンスマニュアル」によるとシンボルというやつらしい。変数の名前のようなもの、と思えばとりあえず理解が進む。そういえば、Rubyでクラス変数を定義するときに使っているから、そういうことだ、と思えばいいか。でも、結局、シンボルはシンボル。そういうものとしてそのまま飲み込んでおこう。
手元の書籍によると、次のように書かれています。
Rubyのシンボルは文字列──たいていは名前──に対応する識別子です。ある名前のシンボルを作成するには、その名前の前にコロンを付けます。また、文字列リテラルの前にコロンを付けることで任意の文字列のシンボルを作成することもできます。
(「プログラミングRuby 第2版 言語編」p.280より)
- 作者: Dave Thomas,Chad Fowler,Andy Hunt,まつもとゆきひろ,田和勝
- 出版社/メーカー: オーム社
- 発売日: 2006/08/26
- メディア: 大型本
- 購入: 7人 クリック: 270回
- この商品を含むブログ (152件) を見る
ちなみに 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 #4
step 7: 剰余の比較改善
mixiの書き込みから "i%5==0" ではなくて "i%5<1" と判定する方法を知った!
なるほどー、と感心しつつ改善してアップ。
anarchy golf - FizzBuzz
これで63Byte、20位!
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
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}
うーん、どっちの改良に未来があるんだろうか?