文字列のパース
F.o.R(Frame of Reference)のような構造図を実現するために、まずは文字列を単語単位で分割したい。
例えば"This is a pen."
という文字列を["This", "is", "a", "pen", "."]
というリストに変えたい。
String.split()
だと空白で分割されるため、"pen."
になってしまい、ピリオドだけ分割できない。
test "split" do
assert "This is a pen." |> String.split() == ["This", "is", "a", "pen", "."]
end
実行結果
code: assert "This is a pen." |> String.split() == ["This", "is", "a", "pen", "."]
left: ["This", "is", "a", "pen."]
right: ["This", "is", "a", "pen", "."]
ピリオドを考慮する
そこで文字列の最後にピリオドが含まれているかを調べる関数を作り、ピリオドが含まれていれば、分割するようにしたい。
文字列の最後にピリオドが含まれているかどうか
def contains_terminator?(token) do
last_char_code =
token
|> String.to_charlist()
|> List.last()
last_char = <<last_char_code::utf8>>
if last_char == "." do
true
else
false
end
end
test "contains_terminator" do
assert "This" |> contains_terminator? == false
assert "is" |> contains_terminator? == false
assert "a" |> contains_terminator? == false
assert "pen." |> contains_terminator? == true
end
想定通りに動くことを確認できた。
ピリオドが含まれていたら分割する
def split_terminator([]), do: []
def split_terminator([head | tail]) do
if contains_terminator?(head) do
[token_a, token_b] = head |> String.split(".")
[token_a, ".", token_b | split_terminator(tail)]
else
[head | split_terminator(tail)]
end
end
test "'This is a pen.'" do
assert ["This", "is", "a", "pen."] |> split_terminator() == ["This", "is", "a", "pen", "."]
end
テストコードを実行したが、エラーになってしまった。
Assertion with == failed
code: assert ["This", "is", "a", "pen."] |> split_terminator() == ["This", "is", "a", "pen", "."]
left: ["This", "is", "a", "pen", ".", ""]
right: ["This", "is", "a", "pen", "."]
どうやら最後に空白の要素ができてしまうらしい。
アドホックなやり方だが、 Enum.filter(fn x -> x !== "" and x !== nil end)
を加えて空白の要素を除去するようにした。
def split_terminator([head | tail]) do
# {result, terminator} = contains_terminator?(head)
if contains_terminator?(head) do
[token_a, token_b] = head |> String.split(".")
[token_a, ".", token_b | split_terminator(tail)]
|> Enum.filter(fn x -> x !== "" and x !== nil end)
else
[head | split_terminator(tail)]
end
end