LoginSignup
8

More than 3 years have passed since last update.

Elixirで整数リストが「謎の文字」として返ってくる現象と、文字列・文字リストについて

Last updated at Posted at 2020-01-05

kokura.ex / fukuoka.ex のim_miolabと申します。

さて、Elixirで整数の数値リストを取り扱っていると、謎の文字 が返ってくることがあります。

iex
iex()> list_int = [80, 90, 110]
'PZn'

この現象にからめて、以下つらつらと書いてみます。

先に結論とまとめ

  • 整数リスト内の全要素が ASCIIコードのエスケープシーケンスや印字可能文字の範囲内 にあると、文字リスト(charlist)として出力される。

  • Elixirでは、「文字列」と「文字リスト」は区別される。

文字の囲いかた e.g. 特徴 データ型
文字列 ダブルクオート("") "hoge" バイナリ BitString
文字リスト シングルクオート('') 'hoge' 整数のリスト List
  • 関数によって、
    ・文字列を引数に期待するもの
    ・List(文字リスト)を引数に期待するもの
    とがあるので、使い分けが必要。

ざっくり検証

それでは、具体的にみていきます。

実行環境

  • Elixir ver 1.9.4
  • Windows 10 Pro
  • Bash

リスト(List)

Elixirのリスト(List) は、[ 要素, 要素, ... ] のように書きます。
(異なる型の要素も同居できます)

以下、iexで整数のリストを書いてみました。

iex
iex()> list_a = [80, 1, 1100]
[80, 1, 1100]

いちおう、型も見てみます。

iex
# 「list_a」の型を確認
iex()> i list_a
Term
  [80, 1, 1100]
Data type
  List
.
.
.

# 「list_a の最初の要素」の型を確認
iex()> i list_a |> Enum.at(0)
Term
  80
Data type
  Integer
.
.
.

リストの中身は、ちゃんとInteger(整数)型になっています。

整数リストの全要素が、ASCIIコードの印字可能範囲にあるケース

冒頭でふれた現象についてです。

iex
iex()> list_int = [80, 90, 110]
'PZn'

「先に結論とまとめ」でも書いたとおり、リスト内の要素がASCIIコード10進数の印字可能範囲に全ヒットしたため、

80 → P
90 → Z
110 → n

となって、'PZn'として出力されています。

型を見てみると、以下のとおりです。

iex
# 「list_int」の型を確認
iex()> i list_int
Term
  'PZn'
Data type
  List
.
.
.

# 「list_int の最初の要素」の型を確認
iex()> i list_int |> Enum.at(0)
Term
  80
Data type
  Integer
.
.
.

狐につままれたような感がしなくもないですが、中の要素はIntegerだということです。

念のため、もう少し確認してみます。

iex
iex()> list_int
'PZn'

iex()> list_int |> Enum.sum
280

リスト内の要素は文字でなく、数値ですね。
(∵ 80 + 90 + 110 = 280)

iex以外でもやってみる

iex以外でも一応確認してみます。

スクリプト実行

おためしで、簡単なコードを書いてみました。

charlist.exs
defmodule Charlist do
  def print_list do
    list_int = [80, 90, 110]
    list_int |> IO.puts
    Enum.sum(list_int) |> IO.puts
  end
end

Charlist.print_list

こちらをスクリプト実行してみます。

bash
$ elixir charlist.exs
PZn
280

iexと同様、ASCIIコード文字列でいったん出力されています。
リストで中身はIntegerなので、Enum.sumはきちんと機能しています。

コンパイル実行

コンパイル実行だとどうなるのでしょうか。

さっきと同じソースコードを、こんどはexファイルとして用意します。

charlist.ex
defmodule Charlist do
  def print_list do
    list_int = [80, 90, 110]
    list_int |> IO.puts
    Enum.sum(list_int) |> IO.puts
  end
end

Charlist.print_list

elixircでコンパイル実行してみます。

bash
$ elixirc charlist.ex
PZn
280

こちらも同様の出力結果となっています。

つまり、この現象はiex特有のものではない、ということです。

コードポイントを指定して文字のリストを返す

逆に、これを利用して、ASCIIコード10進数の整数リストを「文字のリスト」として返すことが可能です。

iex
iex()> list_im = [105, 109]
'im'

'im'の出力結果が得られました!
(ASCIIコード10進数で、105がi、109がmに該当)

文字列と文字リスト

「文字のリスト」について、ここで一度整理します。

まず、文字リスト(charlist)と文字列は別のものです。
たとえば、以下の2つは似ていますが異なるものです。

  • "im"
  • 'im'
iex
iex()> "im" == 'im'
false

ダブルクオート「""」で囲まれた文字は 文字列
シングルクオート「''」で囲まれた文字は 文字リスト になります。

文字列 は、UTF-8でエンコードされたバイナリです。
(いわゆる他の言語でいう文字列に近いのはこっち)

文字リスト は、文字のリストとは言うものの、じつは整数のリストです。
さきほどのlist_im = [105, 109](→結果は'im')を見てみると。。。

iex
iex()> i list_im
Term
  'im'
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
  [105, 109]
.
.
.

Descriptionに、書かれていますね。
This is a list of integers ... であると。
(いっしょにASCIIコードの印字範囲うんぬんの話も説明されてる)

文字リストは整数のリストであるので、以下のようなことができます。

iex
iex()> 'im' |> Enum.sum
214

# 105 + 109 = 214

反対に、文字列を引数として使う関数では、文字リストは使えません(リストなので)。

iex
# これはOK
iex()> "im" |> String.reverse
"mi"

# これはNG
iex()> 'im' |> String.reverse
** (FunctionClauseError) no function clause matching in String.Unicode.next_grapheme_size/1
.
.
.

つまり、

  • 文字列を引数として期待する関数
  • リスト(文字リスト)を引数として期待する関数

の使い分けが必要、ということになります。

文字リストが準備されてる目的は?(結論にかえて)

結局、「何が目的で[80, 90, 110]'PZn'として出力されるのか?」
ってことになるんですが、

一部のErlangモジュールに文字情報を渡すとき、バイナリの文字列でなくて文字リスト(整数のリスト)として持たせておく必要があるので、その名残(?)が由来なのかなあと推測しています。

文字リストがサポートされているのは一部のErlangモジュールがそれを必要としているからです。
(出典:Elixir School

これ以上のはっきりした由来はよく分かりませんでしたmm

なにかわかったら追記します!

p.s.
@お詳しい方 ぜひそっと教えてくださいませ。


参考文献

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8