この記事は Elixir その2 Advent Calendar 2020 14日目です。
先日は@torifukukaiou さんの「GigalixirでPORTを4000以外の値にするのはだめよ (Elixir)」でした。
@torifukukaiou さん13日間お疲れさまでした。しばらく休憩してください。
Elixir楽しいです。少しでも多くの方とその楽しみを共有したいものです。日頃、色んな人のメモにお世話になっているので、僕も勉強した内容をどんどんアウトプットしていこうと思います。
はじめに
Elixirではシングルクオート('Hello'
)とダブルクオート("Hello"
)が同義ではないと何となく分かっていながら、シングルクオートを使う機会が今まで全くありませんでした。使わないので結局、よくわからないまま放置してありました。
が、NervesでIoTのシリアル通信の勉強をしていて戸惑うことがあったので、学んだ内容を軽くメモをしました。
因みにNervesでのIoT開発/電子工作はかなり楽しいので興味のある方には#NervesJP Advent Calendar 2020をおすすめします。
戸惑ったこと
シリアル通信でStringの文字一つ一つをバイトとして送信する際に、StringからCharlistへの変換が必要であること。それを理解してなかった。
# データ送信関数イメージ
iex> send_byte = fn x -> IO.puts("#{x}を送信") end
# 失敗イメージ
iex> "Hello" |> Enum.each(send_byte)
** (Protocol.UndefinedError) protocol Enumerable not implemented for "Hello" of type BitString. This protocol is implemented for the following type(s): RingLogger.CircularBuffer, HashSet, Range, Map, Function, List, Stream, Date.Range, HashDict, GenEvent.Stream, MapSet, File.Stream, IO.Stream
(elixir 1.11.2) lib/enum.ex:1: Enumerable.impl_for!/1
(elixir 1.11.2) lib/enum.ex:141: Enumerable.reduce/3
(elixir 1.11.2) lib/enum.ex:3461: Enum.each/2
# 成功イメージ
iex> 'Hello' |> Enum.each(send_byte)
72を送信
101を送信
108を送信
108を送信
111を送信
:ok
""
と''
の違いを確認
マッチ演算子=
"Hello" = 'Hello'
** (MatchError) no match of right hand side value: 'Hello'
比較演算子==
"Hello" == 'Hello'
false
IO.inspect
iex> IO.inspect("Hello")
"Hello"
iex> IO.inspect('Hello')
'Hello'
なんの情報も得られませんでした。
iexのi
ヘルパー関数
iex> i "Hello"
Term
"Hello"
Data type
BitString
Byte size
5
Description
This is a string: a UTF-8 encoded binary. It's printed surrounded by
"double quotes" because all UTF-8 encoded code points in it are printable.
Raw representation
<<72, 101, 108, 108, 111>>
Reference modules
String, :binary
Implemented protocols
Collectable, IEx.Info, Inspect, List.Chars, String.Chars
iex> i 'Hello'
Term
'Hello'
Data type
List
Description
This is a list of integers that is printed as a sequence of characters
delimited by single quotes because all the integers in it represent printable
ASCII characters. Conventionally, a list of Unicode code points is known as a
charlist and a list of ASCII characters is a subset of it.
Raw representation
[72, 101, 108, 108, 111]
Reference modules
List
Implemented protocols
Collectable, Enumerable, IEx.Info, Inspect, List.Chars, String.Chars
具体的な違いが見えてきました。
# a string: a UTF-8 encoded binary
"Hello" = <<72, 101, 108, 108, 111>>
# a list of integers
'Hello' = [72, 101, 108, 108, 111]
データタイプが全く違うんですね。
is_binary("Hello") #=> true
is_binary('Hello') #=> false
is_bitstring("Hello") #=> true
is_bitstring('Hello') #=> false
is_list("Hello") #=> false
is_list('Hello') #=> true
"Hello"
を'Hello'
に変換
"Hello" |> to_charlist #=> 'Hello'
<<72, 101, 108, 108, 111>> |> to_charlist #=> 'Hello'
'Hello'
を"Hello"
に変換
Kernel.to_string
で可能。
'Hello' |> to_string #=> "Hello"
[72, 101, 108, 108, 111] |> to_string #=> "Hello"
IO.inspect
のオプションを変えてみる
iex> IO.inspect("Hello", binaries: :as_binaries)
<<72, 101, 108, 108, 111>>
"Hello"
iex> IO.inspect('Hello', charlists: :as_lists)
[72, 101, 108, 108, 111]
'Hello'
もう一回正しい理解でデータ送信
iex> send_byte = fn x -> IO.puts("#{x}を送信") end
#Function<44.97283095/1 in :erl_eval.expr/5>
iex> "Hello" |> to_charlist |> Enum.each(send_byte)
72を送信
101を送信
108を送信
108を送信
111を送信
:ok
さいごに
因みにやりたかったことはLCDに書き込むときのこういうやつです。
ここまでまとめてみて、やっと基本が分かってきました。更にElixir - Getting Startedの例のようなUnicodeに対応する場合は文字によっては隠れたバイトがあるので、もう少しややこしい場合があるようです。
Elixir その2 Advent Calendar 2020 初日の[87, 101, 32, 97, 114, 101, 32, 116, 104, 101, 32, 65, 108, 99, 104, 101, 109, 105, 115, 116, 115, 44, 32, 109, 121, 32, 102, 114, 105, 101, 110, 100, 115, 33]
に改めて戻ってみると、おもしろいです。
明日は「[Elixir] GenServerのCallとCast」です。引き続き、Elixirを楽しみましょう。