Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions lib/simetric/levenshtein.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,33 @@ defmodule Simetric.Levenshtein do
3

"""
@spec compare(String.t, String.t) :: non_neg_integer
def compare(string1, string2)
@spec compare(String.t, String.t, integer) :: non_neg_integer
def compare(string1, string2, diff_score \\ 1)

def compare(string, string), do: 0
def compare(string, string, _), do: 0

def compare(string1, ""), do: String.length(string1)
def compare(string1, "", _), do: String.length(string1)

def compare("", string2), do: String.length(string2)
def compare("", string2, _), do: String.length(string2)

def compare(string1, string2) do
def compare(string1, string2, diff_score) do
chars1 = String.graphemes(string1)
chars2 = String.graphemes(string2)
distance(chars1, chars2, length(chars2)..0, 1)
distance(chars1, chars2, length(chars2)..0, 1, diff_score)
end

defp distance([], _, [result | _], _), do: result
defp distance([], _, [result | _], _, _), do: result

defp distance([char | rest], chars2, distlist, step) do
distlist = proceed(char, chars2, Enum.reverse(distlist), [step], step)
distance(rest, chars2, distlist, step + 1)
defp distance([char | rest], chars2, distlist, step, diff_score) do
distlist = proceed(char, chars2, Enum.reverse(distlist), [step], step, diff_score)
distance(rest, chars2, distlist, step + 1, diff_score)
end

defp proceed(_, [], _, acc, _), do: acc
defp proceed(_, [], _, acc, _, _), do: acc

defp proceed(char1, [char2 | rest], [head | [prev | _] = distlist], acc, score) do
diff = if char1 == char2, do: 0, else: 1
defp proceed(char1, [char2 | rest], [head | [prev | _] = distlist], acc, score, diff_score) do
diff = if char1 == char2, do: 0, else: diff_score
score = min(min(score + 1, prev + 1), head + diff)
proceed(char1, rest, distlist, [score | acc], score)
proceed(char1, rest, distlist, [score | acc], score, diff_score)
end
end
24 changes: 24 additions & 0 deletions test/simetric/levenshtein_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,33 @@ defmodule Simetric.LevenshteinTest do
["distance", "difference"] => 5
}

cases_custom = %{
# empty strings
["", ""] => 0,
["", "ab"] => 2,
["abc", ""] => 3,
# equal strings
["abc", "abc"] => 0,
# inserts only
["ac", "abc"] => 1,
# deletions only
["xabxcdxxefxgx", "abcdefg"] => 6,
# substitutions only
["a", "b"] => 2,
["xabxcdxxefxgx", "1ab2cd34ef5g6"] => 12,
# mixed operations
["levenshtein", "frankenstein"] => 9
}

for {input, distance} <- cases do
test "compare #{inspect input}" do
assert Simetric.Levenshtein.compare(unquote_splicing(input)) == unquote(distance)
end
end

for {input, distance} <- cases_custom do
test "compare custom #{inspect input} with diff_distance = 2" do
assert Simetric.Levenshtein.compare(unquote_splicing(input), 2) == unquote(distance)
end
end
end