Current section
Files
Jump to
Current section
Files
lib/levenshtein.ex
defmodule Levenshtein do
@moduledoc """
For what is Levenshtein distance see:
[https://en.wikipedia.org/wiki/Levenshtein_distance](https://en.wikipedia.org/wiki/Levenshtein_distance)
"""
@doc """
Returns an integer representing Levenshtein distance between
`source` and `target`.
Levenshtein distance between two words is the minimum number of
single-character edits (i.e. insertions, deletions or substitutions)
required to change one word into the other.
The algorithm used is based on the [Wagner-Fisher iterative
implementation](http://en.wikipedia.org/wiki/Levenshtein_distance#Iterative_with_two_matrix_rows).
## Examples
iex> Levenshtein.distance("kitten", "sitting")
3
"""
@spec distance(String.t(), String.t()) :: non_neg_integer
def distance(source, target)
def distance(source, source), do: 0
def distance(source, <<>>), do: String.length(source)
def distance(<<>>, target), do: String.length(target)
def distance(source, target) do
source = String.graphemes(source)
target = String.graphemes(target)
distlist = 0..Kernel.length(target) |> Enum.to_list()
do_distance(source, target, distlist, 1)
end
defp do_distance([], _, distlist, _), do: List.last(distlist)
defp do_distance([src_hd | src_tl], target, distlist, step) do
distlist = distlist(target, distlist, src_hd, [step], step)
do_distance(src_tl, target, distlist, step + 1)
end
defp distlist([], _, _, new_distlist, _), do: Enum.reverse(new_distlist)
defp distlist(
[target_hd | target_tl],
[distlist_hd | distlist_tl],
grapheme,
new_distlist,
last_dist
) do
diff = if target_hd != grapheme, do: 1, else: 0
min = min(min(last_dist + 1, hd(distlist_tl) + 1), distlist_hd + diff)
distlist(target_tl, distlist_tl, grapheme, [min | new_distlist], min)
end
end