技術メモ

プログラミングとか電子工作とか

Elixir入門(第六章 文字列とか)

f:id:ysmn_deus:20190122112104p:plain

どうも、靖宗です。
ちょっと期間が空きましたが続けて行きます。まだ楽しそうなところまで行けてませんし、飽きた訳ではありません!
第六章は「Binaries, strings, and charlists」だそうです。たぶん、文字列の扱いとかそういう内容・・・

導入部分に書かれている

iex> string = "hello"
"hello"
iex> is_binary(string)
true

基本型の章で記載がありましたが、確かにスルーしてました。
この辺の詳細とか、連結リストとかで言及のあったシングルクオートの文字列などの説明です。

UTF-8Unicode

基本的な話になりますが、文字はぱそこん上では数字で管理されてます。
このへん曖昧な理解しかしてませんでしたが

  • ASCIIコード=昔の英字を表す文字コード。1バイト
  • UnicodeUTF-8とかそのへんの規格。UTF-8は1~4バイトの可変長。ASCIIの上位互換。

という認識。間違ってたらすんません。

基本的にElixirでは文字列はUTF-8エンコードを使用しているので

iex> string = "hełło"
"hełło"
iex> byte_size(string) # "h": 1byte "e": 1byte "ł": 2byte "ł": 2byte "o": 1byte =合計7byte
7
iex> String.length(string)
5

なるほど、可変長コードなのでASCIIに収まってない様な文字に対しては2バイト割り当てられています。
文字コード?という演算子(?)のようなもので得られるようです。

iex> ?a
97 # 255以下なので1バイト
iex> 
322 # 255以上なので2バイト

あとはString.codepoints("hełło")とかやるとUTF-8で分離されたリストが得られる。使いどころが不明なので次!

Binaries (and bitstrings)

('ω')。o(bitstrings????????????)

バイナリ列のような新しい概念が出てきました。

iex> <<0, 1, 2, 3>>
<<0, 1, 2, 3>>
iex> byte_size(<<0, 1, 2, 3>>)
4

<<>>でバイナリ列を表現できるようです。Pythonでいうb"hogehoge"を文字コードリストとして取り扱ってる感じ?
文字列の連結で使用した<>も使える様です。

iex> <<0, 1>> <> <<2, 3>>
<<0, 1, 2, 3>>

文字列の文字コード列を得るときに<<0>>と結合して見たりするようです。

iex> "hełło" <> <<0>>
<<104, 101, 197, 130, 197, 130, 111, 0>>

あ、"ł"が溢れてるような・・・
基本的にバイナリ列ではきちんと表記しないとあふれるっぽいです。
色々変換する表現方法があるみたいです。

iex> <<255>>
<<255>>
iex> <<256>> # truncated
<<0>>
iex> <<256 :: size(16)>> # use 16 bits (2 bytes) to store the number
<<1, 0>>
iex> <<256 :: utf8>> # the number is a code point
"Ā"

でも最後の所が謎

iex> <<256 :: utf8, 0>>
<<196, 128, 0>>

('ω')。o(????????????)o。('ω')
まったく分からなかったので色々調べてたのですが、どうやらUnicodeの仕様などをきちんと理解してないと言及できないかも。
2バイト文字は基本的にCx, xxで表現されるので、ĀUnicodec480、おそらく0xc4 = 1960x80 = 128<<196, 128>>に変換された。
あってるのかな・・・

<<1:: size(16)>>とかで、バイナリ列の格納サイズを指定出来た。8の倍数じゃないバイナリ列はどうなるのか?(8の倍数なら1バイト、2バイト、・・・のバイナリ列になる)

iex> <<1 :: size(1)>>
<<1::size(1)>>
iex> <<2 :: size(1)>> # truncated
<<0::size(1)>>
iex> is_binary(<<1 :: size(1)>>)
false
iex> is_bitstring(<<1 :: size(1)>>)
true
iex> bit_size(<<1 :: size(1)>>)
1

バイナリではなくbitstring(ビット文字?)というものになるそうで。

バイナリ列やビット文字列でもパターンマッチは使える。

iex> <<0, 1, x>> = <<0, 1, 2>>
<<0, 1, 2>>
iex> x
2

対応するバイナリのサイズが不明の場合(utf-8などは1~4で変わるし)、::binaryと書けばマッチするようです。

iex> <<0, 1, x :: binary>> = <<0, 1, 2, 3>>
<<0, 1, 2, 3>>
iex> x
<<2, 3>>

文字列のパターンマッチも同様の結果とのこと。

iex> "he" <> rest = "hello"
"hello"
iex> rest
"llo"

これを説明するために文字列の仕様を話していた?たしかにこのパターンマッチは便利かも。

Charlists(キャラクタリスト?文字リスト?)

シングルクオートで表される文字列はキャラクタリストとして文字列とは明確に区別されているようです。
どうも区別されているのはErlangの仕様?古いライブラリなどを仕様しているとバイナリ列(=文字列?)が使えないみたいです。
そんなときの関数が用意されているようで、to_string/1to_charlist/1でお互い変換できるようです。

iex> to_charlist "hełło"
[104, 101, 322, 322, 111]
iex> to_string 'hełło'
"hełło"
iex> to_string :hello
"hello"
iex> to_string 1
"1"

atomや数字も文字列にできるのか。

キャラクタリストは文字列ではないので演算子<>が使えない。
その代わりリストなので++が使える。

iex> 'this ' <> 'fails'
** (CompileError) iex:2: invalid literal 'this ' in <<>>
    (elixir) src/elixir_bitstring.erl:19: :elixir_bitstring.expand/6
    (elixir) src/elixir_bitstring.erl:12: :elixir_bitstring.expand/4
    (elixir) expanding macro: Kernel.<>/2
    iex:2: (file)
iex> 'this ' ++ 'works'
'this works'
iex> "he" ++ "llo"
** (ArgumentError) argument error
    :erlang.++("he", "llo")
iex> "he" <> "llo"
"hello"

Erlangも勉強しなきゃどっかで躓くのかなぁ。
躓いてから考えるか!