loading...
gumi TECH Blog

Elixir入門 18: シギル

gumitech profile image gumi TECH Updated on ・3 min read

本稿はElixir公式サイトの許諾を得て「Sigils」の解説にもとづき、加筆補正を加えて、Elixirにおけるシギルの使い方についてご説明します。

Elixirが扱うテキストには、ダブルクォーテーション("")の文字列とシングルクォテーションの文字リスト('')があります。ほかにアトムもテキスト表現のひとつです。さらに、Elixirでテキスト表現を扱う仕組みとして、シギルについてご説明します。

Elixirは拡張性を重視します。言語が拡張しやすければ、開発者やコミュニティなどがそれぞれの目的に合うように機能が加えられるからです。シギルもまたElixirのテキスト表現を拡張できます。

シギルはチルダ(~)ではじまる識別子に区切り文字を添えて表します。そのあとに、オプションの修飾子が加わることもあります。

正規表現

Elixirでもっとも使われるシギルは~rで、正規表現をつくります。Elixirの正規表現は、Perl互換のPCREライブラリによる実装です。修飾子も使えます。たとえば、iは大文字小文字を区別しません。正規表現との等価比較には、=~/2をお使いください。

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

つぎのコードは、正規表現で文字列が半角スペース区切りの2単語以上かどうかを確かめます。

iex> words = ~r/(\w+)\s(\w+)/
~r/(\w+)\s(\w+)/
iex> "hello" =~ words
false
iex> "hello world" =~ words
true

さらに、ElixirではErlangの正規表現にもとづくAPIが使えます。つぎのコードは、文字列のリストに分けるRegex.split/3と文字列を置き替えるRegex.replace/4の使用例です。詳しくは「Regex」モジュールの解説をお読みください。

iex> string = "100_000_000"
"100_000_000"
iex> Regex.split(~r/_/, string)
["100", "000", "000"]
iex> Regex.replace(~r/_/, string, ",")
"100,000,000"

ご紹介するコード例では、正規表現の区切り文字にバックスラッシュ(\)を用いています。けれども、シギルにはつぎの8つの区切り文字が使えます。

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

区切り文字をいくつも使い分けられるのは、リテラルに区切り文字が含まれてもエスケープせずに済むようにするためです。

