ElixirでElixirインタプリタを作成していく
第2回 基本的な引数の分割
前回に引き続き,今回は文字列として与えられる引数をリストとして返す関数を作成していく.
リストはapply
関数に渡すものであり,
"1, 2.0, \"hello\"" |> function
# [1, 2.0, "hello"]
のようになるようにする.
しかし上記のように分割するために初めに文字列のリストとして分割すると変換しやすそうである
"1, 2.0, \"hello\"" |> function
# ["1", "2.0", "\"hello\""]
ここで関数functionを次のように定義する
定義
関数名:arg_convertion
引数:String
返り値:[String]
実装
今回は引数分割の実装を行っていく.
ここで通常,
で区切れば引数を分割できると考えるだろう.
つまり次のように書くことができる
String |> String.split(",")
しかしよく考えてほしい,List
やMap
,Tuple
にはそれぞれの中で,
を使用する
次のような場合を考えてみよう
"1, [2, 3, 4]"
これは,引数の個数は2であることがわかるであろう.実際に関数を呼び出すとき
Example.function 1, [2, 3, 4]
となり,呼び出される関数はExample.function/2
であるべきだ
しかしString.split
で分割してしまうと
["1", " [2", " 3", " 4]"]
となり,引数が4つあるように感じてしまう.
この状態から後ろ3つをList
として抽出することは不可能でないだろう
しかし,理想としては一発で2つに分割できる事が望ましい
そこで正規表現を用いたRegex.split
関数を用いることとする
次にRegex.split
を用いたコードを示すが,このコードはすでにList
や文字列などを考慮して作成したものだということを念頭にいれていてほしい
Regex.split(~r/(\%.*\{.*\}|\[.*\]|\{.*\}|,|\".*?[^\\]\"|\s)/, arg, include_captures: true)
この時,include_captures: true
によって分割の指標となる文字列もList
として現れることとなる
実際に使用すると次のようになる
["1", ",", "", " ", "", "[2, 3, 4]", ""]
,
やスペース,空文字列も分割されていることがわかるだろう,
そこでいらないものを排除する関数を作成する.
defmodule ElixirInterpreter.Core.Utils do
def drop_value(list, [head | tail]) when is_list(list) do
list
|> Enum.reject(fn x -> x == head end)
|> drop_value(tail)
end
def drop_value(list, []) when is_list(list), do: list
end
上記の関数を作成して次のようにする
["1", ",", "", " ", "", "[2, 3, 4]", ""] |> ElixirInterpreter.Core.Utils.drp_value
すると結果は
["1", "[2, 3, 4]"]
となり,2つに分割できていることが確認できる.
これはList
だけでなくMap
やTuple
,Struct
においても分割が可能だ
以上の関数を関数として実装しておく,
defmodule ElixirInterpreter.Core do
...
def arg_split(arg) when is_binary(arg) do
Regex.split(~r/(\%.*\{.*\}|\[.*\]|\{.*\}|,|\".*?[^\\]\"|\s)/, arg, include_captures: true)
end
end
これで引数をList
に変換する目途が立ったように思う
今回でInteger
,Float
,nil
の実装も考えたが内容が大きなものだったので次回に持ち越そうと思う
第0回 目次
第1回 文字列からモジュールと関数を導き出す
第3回 文字列のリストを値として変換していく
以下ソースコード(マスターでゴリゴリ開発してるので悪しからず.)
GitHub: ElixirInterpreter