エンジニアのソフトウェア的愛情

または私は如何にして心配するのを止めてプログラムを・愛する・ようになったか

Elixir でビット列を展開する覚書

例えば右のように

11001010(2) = CA(16)

1111000011001100(2) = F0CC(16)

に Elixir で展開するための覚書です。

Elixir の内包表記は for

Elixir にも内包表記があります。他の関数型言語とくらべて内包表記っぽくない表記と感じましたが、意味するとろは確かに内包表記です。

iex> for n <- [1,2,3,4,5], do: n * n
[1, 4, 9, 16, 25]
iex> for x <- [1,3,5], y <- [2,4,6], do: {x, y}
[{1, 2}, {1, 4}, {1, 6}, {3, 2}, {3, 4}, {3, 6}, {5, 2}, {5, 4}, {5, 6}]

ちなみに for/1 はモジュール Kernel.SpecialForms で定義されています。

Elixir (と Erlang)の特徴はバイナリにも内包表記が使える点です。

iex> for <<c <- <<1, 2, 3, 4, 5>> >>, do: c * c                
[1, 4, 9, 16, 25]

個々の演算の値をバイナリにして、:into オプションを利用すれば、結果もバイナリで得ることができます。

iex> for <<c <- <<1, 2, 3, 4, 5>> >>, into: <<>>, do: <<c * c>>
<<1, 4, 9, 16, 25>>

また個々の演算の値がバイナリになっていればよいので、元のバイナリに対して長さの異なる結果を得ることができます。

iex> for <<c <- <<1, 2, 3, 4, 5>> >>, into: <<>>, do: <<c, c>> 
<<1, 1, 2, 2, 3, 3, 4, 4, 5, 5>>

加えて任意のサイズのビットで値を取り出すことができます。

iex> for <<bit::1 <- <<128>> >>, into: <<>>, do: <<bit>>
<<1, 0, 0, 0, 0, 0, 0, 0>>

バイナリはビット数を指定しない場合は 8 ビットで扱われるため、この <<bit>> は 8 ビットの値と解釈されます。 もちろんここでもビット数を指定することができるので、例えば次のようにすると元のバイナリが得られます。

iex> for <<bit::1 <- <<128>> >>, into: <<>>, do: <<bit::1>>
<<128>>

ビット列を展開する

必要な情報がそろったので、これらを踏まえて。

iex> x = 0xCA
202
iex> <<y::16>> = for <<bit::1 <- <<x::8>> >>, into: <<>>, do: << <<bit::1>>, <<bit::1>> >>
<<240, 204>>
iex> Integer.to_string(y, 16)                                                             
"F0CC"

CA(16) から F0CC(16) を得ることができました。

ビット列を文字列に展開する

Elixir の文字列はビット列です。

iex> "" == <<>>
true
iex> <<65>> == "A"
true

つまり文字列に対するいろいろな加工を内包表記を使って書くことができます。

iex> for <<c <- "hello">>, do: Integer.to_string(c, 16)
["68", "65", "6C", "6C", "6F"]
iex> for c <- [0x68, 0x65, 0x6C, 0x6C, 0x6F], into: "", do: <<c>>
"hello"

この仕組みを利用すればビット列を任意の文字列に展開することもできます。

iex> for <<bit::1 <- <<0x5A>> >>, into: "", do: if bit == 1, do: "@", else: "_"
"_@_@@_@_"

これを踏まえて。 フォントデータをキャラクターで表示してみます。

iex> [0x10, 0x28, 0x44, 0x82, 0xfe, 0x82, 0x82, 0x00] |> Enum.each(&IO.puts(for <<bit::1 <- <<&1>> >>, into: "", do: if bit == 1, do: "[]", else: "  "))
      []        
    []  []      
  []      []    
[]          []  
[][][][][][][]  
[]          []  
[]          []  

キャラクタ以外にも、たとえば 0<<0::24>>1<<0xffffff::24>> と展開すれば 2 値のデータから 24 ビットカラーのデータを作成することができます。

補足:Erlang の内包表記

Erlang にもリストとバイナリ両方の内包表記があります。こちらの方がよく見る内包表記の形式をしています。

> [X * 2 || X <- [1,2,3,4,5]].
[2,4,6,8,10]
> << <<(C* 2)>> || <<C>> <= <<1,2,3,4,5>> >>.
<<2,4,6,8,10>>
> [C * 2 || <<C>> <= <<1,2,3,4,5>>].         
[2,4,6,8,10]
> << <<(X * 2)>> || X <- [1,2,3,4,5]>>.
<<2,4,6,8,10>>

いつか読むはずっと読まない:失われたものを復元するという偉業

とうとう新種として判明し命名されました。

2019年9月現在、全身実物化石と全身復元骨格を間近で見ることができます。これはぜひ見て欲しい。

恐竜・古生物ビフォーアフター

恐竜・古生物ビフォーアフター

恐竜の魅せ方 展示の舞台裏を知ればもっと楽しい

恐竜の魅せ方 展示の舞台裏を知ればもっと楽しい