Kasasagi’s memorandum

JavaとかProcessingとか。最近はAtcoderとか。

授業の課題でコードゴルフをした

こんばんは、かささぎです。

授業でLZSS符号を復号するプログラムを作れって課題が出ました。
割と単純にできると感じ、そのまま解くのも面白くないのでコードゴルフをしてみることにしました。

コードゴルフとはどれだけソースコードを短く書けるかを競う競技です。
メソッドが豊富で短く書きやすいといわれるRubyで挑戦することにしました。


制約


ソースコード

コードの短縮においては、こちらのページを参考にさせて頂きました→RubyのコードゴルフのTips - Qiita

r="";File.open("test1.enc","rb").read.chars.map{|i|i=~/\w/?i:(i.bytes[0]-128).divmod(4)}.each{|v|r+=v.size==1?v:(r[v[0]..-1]*4)[0,v[1]+1]};puts r

145bytesで書けました。思ったより短くなりました。


解説

LZSSのアルゴリズムではなく、コードゴルフに重きを置いて解説していきます。
作成したコードをわかりやすく書くとこんな感じになります。

r = ""
f = File.open("test2.enc","rb")

d = f.read.chars
d.map!{|i| i.match(/\w/) ? i : (i.bytes[0]-2**7).divmod(4) }

d.each do|v|
	
	r+=v if v.size==1
	if v.size==2 then
		l = r[v[0]..-1].to_s * 4
		r += l[0,v[1]+1]
	end
end
puts r

変数rに復号で得られた文字を追加していって、最後に出力するという構造です。

File.open()の第二引数は文字コードを指定しています。Windows文字コードでは"rb"とする必要があります。
UNIX系では"r"のみでいいようなので、もう1byte短縮できますね。

Windowsでは改行の文字コードが2bytesらしいので、セミコロンで連結することによって1byteで抑えました。
RubyでのコードゴルフWindowsは向いてなさそうですね。。。

取り出した文字が\w(単語を構成する文字)に引っかかるかどうかで、dに格納するデータを分岐します。
divmodメソッドは割り算の商と余りをリストとして出力してくれます。このメソッド好き。
ちなみに、matchメソッドは=~と置き換えることができたので、6bytes短くできました。

あとはdのデータをeachでループして、データサイズごとに処理を変えています。
3項演算子を用いてかなり短く書くことができました。

3項演算子は、

  • 可読性が死ぬから悪だ
  • 絶対に使うな
  • if,else大量に使うよりはいいだろ
  • 1行で収まる範囲ならOK
  • PHPではやめとけ

など論争が続いていますが、コードゴルフにおいては正義です。たぶん。


感想

始めてコードゴルフをやってみたのですが、結構楽しかったです。

Rubyは多数のメソッドがあり、短いものに置き換えれることがあるという点で、いろいろ調べたりして実装するのが勉強になりました。
またRubyでは()やスペースをかなり省略できることを知り、驚きました。たぶんこれをやらなかったら気づきませんでした。

もとはこちらの記事→Ruby FizzBuzz最短コードメモ (51bytes) ネタバレ注意 - Qiitafizzbuzzのコードを見て、なんだこれってなって興味を持ちました。
このコードほど可読性を落とすことはできなかったのですが(可読性を落とすことが目的ではない)、コードゴルフのおもしろさというものを実感できた気がします。

まだここを改善できるみたいな点がありましたら、教えてください。


それでは。