技術メモ

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

Elixir入門(第十八章 Sigils)

f:id:ysmn_deus:20190122112104p:plain

どうも、靖宗です。
この章はSigil。まったく聞いたこともないので辞書で調べてみるとシジルまたはシギル(英: sigil、羅: sigillum)は主に西洋魔術で使われる図形、記号、紋章、線形である。
とうとう魔術まで出てきました・・・

なんか文字列拡張しようぜ!みたいな雰囲気の文章が書かれていますが、英語がそこまで得意じゃない自分はコードで読み解いていきます。
クソザコ英語能力で分かるのは~で始まり、なんか区切り文字があるのがsigilだよ!と書いてある事ぐらいです。
それでは進んでいきましょう。

Regular expressions(正規表現

まぁプログラミング言語ならありますよね、という感じの正規表現
なじみ深い感じで~rで表されるようです。

# A regular expression that matches strings which contain "foo" or "bar":
iex> regex = ~r/foo|bar/
~r/foo|bar/
iex> "foo" =~ regex
true
iex> "bat" =~ regex
false

小文字大文字判定などのオプションの付け方はPerlとコンパチだそうです。
必要に応じて調べれば良さそう。

iex> "HELLO" =~ ~r/hello/
false
iex> "HELLO" =~ ~r/hello/i
true

今は/を区切り文字として使ってますが、Elixirでは以下の8つが使用できるそうで。

~r/hello/
~r|hello|
~r"hello"
~r'hello'
~r(hello)
~r[hello]
~r{hello}
~r<hello>

なんなら統一してくれた方が楽な気がするんですが・・・
一応パターン次第ではエスケープ文字が冗長になる(~r/^https?:\/\//の区切り文字を変えれば~r(^https?://)と表現できるのでスッキリ!)からって理由だそうですが、しっかりエスケープした方が間違いが少なくなるような。
何はともあれすすみませう

Strings, char lists, and word lists sigils

正規表現の他にもいろいろなsigilが。

Strings

~sでダブルクォーテーションの文字列(一般的な文字列)が生成できるとのこと。

iex> ~s(this is a string with "double" quotes, not 'single' ones)
"this is a string with \"double\" quotes, not 'single' ones"

ダブルクォーテーションが勝手にエスケープされてたりと、これは有用かもしれません。(ケースバイケースだとは思います)

Char lists

こちらはシングルクォートの方ですね。最初が~cであること以外は特に変わりません。

iex> ~c(this is a char list containing 'single quotes')
'this is a char list containing \'single quotes\''

こちらもきっちりシングルクォートはエスケープされております。

Word lists

こちらは文字列のリストを生成するためのsigilだそうです。
空白で区切った文字がリストとなるようです。これは~wです。

iex> ~w(foo bar bat)
["foo", "bar", "bat"]

Word listsのsigilにはオプションが利用でき、cでChar listsへ、aでアトムへ変換するようです。文字列へは何も指定しなくて大丈夫ですが、一応sのオプションがあるようです。

iex> ~w(foo bar bat)a
[:foo, :bar, :bat]

Interpolation and escaping in sigils

sigilは大文字と小文字の場合で挙動が違うようです。
小文字の場合はinterpolation(#{}で文字列中で変換したりするやつ)はいつも通り計算されるようですが、大文字であればこの補間は行われないようです。

iex(68)> ~s(String with escape codes \x26 #{"inter" <> "polation"})
"String with escape codes & interpolation"
iex(69)> ~S(String with escape codes \x26 #{"inter" <> "polation"})
"String with escape codes \\x26 \#{\"inter\" <> \"polation\"}"

サンプルはなぜか大文字と小文字で文章が変わってますが、比較するなら同じものを実行するべきだと思います(╬´◓ω◔`╬)
大文字だとエスケープシーケンスの文字も自動でエスケープが入るようです。

iex(74)> ~s(\a)
"\a"
iex(75)> ~S(\a)
"\\a"

一応sigilでは`"エスケープできるけど区切り文字変えようね!とは書いてます。
郷に入っては郷に従えか・・・

ちなみに余談ですがMarkdownでバックォートのエスケープが分からずぐぐりました。↑のバッククォートは`` ` ``と書かれてます。

sigilは関数の属性のところで出てきた@docに書くような3つの(ダブル|シングル)クォートに対応しているようです。

iex> ~s"""
...> this is
...> a heredoc string
...> """

これはそのまんま、@docなどに活用されるようです。

@doc """
Converts double-quotes to single-quotes.

## Examples

    iex> convert("\\\"foo\\\"")
    "'foo'"

"""
def convert(...)

これではエラーがでるので

@doc ~S"""
Converts double-quotes to single-quotes.

## Examples

    iex> convert("\"foo\"")
    "'foo'"

"""
def convert(...)

こう書くと。確かにこちらの方が分かりやすい。

Custom sigils

Elixirの哲学?目標?の一部に拡張性があるらしいんですが、sigilもカスタマイズできるようになっているそうです。
正規表現のsigilがsigil_rという関数で表現できるようで

iex> sigil_r(<<"foo">>, 'i')
~r"foo"i

これと同じ様にsigil_{もじ}という関数を定義すればsigilとして利用できるようです。
サンプルを見ます。

iex> defmodule MySigils do
...>   def sigil_i(string, []), do: String.to_integer(string)
...>   def sigil_i(string, [?n]), do: -String.to_integer(string)
...> end
iex> import MySigils
iex> ~i(13)
13
iex> ~i(42)n
-42

MySigilsモジュールではsigil_iを定義してるだけですが、~iが利用できるようになっています。
区切り文字なども特に定義の中には書かれていませんが、sigilと同様の挙動を取っている所をみるとsigilのカスタマイズとして認識されているようです。