호두나무 공방/Exercism in Elixir

Luhn - Exercism in Elixir

2022. 9. 6. 22:35

문제 보기

지정된 규칙에 따라 신용카드 번호가 올바른지 아닌지를 검증하는 문제였다. 오른쪽부터 세서 짝수 번째 자리에 있는 숫자에 2를 곱한 뒤, 모든 자리수를 더했을 때 10의 배수가 되면 유효한 자리수로 판정한다.

일반적인 신용카드처럼 자리수가 16자리 고정이면 참 쉬웠을텐데, 입력되는 번호의 자리수의 홀짝이 제각각이어서 자리수에 따라 달리 처리해야 하는 부분이 조금 까다로웠다.

defmodule Luhn do
  require Integer

  @doc """
  Checks if the given number is valid via the luhn formula
  """
  @spec valid?(String.t()) :: boolean
  def valid?(number) do
    space_removed = String.replace(number, ~r/\s+/, "")

    if String.length(space_removed) > 1 and String.match?(space_removed, ~r/^[0-9]+$/) do
      space_removed
      |> String.graphemes()
      |> Enum.map(&String.to_integer/1)
      |> Enum.with_index()
      |> Enum.map(fn {v, i} ->
        double_every_second(v, i, String.length(space_removed))
        |> reduce_to_single_digit()
      end)
      |> Enum.sum()
      |> Kernel.rem(10)
      |> Kernel.==(0)
    else
      false
    end
  end

  defp double_every_second(v, i, str_len) when Integer.is_even(str_len) do
    if rem(i, 2) == 0, do: v * 2, else: v
  end

  defp double_every_second(v, i, str_len) when Integer.is_odd(str_len) do
    if rem(i, 2) == 1, do: v * 2, else: v
  end

  defp reduce_to_single_digit(v) when v > 9, do: v - 9
  defp reduce_to_single_digit(v), do: v
end