Rubyでビット演算を扱ったとき、個人的に便利あるいは不便だなと思ったところの話。また、それに対して最近話題のCrystalを使えばいい感じになるのでは、ということを書いてみました。
うれしいところ
Rubyでビットや、10進数以外を扱う場面において、やはりうれしいのはコードが読みやすくなる点だと思います。数値リテラルにはアンダースコアを含めることができるので、64ビットとかそういう長いものでも 0xFFFF_FFFF_FFFF_FFFF
のようにわかりやすく書けるし、異なる進数間のやりとりも数値と文字列間のやりとりもとても容易です。
"1111".to_i(2) #=> 15
15.to_s(2) #=> "1111"
"%b" % 0b1010 #=> "1010"
つらいところ
ちょっとつらいなと思ったのは、符号なし整数を扱いたいときの左シフトと反転です。
例えば、 0b1111_1111
を4つ左にシフトした場合 0b1111_0000
が返ってくることを、また、 0b0101_0101
のNOTは 0b1010_1010
となることを心のどこかで期待してしまいます。しかし実際には
"%b" % (0b1111_1111 << 4)
#=> "111111110000"
"%b" % (~0b0101_0101)
#=> "..10101010"
という感じになります(符号なしと指定できないし、型がないので当たり前といえば当たり前ですが…)。NOTの方に至っては左側に無限に1ビットが立ってることを表現するための謎の文字列が返ってきます。
一応解決策として、ほしい大きさの分だけマスクをかければなんとかなるのですが、これを毎回書くのは煩わしい。
"%b" % ((0b1111_1111 << 4) & 0b1111_1111)
#=> "11110000"
"%b" % (~0b0101_0101 & 0b1111_1111)
#=> "10101010"
そこでCrystalを使ってみる
解決策として別の言語を使うというのはどうなんだという気もしますが、CrystalはRubyとほぼ同じ文法なので移行は可能だと思います。
もうみなさんご存知かと思いますがCrystalには型があるので、(Rubyのように表現力が必要でなおかつ)ビット演算を扱いたいという場面において、しあわせになる手段のひとつとなるのではないのでしょうか。
先程の左シフト、反転の話もCrystalでは符号なし8ビット整数のサフィックスをつけてやるだけで解決します。
"%b" % (0b1111_1111_u8 << 4)
#=> "11110000"
"%b" % (~0b0101_0101_u8)
#=> "10101010"
つよい…
おわりに
Crystal 応援しています。
参考 (本投稿で使用したCrystalのバージョンは 0.7.5 でした)
http://crystal-lang.org/docs/syntax_and_semantics/literals/integers.html