雰囲気でエンジニアやっています。
トリなんですが昨日熱を出して最高なイブを過ごし、更にクリスマスであることすら忘れていたので、今サンタさんもびっくりなくらい大慌てなわけです。
ってなわけで何番煎じかわからないけど、Elixirの不思議(?)な文字の世界で遊びます。
彼女(妄想)からL○NE来た
77 101 114 114 121 67 104 114 105 115 116 109 97 115
Elixirって時点で勘のいい人はすぐに何が書いてあるかわかりますね。
こんなL○NEを送ってくれるバイナリアンな彼女がいたら嬉しいですね。
わからなかったらElixirをインスコしてReplを起動して、可愛い彼女のために何が書いてあるか読み解きましょう。
$ iex
Erlang/OTP 22 [erts-10.6.1] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [hipe] [dtrace]
Interactive Elixir (1.9.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> <<77, 101, 114, 114, 121, 67, 104, 114, 105, 115, 116, 109, 97, 115>>
"MerryChristmas"
素敵なメッセージ!
<<>>
でバイナリの世界へ簡単にダイブ
大体どのプログラミング言語でもある、Fileを読んだりするやつを見てみます。
File.read("hoge.wav")
# {:ok,
# <<82, 73, 70, 70, 252, 94, 66, 2, 87, 65, 86, 69, 102, 109, 116, 32, 16, 0, 0,
# 0, 1, 0, 2, 0, 68, 172, 0, 0, 16, 177, 2, 0, 4, 0, 16, 0, 100, 97, 116, 97,
# 236, 93, 66, 2, 0, 0, 0, 0, ...>>}
ここでWaveファイルをせっかく読んだのでちらっとヘッダを覗いてみます。Waveファイルの構造については別途参照ください。
defmodule Wave do
def parse(file) do
File.read(file)
|> header()
end
def header({:error, _}), do: {:error, "読み込み失敗"}
def header({:ok, body}) do
<<riff :: binary-size(4), size :: unsigned-size(32), wave_header :: binary-size(4), fmt_chunk :: binary-size(4), rest :: binary>> = body
IO.inspect riff
IO.inspect size
IO.inspect wave_header
IO.inspect fmt_chunk
IO.inspect rest
end
end
適当なWaveファイルを読ませてみます。
$ iex -S mix
iex(1)> Wave.parse("hoge.wav")
"RIFF"
4234035714
"WAVE"
"fmt "
<<16, 0, 0, 0, 1, 0, 2, 0, 68, 172, 0, 0, 16, 177, 2, 0, 4, 0, 16, 0, 100, 97,
116, 97, 236, 93, 66, 2, 0, 0, 0, 0, 0, 0, 255, 255, 1, 0, 2, 0, 254, 255,
253, 255, 3, 0, 4, 0, 252, 255, ...>>
パターンマッチ最高。
文字もバイナリ
なんです。
"a" <> "b" # "ab"
"a" <> <<0>> # <<97, 0>>
match? "a", <<97>> # true
結局その数字は何かってASCIIなんですね。
まぁこの辺はElixirのチュートリアルでも書いてあることですね。
もっと分解してみる
先程の彼女からのメッセージの頭文字 "M" でちょっと深堀りします。
"M" <> <<0>>
が <<77, 0>>
なので <<77>> == "M"
ですね(?M
だけでも調べられます)。
77
は2進数で 1001101
ですね。これをElixir的に書くと
<<0::1, 1::1, 0::1, 0::1, 1::1, 1::1, 0::1, 1::1>>
ということは
$ iex -S mix
iex(1)> <<0::1, 1::1, 0::1, 0::1, 1::1, 1::1, 0::1, 1::1>>
"M"
iex(2)> match? "M", <<0::1, 1::1, 0::1, 0::1, 1::1, 1::1, 0::1, 1::1>>
true
これだけ見るとわけがわかりませんね。
まとめ
普段はあまり触れない0/1の世界へ、意外と手軽に入っていける面白さがElixirにはあるなぁと思います。
フワッと理解していたbitとかの単位についてがクリアになるなぁと。
あとElixirのパターンマッチのおかげでこの手のコードは短く書けると思います。
前前前職ではElixirを使って結構バイナリいじって楽しんでました。そのときに作ったライブラリがいくつか公開していたはず……
意外と話が広がらなくて困りましたが、とりあえずメリークリスマス。
みなさんアドベントカレンダお疲れさまでした。