Last active
May 18, 2017 20:17
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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