授業の課題でコードゴルフをした
こんばんは、かささぎです。
授業でLZSS符号を復号するプログラムを作れって課題が出ました。
割と単純にできると感じ、そのまま解くのも面白くないのでコードゴルフをしてみることにしました。
コードゴルフとはどれだけソースコードを短く書けるかを競う競技です。
メソッドが豊富で短く書きやすいといわれるRubyで挑戦することにしました。
制約
- 参照部5bit、符号化部2bitで符号化されたLZSS符号(Algorithms with Python / LZ77 符号 (LZSS 符号))を復号する
- 暗号化したデータをアスキーコードで出力したファイル"test1.enc"を読み取り、復号を行う
- 出力にはputsを用いる(pやppはデバッグ用のため)
- Windows環境
ソースコード
コードの短縮においては、こちらのページを参考にさせて頂きました→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) ネタバレ注意 - Qiitaでfizzbuzzのコードを見て、なんだこれってなって興味を持ちました。
このコードほど可読性を落とすことはできなかったのですが(可読性を落とすことが目的ではない)、コードゴルフのおもしろさというものを実感できた気がします。
まだここを改善できるみたいな点がありましたら、教えてください。
それでは。