require Sequences
require Drop
defmodule DropTest do
def run do
IO.puts "enum0 = Sequences.fibs |> Stream.map(&IO.inspect/1)"
enum0 = Sequences.fibs |> Stream.map(&IO.inspect/1)
IO.puts "enum1 = enum0 |> drop(5)"
enum1 = enum0 |> Drop.drop(5)
IO.puts "enum1 |> Enum.take(5) |> IO.inspect"
enum1 |> Enum.take(5) |> IO.inspect
IO.puts "enum1 |> Enum.at(5) |> IO.inspect"
enum1 |> Enum.at(5) |> IO.inspect
end
end
DropTest.run
defmodule Drop do
@doc ~S"""
Drop Function.
Similar to `Stream.drop`, but STRICTLY drops first `n` elements (n > 0).
## Examples
iex> Sequences.fibs |> Drop.drop(5) |> Enum.take(5)
[8, 13, 21, 34, 55]
"""
def drop(enum, count) do
fun = fn
(v, 0) -> {:suspend, v}
(_, n) -> {:cont, n - 1}
end
case Enumerable.reduce(enum, {:cont, count - 1}, fun) do
{:halted, _} -> []
{:done, _} -> []
{:suspended, _, next_fun} -> &do_drop(next_fun, &1, &2)
end
end
defp do_drop(_, {:halt, acc}, _fun), do: {:halted, acc}
defp do_drop(next, {:suspend, acc}, fun) do
{:suspended, acc, &do_drop(next, &1, fun)}
end
defp do_drop(next, {:cont, acc}, fun) do
case next.({:cont, 0}) do
{:halted, _} -> {:halted, acc}
{:done, _} -> {:done, acc}
{:suspended, v, next_fun} -> do_drop(next_fun, fun.(v, acc), fun)
end
end
end
defmodule Sequences do
@doc ~S"""
Natural Integers (Non-Negative Integers).
## Examples
# take 1..10
iex> Sequences.nats |> Enum.take(10)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
"""
def nats do
&nats(1, &1, &2)
end
defp nats(_, {:halt, acc}, _fun), do: {:halted, acc}
defp nats(n, {:suspend, acc}, fun), do: {:suspended, acc, &nats(n, &1, fun)}
defp nats(n, {:cont, acc}, fun), do: nats(n + 1, fun.(n, acc), fun)
@doc ~S"""
Fibonacci Sequence ([1, 1, 2, 3, ...]).
## Examples
# take first 10 Fibonacci Numbers.
iex> Sequences.fibs |> Enum.take(10)
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
# The 100th Fibonacci Number
iex> Sequences.fibs |> Enum.at(99)
354224848179261915075
"""
def fibs do
&fibs({1, 1}, &1, &2)
end
defp fibs(_, {:halt, acc}, _fun), do: {:halted, acc}
defp fibs(v, {:suspend, acc}, fun), do: {:suspended, acc, &fibs(v, &1, fun)}
defp fibs({a, b}, {:cont, acc}, fun), do: fibs({b, a + b}, fun.(a, acc), fun)
@doc ~S"""
Primes.
## Examples
# First 20 primes
iex> Sequences.primes |> Enum.take(20)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71]
# The 10000th prime
iex> Sequences.primes |> Enum.at(9999)
104729
"""
def primes do
&primes(2, &1, &2)
end
defp primes(_, {:halt, acc}, _fun), do: {:halted, acc}
defp primes(v, {:suspend, acc}, fun) do
{:suspended, acc, &primes(v, &1, fun)}
end
defp primes(2, {:cont, acc}, fun) do
primes(3, fun.(2, acc), fun)
end
defp primes(3, {:cont, acc}, fun) do
primes({%{}, 5, 2, nil}, fun.(3, acc), fun)
end
defp primes({h, p, d, nil}, {:cont, acc}, fun) do
m = next_m(h, p, p, 4, true)
n = p + d
next_h = h |> Map.put(m, p)
primes({next_h, n, 6 - d, Map.get(next_h, n)}, fun.(p, acc), fun)
end
defp primes({h, q, d, b}, {:cont, _} = acc, fun) do
k = if rem(q + b, 3) == 0, do: 2, else: 4
m = next_m(h, b, q, k, true)
n = q + d
next_h = h |> Map.put(m, b) |> Map.delete(q)
primes({next_h, n, 6 - d, Map.get(next_h, n)}, acc, fun)
end
defp next_m(_, _, m, _, false), do: m
defp next_m(h, n, pre_m, k, true) do
m = pre_m + n * k
next_m(h, n, m, 6 - k, Map.has_key?(h, m))
end
end