6

I'm studying functional programming and I want to implement something like this.

while(true) do
  if(somethingHappensHere) {
    break
  }
  counter++
end
return counter

How can I do this in functional way using elixir?

Thanks for this.

4 Answers 4

8

While in most functional programming languages one would use a recursion for this task, Elixir particularly provides the way to do this without using an explicit recursion call: Enum.reduce_while/3:

Enum.reduce_while(1..100, 0, fn i, acc ->
  if condition, do: {:halt, acc}, else: {:cont, acc + i}
end)

For lazy evaluation one would use Stream.reduce_while/3.

To make it infinite, one might use one of infinite generators, provided by Stream module, like Stream.iterate/2:

Stream.iterate(0, &(&1+1)) |> Enum.reduce_while(0, fn i, acc ->
  if i > 6, do: {:halt, acc}, else: {:cont, acc + 1}
end)
#⇒ 7

For the sake of recursion, this is how the recursive solution might be implemented in Elixir:

defmodule M do
  def checker, do: & &1 <= 0
  def factorial(v, acc \\ 1) do
    if checker().(v), do: acc, else: factorial(v - 1, v * acc)
  end
end

M.factorial 6                            
#⇒ 720
Sign up to request clarification or add additional context in comments.

Comments

1

Not sure about elixir specifically, but you can achieve this using recursion:

function myFunction(int counter)
{
  if (condition) {
    return counter
  }
  return myFunction(counter + 1)
}

This essentially sets up a function that can infinitely recurse (call itself), each time passing in the next counter value.

By having the recursive call as the last thing the function does, this is known as tail-call recursion which elixir supports (as per: Does Elixir infinite recursion ever overflow the stack?)

This can then be used as such:

int counterValue = myFunction(0)

With the function only returning once the condition is true.

You could also make this more generic by having the function take another function that returns true or false (i.e. performs the conditional check).

As I said, unfortunately I'm not aware of the syntax of elixir, but I'm sure you'll be able to bridge that gap.

4 Comments

What concurrency has to do with a recursion?
@mudasobwa sorry, should perhaps have elaborated. If the condition is something that is updated elsewhere, then obviously the recursive loop is going to prevent it being updated if it’s single threaded. So you’ll need to use concurrency to make sure it’s all fine.
“elsewhere” won’t happen at all if things are single-threaded. I am not sure what concurrency at all has to do with recursion and I find the whole sentence about concurrency being very misleading.
@mudasobwa fair point. I can see how it would probably need expanding / could confuse the message. I’ve removed 👍
1

An example of something in Elixir syntax:

defmodule SOQuestion do 
  def test(counter) do
    if (something_happens_here?()), do: counter, else: test(counter+1)
  end
  def something_happens_here?() do
    true
  end
end

And it would be invoked like this:

SOQuestion.test(0)

A couple of notes on this:

1.) It's a code fragment. Obviously it's tough to be very complete given the broad nature of your question.

2.) something_happens_here? being a predicate it would normally be named ending with a question mark.

3.) If something_happens_here? were defined in a different module, then the call would be if (Module.something_happens_here?())

4.) I've obviously coded something_happens_here? to simply return true unconditionally. In real code, of course, you'd want to pass some argument to something_happens_here? and act on that to determine which boolean to return.

Given all that I totally agree with @mudasowba--this sort of thing is usually better handled with one of the higher order functions built into the language. It's less error prone and often much easier for others to read too.

Comments

1

As mentioned, you could use a number of built-in functions like Enum.reduce_while/3. However, sometimes it is just as easy (or fun) to use simple recursion.

Using recursion:

I will make some generic examples and use bar(foo) as an example of your somethingHappensHere condition.

1) If bar(foo) is something allowed in a guard clause:

defmodule Counter do
  def count do
    count(foo, 0)
  end

  defp count(foo, count) when bar(foo), do: count

  defp count(foo, count), do: count(foo, count + 1)
end

2) If bar(foo) is a function that returns a boolean:

defmodule Counter do
  def count(foo) do
    count(foo, 0)
  end

  defp count(foo, count) do
    if bar(foo) do
      count
    else
      count(foo, count + 1)
    end
  end
end

3) If bar(foo) returns something other than a boolean, that you can pattern-match on, like:

defmodule Counter do
  def count(foo) do
    count(foo, 0)
  end

  defp count(foo, count) do
    case bar(foo) do
      {:ok, baz}  -> count
      {:error, _} -> count(foo, count + 1)
    end
  end
end

Call the module and function:

Counter.count(foo)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.