iex> ~r(^https?://) == ~r/^https?:\/\//
true

文字列と文字リストおよび単語リストのシギル

正規表現のほかに、文字列と文字リストおよび単語リストのシギルがあります。

文字列

シギル~sは文字列をつくります。ダブルクォーテーション("")の文字列と同じです。けれど、文字列にダブルクォーテーションが含まれるときに便利です。

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

文字リスト

文字リストをつくるのはシギル~cです。シングルクォーテーション('')の含まれる文字リストが簡単に書けます。

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

単語リスト

シギル~wは単語のリストをつくるために用いられます。単語はスペースで区切られた文字列です。修飾子としてsaあるいはcが添えられます。変わるのは、単語リスト要素のデータ型です。sはデフォルトの文字列、aがアトム、cは文字リストになります。

iex> ~w(hello tokyo japan)
["hello", "tokyo", "japan"]
iex> ~w(hello tokyo japan)a
[:hello, :tokyo, :japan]
iex> ~w(hello tokyo japan)c
['hello', 'tokyo', 'japan']
iex> ~w(拝啓 時下ますます)
["拝啓", "時下ますます"]

NaiveDateTime

シギル~Nがつくるのは特別なNaiveDateTime構造体です。タイムゾーンのない日時を表します。NaiveDateTime構造体を直につくることはあまりないでしょう。ただし、パターンマッチングに使うことが考えられます。

iex> new_year_18 = NaiveDateTime.from_iso8601("2018-01-01 00:00:00")
{:ok, ~N[2018-01-01 00:00:00]}
iex> new_year_18 == {:ok, ~N[2018-01-01 00:00:00]}
true

シギルの補間とエスケープ

シギルには~Nを除いて、それぞれ小文字と大文字があります。違いはエスケープ文字と補間に対応するかどうかです(表001)。大文字のシギルはエスケープ文字もそのまま文字として扱い、補間をしません。

iex> intr = "inter "
"inter "
iex> pol = "polation"
"polation"
iex> ~s(String with escape codes \x26 #{intr <> pol})
"String with escape codes & inter polation"
iex> ~S(String without escape codes \x26 #{intr <> pol})
"String without escape codes \\x26 \#{intr <> pol}"
iex> ~w(String without escape codes \x26 #{intr <> pol})
["String", "without", "escape", "codes", "&", "inter", "polation"]
iex> ~W(String without escape codes \x26 #{intr <> pol})
["String", "without", "escape", "codes", "\\x26", "\#{intr", "<>", "pol}"]

表001■シギルとエスケープ

エスケープあり エスケープなし 表現
~r ~R 正規表現
~s ~S 文字列
~c ~C 文字リスト
~w ~W 単語リスト
~N NaiveDateTime構造体

使えるエスケープコードはつぎのとおりです(「ASCII制御文字」参照)。

  • \\: バックスラッシュ(\)
  • \a: ベル(警告)
  • \b: バックスペース
  • \d: 削除
  • \e: エスケープ
  • \f: 書式送り
  • \n: 改行
  • \r: 行頭復帰
  • \s: スペース
  • \t: タブ
  • \v: 垂直タブ
  • \0: ヌル文字
  • \xDD: 16進数1バイト
    • 例: \x13
  • \uDDDDまたは\u{D...}: 16進数Unicodeコードポイント
    • 例: \u{1F600})

(ダブルまたはシングル)クォーテーションで囲んだ中に同じ引用符が含まれると\でエスケープしなければなりません。

それに加えて、二重引用符で囲まれた文字列の中の二重引用符は\ "としてエスケープする必要があり、一重引用符で囲まれた文字リスト内の一重引用符は\ 'としてエスケープする必要があります。そういうとき、大文字シギルを使えばエスケープのわずらわしさが避けられるのです。

シギルはヒアドキュメントでも使えます。区切り文字は3連クォーテーションです。

iex> ~s"""
...> これは
...> ヒア文字列
...> """
"これは\nヒア文字列\n"

ヒアドキュメントにシギルを使う場合としてもっとも考えられるのは、ドキュメントを書くときです。エスケープ文字が含まれることは少なくありません(図001)。とくにエスケープ文字が続くと見にくく、エラーも起きやすくなります。

defmodule Example do
  @doc """
    ダブルクォーテーションをシングルクォーテーションに変換する。

    ## 例

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

  """
  def convert(str) do

  end
end

図001■表示されたドキュメント

elixir_18_001.png

こういうとき、大文字シギル~Sを使えば、文字列をそのまま書けるのです。

defmodule Example do
  @doc ~S"""
    ダブルクォーテーションをシングルクォーテーションに変換する。

    ## 例

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

  """
  def convert(str) do

  end
end

カスタムシギル

Elixirのシギルは拡張できます。たとえば、シギル~rを使うのは、sigil_r/2の呼び出しと同じです。第1引数にバイナリまたは文字リスト、第2引数にはオプションの修飾子を渡します。

iex> r_hello = sigil_r(<<"hello">>, 'i')
~r/hello/i
iex> "HELLO" =~ r_hello
true

sigil_{identifier}のパターンにしたがった関数を実装するとシギルが定められます。String.upcase/2String.downcase/2は、文字列をそれぞれ大文字あるいは小文字に変える関数です。これらふたつの関数で、つぎのように文字列を大文字や小文字にするシギルができます。小文字は第2引数の文字リストに修飾子として加えました。

defmodule MySigils do
  def sigil_u(string, []), do: String.upcase(string)
  def sigil_u(string, [?d]), do: String.downcase(string)
end
iex> import MySigils
MySigils
iex> ~u(hello world)
"HELLO WORLD"
iex> ~u(Hello World)d
"hello world"

シギルはマクロにして定めれば、コンパイル時の作業にも使えます。たとえば、正規表現は、ソースコードをコンパイルすると、表現が最適化されます。すると、実行時にはこの手順が省かれるのです。

Elixir入門もくじ

番外

Posted on by:

gumitech profile

gumi TECH

@gumitech

gumi TECH は、株式会社gumiのエンジニアによる技術記事公開やDrinkupイベントなどの技術者交流を行うアカウントです。 gumi TECH Blog: http://dev.to/gumi / gumi TECH Drinkup: http://gumitech.connpass.com

gumi TECH Blog

株式会社gumiのエンジニアによる技術記事を公開しています。

Discussion

pic
Editor guide