Skip to content

Instantly share code, notes, and snippets.

@eksperimental
Last active May 18, 2017 20:17
Show Gist options
  • Save eksperimental/03ff05172b174a7f29861ccd0be0acc3 to your computer and use it in GitHub Desktop.
Save eksperimental/03ff05172b174a7f29861ccd0be0acc3 to your computer and use it in GitHub Desktop.
For loop in Elixir: originated from this discussion: http://elixirforum.com/t/how-to-do-a-for-loop-in-elixir-using-only-recursion/595
defmodule ForLoop do
@doc ~S"""
Iterates over `term`, while `condition` between `term` and `final` is truthy,
applying `fun`. It returns the accumulated terms.
- `term` is any that will be iterated over.
- `final_term` is the element that will be run against `term` in `condition`.
- `condition` can take 1 argument: `term`, or two arguments: `term` and `final`.
- `fun` is run every time a `condition` is evaluated to truthy.
- `transform` is applied to `term` at the end of every loop, before the next iteration.
## Examples
iex> ForLoop.for_loop(1, 5, &(&1))
[1, 2, 3, 4, 5]
# condition can take one argument, so we don't need to
iex> ForLoop.for_loop(12345, nil, &(String.length("#{&1}") <= 10), &(&1 * 7), &(:"#{&1}"))
[:"12345", :"86415", :"604905", :"4234335", :"29640345", :"207482415", :"1452376905"]
"""
@spec for_loop(term, term, (term -> term) | (term, term -> term), (term -> term), (term -> term)) :: list
def for_loop(term, final, condition \\ &(&1 <= &2), transform \\ &(&1 + 1), fun)
when (is_function(condition, 1) or is_function(condition, 2))
and is_function(transform, 1)
and is_function(fun, 1) do
loop(term, final, condition, transform, fun, [])
end
defp loop(current, final, condition, transform, fun, acc) do
loop? =
cond do
is_function(condition, 1) ->
condition.(current)
is_function(condition, 2) ->
condition.(current, final)
end
if loop? do
loop(transform.(current), final, condition, transform, fun, [fun.(current) | acc])
else
Enum.reverse(acc)
end
end
end
# plain loop from 1 to 10
ForLoop.for_loop(1, 10, &(IO.puts(&1)))
|> IO.inspect
# a loop and accumulating results at the same time
ForLoop.for_loop(1, 10,
fn(x) ->
square = x * x
IO.inspect(square)
:"square_#{square}"
end
)|> IO.inspect
# from 10 to 1, decreasing it by 0.5
ForLoop.for_loop(10, 1, &(&1 >= &2), &(&1 - 0.5), &(IO.puts(&1)))
|> IO.inspect
# take a descending list from 10 to 1, print the results as the list is being shortened,
# stop when the head of the list is 4,
# accumulate the heads of the list in each iteration
ForLoop.for_loop(Enum.to_list(10..1), [4], &(&1 >= &2), &(tl(&1)),
fn x ->
IO.inspect(x)
hd(x)
end
) |> IO.inspect
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment