defmodule SpellChecker do require Logger @moduledoc """ The `SpellChecker` module is designed to check the correctness of words against a predefined word list and provide suggestions for incorrect words. ## Functions `is_correct?/2` `is_correct/1` `is_correct!/2` """ @file_path "lib\\wordlist.txt" def is_correct?(word_list, acc \\ []) def is_correct?([], acc) do acc end @doc """ `is_correct?/2` This function checks if a list of words are correct and returns a list of the correct words. Parameters: `word_list` (list): A list of words to check. `acc` (list, optional): An accumulator for correct words (used internally, defaults to an empty list). Returns: A list of correct words. ## Examples iex> words = ["hello", "worlld", "Programming"] iex> correct_words = SpellChecker.is_correct?(words) iex> ["hello", "Elixir"] """ def is_correct?([new_word | rest], acc) do case MapSet.member?(load_word_list(), String.downcase(new_word)) do true -> is_correct?(rest, [new_word | acc]) _ -> is_correct?(rest, acc) end end @doc """ `is_correct/1` This function checks if a single word is correct. Parameters: word (string): A word to check. Returns: A tuple indicating if the word is correct. ## Examples iex> SpellChecker.is_correct("hello") iex> {:ok, true} iex> SpellChecker.is_correct("worlld") iex> {:none, false} """ def is_correct(word) when is_bitstring(word) and word != "" do case MapSet.member?(load_word_list(), word) do true -> {:ok, true} false -> {:none, false} end end @doc """ `is_correct!/2` This function checks if a single word is correct and provides suggestions if it is not. Parameters: `word` (string): A word to check. `suggest` (integer, optional): The number of suggestions to return (defaults to 5). ## Examples iex> SpellChecker.is_correct!("hello") iex> {:ok, true} iex> SpellChecker.is_correct!("worlld", 3) iex> ["world", "word", "would"] """ def is_correct!(word, suggest \\ 5) when is_bitstring(word) and word != "" do case MapSet.member?(load_word_list(), word) do true -> {:ok, true} false -> suggest(word, suggest) end end defp suggest(word, suggest) do Enum.reduce(load_word_list() |> Enum.to_list(), [], fn e, acc -> if Simetric.Jaro.Winkler.compare(e, word) > 0.85 do [e | acc] else acc |> Enum.slice(0..suggest) end end) end defp load_word_list() do File.stream!(@file_path) |> Stream.map(&String.trim/1) |> Enum.into(MapSet.new()) end end