1
\$\begingroup\$

I just started learning Elixir and stumbled upon this challenge over on Programming Puzzles & Code Golf. It is a well-suited task for beginners, so I chose to give it a go (to be clear, give it a go means solve it normally, not golfing it). To keep this question self-contained, here is the task – citing the linked post:

For a given positive integer \$n\$:

  1. Repeat the following until \$n < 10\$ (until \$n\$ contains one digit).
  2. Extract the last digit.
  3. If the extracted digit is even (including 0) multiply the rest of the integer by \$2\$ and add \$1\$ ( \$2n+1\$ ). Then go back to step 1 else move to step 4.
  4. Divide the rest of the integer with the extracted digit (integer / digit) and add the remainder (integer % digit), that is your new \$n\$.

For example, \$61407\$ gives \$5\$ when ran through this mechanism. I've come up with the following code:

defmodule ExtractAndDivide do
  def extract_and_divide(x) do
    if x < 10 do x
    else
      head = div x, 10
      tail = rem x, 10
      case rem tail, 2 do
        0 -> head * 2 + 1 |> extract_and_divide
        1 -> div(head, tail) + rem(head, tail) |> extract_and_divide
      end
    end
  end
end

I'm seeking general advice, but mainly focusing on the following:

  • Naming and Syntax better practices (usage of parenthesises, variable names etc.)
  • Usage of |> (pipe) in this context. Would you ever see it used the way I did it in production code? Should I switch to "normal" notation instead?
  • Less verbose or more elegant way to avoid the seemingly unaesthetic if x < 10 do x ... else ... end structure, perhaps using case would be better here?
  • Is recursion the way to go? Should I stick to it or are there better, equivalent methods?
\$\endgroup\$
1
\$\begingroup\$

I do not have much to say about the pipe operator. It looks fine to me, although maybe some else has something to say...

As for the if-else clause, you can use cond. It is basically a stylized if statement that looks like a case statement. One of your conditions can be x < 10 -> and the other default statement would be true ->.

I am not entirely sure if this is the best practice since the wording is a bit ambiguous in the documentation. Under the use case for cond it says the following:

This is equivalent to else if clauses in many imperative languages (although used way less frequently here).

Which I interpret to be else if clauses are used less often (implying that cond is often preferred).

\$\endgroup\$
0
\$\begingroup\$

(untested)

defmodule ExtractAndDivide do
  def extract_and_divide(x) when x < 10, do: x
  def extract_and_divide(x) do
    head = div x, 10
    tail = rem x, 10
    up_or_down = rem tail, 2
    go_up_or_down(head, tail, up_or_down)
  end
  defp go_up_or_down(head, tail, up_or_down) when up_or_down == 0 do
    extract_and_divide(head * 2 + 1)
  end
  defp go_up_or_down(head, tail, _up_or_down_is_one) do
    extract_and_divide(div(head, tail) + rem(head, tail))
  end
end

The idea with having multiple conditional function clauses is twofold: it reduces nesting (and thus should increase readability), and - quite powerful - it allows to to easily test individual conditional parts. Whether to make the recursive call to extract_and_divide by piping the calculation through the function invocation or directly (like I did purely to show the alternative) is largely a matter of taste.

(note: skipped/circumvented potential nitpicking around naming and whether up_or_down shouldn't be a boolean; I just wanted to illustrate the pattern of doing logic by using multiple function heads)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.