1646 lines
39 KiB
Elixir
1646 lines
39 KiB
Elixir
defprotocol Enum.Iterator do
|
|
@moduledoc """
|
|
This is the protocol used by the `Enum` module.
|
|
Usually, when you invoke a function in the module `Enum`,
|
|
the first argument passed to `Enum` is a collection which
|
|
is forwarded to this protocol in order to retrieve information
|
|
on how to iterate the collection. That said, when:
|
|
|
|
Enum.map [1,2,3], &1 * 2
|
|
|
|
Is invoked, it invokes `Enum.Iterator.iterator([1,2,3])`
|
|
which returns all the information required by Enum.
|
|
Read each function documentation below for more information.
|
|
"""
|
|
|
|
@only [List, Record, Function]
|
|
|
|
@doc """
|
|
Iteration in Elixir happens with the help of a iterator
|
|
function. Every time this function is called, it must
|
|
return a tuple with two elements. The first element
|
|
is the next item and the second can be any Elixir term
|
|
which the function is going to receive as argument the
|
|
next time it is invoked.
|
|
|
|
When there are no more items to be iterated, the function
|
|
must return the atom `:stop`.
|
|
|
|
In order to retrieve this iterator function, Elixir invokes
|
|
`Enum.Iterator.iterator(collection)` which should return a
|
|
tuple with two elements: the first element is the iterator
|
|
function and the second is the first step of iteration.
|
|
|
|
As an example, here is the implementation of iterator for lists:
|
|
|
|
def iterator(list), do: { iterate(&1), iterate(list) }
|
|
defp iterate([h|t]), do: { h, t }
|
|
defp iterate([]), do: :stop
|
|
|
|
## Iterating lists
|
|
|
|
If a data structure needs to be converted to a list in order
|
|
to be iterated, the iterator function can simply return the
|
|
list and the Enum module will be able to take over the list
|
|
and retrieve the proper iterator function.
|
|
"""
|
|
def iterator(collection)
|
|
|
|
@doc """
|
|
The function used to retrieve the collection size.
|
|
"""
|
|
def count(collection)
|
|
end
|
|
|
|
defmodule Enum do
|
|
alias Enum.Iterator, as: I
|
|
|
|
@moduledoc """
|
|
Provides a set of algorithms that enumerate over collections according to the
|
|
`Enum.Iterator` protocol. Most of the functions in this module have two
|
|
flavours. If a given collection implements the mentioned protocol (like
|
|
list, for instance), you can do:
|
|
|
|
Enum.map [1,2,3], fn(x) -> x * 2 end
|
|
|
|
Depending on the type of the collection, the user-provided function will
|
|
accept a certain type of argument. For dicts, the argument is always a
|
|
`{ key, value }` tuple.
|
|
"""
|
|
|
|
@type t :: Enum.Iterator.t
|
|
@type element :: any
|
|
|
|
@doc """
|
|
Invokes the given `fun` for each item in the `collection` and returns true if
|
|
each invocation returns true as well, otherwise it short-circuits and returns
|
|
false.
|
|
|
|
## Examples
|
|
|
|
Enum.all? [2,4,6], fn(x) -> rem(x, 2) == 0 end
|
|
#=> true
|
|
|
|
Enum.all? [2,3,4], fn(x) -> rem(x, 2) == 0 end
|
|
#=> false
|
|
|
|
If no function is given, it defaults to checking if
|
|
all items in the collection evaluate to true.
|
|
|
|
Enum.all? [1,2,3] #=> true
|
|
Enum.all? [1,nil,3] #=> false
|
|
|
|
"""
|
|
@spec all?(t) :: boolean
|
|
@spec all?(t, (element -> boolean)) :: boolean
|
|
|
|
def all?(collection, fun // fn(x) -> x end)
|
|
|
|
def all?(collection, fun) when is_list(collection) do
|
|
do_all?(collection, fun)
|
|
end
|
|
|
|
def all?(collection, fun) do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } ->
|
|
do_all?(pointer, iterator, fun)
|
|
list when is_list(list) ->
|
|
do_all?(list, fun)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Invokes the given `fun` for each item in the `collection` and returns true if
|
|
at least one invocation returns true. Returns false otherwise.
|
|
|
|
## Examples
|
|
|
|
Enum.any? [2,4,6], fn(x) -> rem(x, 2) == 1 end
|
|
#=> false
|
|
|
|
Enum.any? [2,3,4], fn(x) -> rem(x, 2) == 1 end
|
|
#=> true
|
|
|
|
If no function is given, it defaults to checking if
|
|
at least one item in the collection evaluates to true.
|
|
|
|
Enum.any? [false,false,false] #=> false
|
|
Enum.any? [false,true,false] #=> true
|
|
|
|
"""
|
|
@spec any?(t) :: boolean
|
|
@spec any?(t, (element -> boolean)) :: boolean
|
|
|
|
def any?(collection, fun // fn(x) -> x end)
|
|
|
|
def any?(collection, fun) when is_list(collection) do
|
|
do_any?(collection, fun)
|
|
end
|
|
|
|
def any?(collection, fun) do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } ->
|
|
do_any?(pointer, iterator, fun)
|
|
list when is_list(list) ->
|
|
do_any?(list, fun)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Finds the element at the given index (zero-based).
|
|
Raises out of bounds error in case the given position
|
|
is outside the range of the collection.
|
|
|
|
Expects an ordered collection.
|
|
|
|
## Examples
|
|
|
|
Enum.at! [2,4,6], 0 #=> 2
|
|
Enum.at! [2,4,6], 2 #=> 6
|
|
Enum.at! [2,4,6], 4 #=> raises Enum.OutOfBoundsError
|
|
|
|
"""
|
|
@spec at!(t, non_neg_integer) :: element | no_return
|
|
def at!(collection, n) when is_list(collection) and n >= 0 do
|
|
do_at!(collection, n)
|
|
end
|
|
|
|
def at!(collection, n) when n >= 0 do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } ->
|
|
do_at!(pointer, iterator, n)
|
|
list when is_list(list) ->
|
|
do_at!(list, n)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Returns the collection size.
|
|
|
|
## Examples
|
|
|
|
Enum.count [1,2,3] #=> 3
|
|
|
|
"""
|
|
@spec count(t) :: non_neg_integer
|
|
def count(collection) do
|
|
I.count(collection)
|
|
end
|
|
|
|
@doc """
|
|
Counts for how many items the function returns true.
|
|
"""
|
|
@spec count(t, (element -> boolean)) :: non_neg_integer
|
|
def count(collection, fun) when is_list(collection) do
|
|
do_count(collection, fun)
|
|
end
|
|
|
|
def count(collection, fun) do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } ->
|
|
do_count(pointer, iterator, fun)
|
|
list when is_list(list) ->
|
|
do_count(list, fun)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Drops the first `count` items from the collection.
|
|
Expects an ordered collection.
|
|
|
|
## Examples
|
|
|
|
Enum.drop [1,2,3], 2 #=> [3]
|
|
Enum.drop [1,2,3], 10 #=> []
|
|
Enum.drop [1,2,3], 0 #=> [1,2,3]
|
|
|
|
"""
|
|
@spec drop(t, integer) :: list
|
|
def drop(collection, count) when is_list(collection) and count >= 0 do
|
|
do_drop(collection, count)
|
|
end
|
|
|
|
def drop(collection, count) when count >= 0 do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } ->
|
|
do_drop(pointer, iterator, count)
|
|
list when is_list(list) ->
|
|
do_drop(list, count)
|
|
end
|
|
end
|
|
|
|
def drop(collection, count) when count < 0 do
|
|
{ list, count } = iterate_and_count(collection, count)
|
|
drop(list, count)
|
|
end
|
|
|
|
@doc """
|
|
Drops items at the beginning of `collection` while `fun` returns true.
|
|
Expects an ordered collection.
|
|
|
|
## Examples
|
|
|
|
Enum.drop_while [1,2,3,4,5], fn(x) -> x < 3 end
|
|
#=> [3,4,5]
|
|
"""
|
|
@spec drop_while(t, (element -> boolean)) :: list
|
|
def drop_while(collection, fun) when is_list(collection) do
|
|
do_drop_while(collection, fun)
|
|
end
|
|
|
|
def drop_while(collection, fun) do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } ->
|
|
do_drop_while(pointer, iterator, fun)
|
|
list when is_list(list) ->
|
|
do_drop_while(list, fun)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Invokes the given `fun` for each item in the `collection`.
|
|
Returns the `collection` itself.
|
|
|
|
## Examples
|
|
|
|
Enum.each ['some', 'example'], fn(x) -> IO.puts x end
|
|
|
|
"""
|
|
@spec each(t, (element -> any)) :: :ok
|
|
def each(collection, fun) when is_list(collection) do
|
|
:lists.foreach(fun, collection)
|
|
:ok
|
|
end
|
|
|
|
def each(collection, fun) do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } ->
|
|
do_each(pointer, iterator, fun)
|
|
:ok
|
|
list when is_list(list) ->
|
|
each(list, fun)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Returns true if the collection is empty, otherwise false.
|
|
|
|
## Examples
|
|
|
|
Enum.empty? [] #=> true
|
|
Enum.empty? [1,2,3] #=> false
|
|
|
|
"""
|
|
@spec empty?(t) :: boolean
|
|
def empty?(collection) when is_list(collection) do
|
|
collection == []
|
|
end
|
|
|
|
def empty?(collection) do
|
|
case I.iterator(collection) do
|
|
{ _iterator, pointer } -> pointer == :stop
|
|
list when is_list(list) -> list == []
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Filters the collection, i.e. returns only those elements
|
|
for which `fun` returns true.
|
|
|
|
## Examples
|
|
|
|
Enum.filter [1, 2, 3], fn(x) -> rem(x, 2) == 0 end
|
|
#=> [2]
|
|
|
|
"""
|
|
@spec filter(t, (element -> boolean)) :: list
|
|
def filter(collection, fun) when is_list(collection) do
|
|
lc item inlist collection, fun.(item), do: item
|
|
end
|
|
|
|
def filter(collection, fun) do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } ->
|
|
do_filter(pointer, iterator, fun)
|
|
list when is_list(list) ->
|
|
filter(list, fun)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Filters the collection and maps its values in one pass.
|
|
|
|
## Examples
|
|
|
|
Enum.filter_map [1, 2, 3], fn(x) -> rem(x, 2) == 0 end, &1 * 2
|
|
#=> [4]
|
|
|
|
"""
|
|
@spec filter_map(t, (element -> boolean), (element -> element)) :: list
|
|
def filter_map(collection, filter, mapper) when is_list(collection) do
|
|
lc item inlist collection, filter.(item), do: mapper.(item)
|
|
end
|
|
|
|
def filter_map(collection, filter, mapper) do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } ->
|
|
do_filter_map(pointer, iterator, filter, mapper)
|
|
list when is_list(list) ->
|
|
filter_map(list, filter, mapper)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Returns the first item for which `fun` returns a truthy value. If no such
|
|
item is found, returns `ifnone`.
|
|
|
|
## Examples
|
|
|
|
Enum.find [2,4,6], fn(x) -> rem(x, 2) == 1 end
|
|
#=> nil
|
|
|
|
Enum.find [2,4,6], 0, fn(x) -> rem(x, 2) == 1 end
|
|
#=> 0
|
|
|
|
Enum.find [2,3,4], fn(x) -> rem(x, 2) == 1 end
|
|
#=> 3
|
|
|
|
"""
|
|
@spec find(t, (element -> any)) :: element | :nil
|
|
@spec find(t, any, (element -> any)) :: element | :nil
|
|
|
|
def find(collection, ifnone // nil, fun)
|
|
|
|
def find(collection, ifnone, fun) when is_list(collection) do
|
|
do_find(collection, ifnone, fun)
|
|
end
|
|
|
|
def find(collection, ifnone, fun) do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } ->
|
|
do_find(pointer, iterator, ifnone, fun)
|
|
list when is_list(list) ->
|
|
do_find(list, ifnone, fun)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Similar to find, but returns the value of the function
|
|
invocation instead of the element itself.
|
|
|
|
## Examples
|
|
|
|
Enum.find_value [2,4,6], fn(x) -> rem(x, 2) == 1 end
|
|
#=> nil
|
|
|
|
Enum.find_value [2,3,4], fn(x) -> rem(x, 2) == 1 end
|
|
#=> true
|
|
|
|
"""
|
|
@spec find_value(t, (element -> any)) :: any | :nil
|
|
@spec find_value(t, any, (element -> any)) :: any | :nil
|
|
|
|
def find_value(collection, ifnone // nil, fun)
|
|
|
|
def find_value(collection, ifnone, fun) when is_list(collection) do
|
|
do_find_value(collection, ifnone, fun)
|
|
end
|
|
|
|
def find_value(collection, ifnone, fun) do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } ->
|
|
do_find_value(pointer, iterator, ifnone, fun)
|
|
list when is_list(list) ->
|
|
do_find_value(list, ifnone, fun)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Similar to find, but returns the index (count starts with 0)
|
|
of the item instead of the element itself.
|
|
|
|
Expects an ordered collection.
|
|
|
|
## Examples
|
|
|
|
Enum.find_index [2,4,6], fn(x) -> rem(x, 2) == 1 end
|
|
#=> nil
|
|
|
|
Enum.find_index [2,3,4], fn(x) -> rem(x, 2) == 1 end
|
|
#=> 2
|
|
|
|
"""
|
|
@spec find_index(t, (element -> any)) :: non_neg_integer | :nil
|
|
def find_index(collection, fun) when is_list(collection) do
|
|
do_find_index(collection, 0, fun)
|
|
end
|
|
|
|
def find_index(collection, fun) do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } ->
|
|
do_find_index(pointer, iterator, 0, fun)
|
|
list when is_list(list) ->
|
|
do_find_index(list, 0, fun)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Returns the first item in the collection or nil otherwise.
|
|
|
|
## Examples
|
|
|
|
Enum.first [] #=> nil
|
|
Enum.first [1,2,3] #=> 1
|
|
|
|
"""
|
|
@spec first(t) :: :nil | element
|
|
def first([]), do: nil
|
|
def first([h|_]), do: h
|
|
|
|
def first(collection) do
|
|
case I.iterator(collection) do
|
|
{ _iterator, { h, _ } } -> h
|
|
{ _iterator, :stop } -> nil
|
|
list when is_list(list) -> first(list)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Joins the given `collection` according to `joiner`.
|
|
Joiner can be either a binary or a list and the
|
|
result will be of the same type as joiner. If
|
|
joiner is not passed at all, it defaults to an
|
|
empty binary.
|
|
|
|
All items in the collection must be convertible
|
|
to binary, otherwise an error is raised.
|
|
|
|
## Examples
|
|
|
|
Enum.join([1,2,3]) #=> "123"
|
|
Enum.join([1,2,3], " = ") #=> "1 = 2 = 3"
|
|
Enum.join([1,2,3], ' = ') #=> '1 = 2 = 3'
|
|
|
|
"""
|
|
@spec join(t) :: String.t
|
|
@spec join(t, String.t | char_list) :: String.t | char_list
|
|
def join(collection, joiner // "")
|
|
|
|
def join(collection, joiner) when is_list(joiner) do
|
|
binary_to_list join(collection, list_to_binary(joiner))
|
|
end
|
|
|
|
def join(collection, joiner) when is_list(collection) and is_binary(joiner) do
|
|
do_join(collection, joiner, nil)
|
|
end
|
|
|
|
def join(collection, joiner) when is_binary(joiner) do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } ->
|
|
do_join(pointer, iterator, joiner, nil)
|
|
list when is_list(list) ->
|
|
do_join(list, joiner, nil)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Returns a new collection, where each item is the result
|
|
of invoking `fun` on each corresponding item of `collection`.
|
|
For dicts, the function accepts a key-value tuple.
|
|
|
|
## Examples
|
|
|
|
Enum.map [1, 2, 3], fn(x) -> x * 2 end
|
|
#=> [2, 4, 6]
|
|
|
|
Enum.map [a: 1, b: 2], fn({k, v}) -> { k, -v } end
|
|
#=> [a: -1, b: -2]
|
|
|
|
"""
|
|
@spec map(t, (element -> any)) :: list
|
|
def map(collection, fun) when is_list(collection) do
|
|
lc item inlist collection, do: fun.(item)
|
|
end
|
|
|
|
def map(collection, fun) do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } ->
|
|
do_map(pointer, iterator, fun)
|
|
list when is_list(list) ->
|
|
map(list, fun)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Maps and joins the given `collection` in one pass.
|
|
Joiner can be either a binary or a list and the
|
|
result will be of the same type as joiner. If
|
|
joiner is not passed at all, it defaults to an
|
|
empty binary.
|
|
|
|
All items in the collection must be convertible
|
|
to binary, otherwise an error is raised.
|
|
|
|
## Examples
|
|
|
|
Enum.map_join([1,2,3], &1 * 2) #=> "246"
|
|
Enum.map_join([1,2,3], " = ", &1 * 2) #=> "2 = 4 = 6"
|
|
Enum.map_join([1,2,3], ' = ', &1 * 2) #=> '2 = 4 = 6'
|
|
|
|
"""
|
|
@spec map_join(t, (element -> any)) :: String.t
|
|
@spec map_join(t, String.t | char_list, (element -> any)) :: String.t | char_list
|
|
def map_join(collection, joiner // "", mapper)
|
|
|
|
def map_join(collection, joiner, mapper) when is_list(joiner) do
|
|
binary_to_list map_join(collection, list_to_binary(joiner), mapper)
|
|
end
|
|
|
|
def map_join(collection, joiner, mapper) when is_list(collection) and is_binary(joiner) do
|
|
do_map_join(collection, mapper, joiner, nil)
|
|
end
|
|
|
|
def map_join(collection, joiner, mapper) when is_binary(joiner) do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } ->
|
|
do_map_join(pointer, iterator, mapper, joiner, nil)
|
|
list when is_list(list) ->
|
|
do_map_join(list, mapper, joiner, nil)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Invokes the given `fun` for each item in the `collection`
|
|
while also keeping an accumulator. Returns a tuple where
|
|
the first element is the mapped collection and the second
|
|
one is the final accumulator.
|
|
|
|
For dicts, the first tuple element has to be a { key, value }
|
|
tuple itself.
|
|
|
|
## Examples
|
|
|
|
Enum.map_reduce [1, 2, 3], 0, fn(x, acc) -> { x * 2, x + acc } end
|
|
#=> { [2, 4, 6], 6 }
|
|
|
|
"""
|
|
@spec map_reduce(t, any, (element, any -> any)) :: any
|
|
def map_reduce(collection, acc, f) when is_list(collection) do
|
|
:lists.mapfoldl(f, acc, collection)
|
|
end
|
|
|
|
def map_reduce(collection, acc, fun) do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } ->
|
|
do_map_reduce(pointer, iterator, [], acc, fun)
|
|
list when is_list(list) ->
|
|
map_reduce(list, acc, fun)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Partitions `collection` into two where the first one contains elements
|
|
for which `fun` returns a truthy value, and the second one -- for which `fun`
|
|
returns false or nil.
|
|
|
|
## Examples
|
|
|
|
Enum.partition [1, 2, 3], fn(x) -> rem(x, 2) == 0 end
|
|
#=> { [2], [1,3] }
|
|
|
|
"""
|
|
@spec partition(t, (element -> any)) :: {list, list}
|
|
def partition(collection, fun) when is_list(collection) do
|
|
do_partition(collection, fun, [], [])
|
|
end
|
|
|
|
def partition(collection, fun) do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } ->
|
|
do_partition(pointer, iterator, fun, [], [])
|
|
list when is_list(list) ->
|
|
do_partition(list, fun, [], [])
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Invokes `fun` for each element in the collection passing that element and the
|
|
accumulator `acc` as arguments. `fun`'s return value is stored in `acc`.
|
|
Returns the accumulator.
|
|
|
|
## Examples
|
|
|
|
Enum.reduce [1, 2, 3], 0, fn(x, acc) -> x + acc end
|
|
#=> 6
|
|
|
|
"""
|
|
@spec reduce(t, any, (element, any -> any)) :: any
|
|
def reduce(collection, acc, fun) when is_list(collection) do
|
|
:lists.foldl(fun, acc, collection)
|
|
end
|
|
|
|
def reduce(collection, acc, fun) do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } ->
|
|
do_reduce(pointer, iterator, acc, fun)
|
|
list when is_list(list) ->
|
|
reduce(list, acc, fun)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Reverses the collection.
|
|
|
|
## Examples
|
|
|
|
Enum.reverse [1, 2, 3]
|
|
#=> [3, 2, 1]
|
|
|
|
"""
|
|
@spec reverse(t) :: list
|
|
def reverse(collection) when is_list(collection) do
|
|
:lists.reverse(collection)
|
|
end
|
|
|
|
def reverse(collection) do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } -> do_reverse(pointer, iterator, [])
|
|
list when is_list(list) -> reverse(list)
|
|
end
|
|
end
|
|
|
|
@doc false
|
|
@spec qsort(t) :: list
|
|
def qsort(collection) when is_list(collection) do
|
|
IO.write "[WARNING] Enum.qsort is deprecated, please use Enum.sort instead\n#{Exception.formatted_stacktrace}"
|
|
do_list_qsort(collection, [])
|
|
end
|
|
|
|
def qsort(collection) do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } ->
|
|
IO.write "[WARNING] Enum.qsort is deprecated, please use Enum.sort instead\n#{Exception.formatted_stacktrace}"
|
|
do_qsort(pointer, iterator, [])
|
|
list when is_list(list) ->
|
|
qsort(list)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Sorts the collection using the merge sort algorithm.
|
|
|
|
## Examples
|
|
|
|
Enum.sort [3,2,1] #=> [1,2,3]
|
|
|
|
"""
|
|
@spec sort(t) :: list
|
|
def sort(collection) when is_list(collection) do
|
|
:lists.sort(collection)
|
|
end
|
|
|
|
def sort(collection) do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } ->
|
|
do_sort(pointer, iterator, &1 <= &2)
|
|
list when is_list(list) ->
|
|
sort(list)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Sorts the collection using the merge sort algorithm.
|
|
|
|
## Examples
|
|
|
|
Enum.sort [3,2,1], &1 > &2 #=> [1,2,3]
|
|
|
|
"""
|
|
@spec sort(t, (element, element -> boolean)) :: list
|
|
def sort(collection, fun) when is_list(collection) do
|
|
:lists.sort(fun, collection)
|
|
end
|
|
|
|
def sort(collection, fun) do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } ->
|
|
do_sort(pointer, iterator, fun)
|
|
list when is_list(list) ->
|
|
sort(list, fun)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Splits the enumerable into two collections, leaving `count`
|
|
elements in the first one. If `count` is a negative number,
|
|
it starts couting from the back to the beginning of the
|
|
collection.
|
|
|
|
Be aware that a negative `count` implies the collection
|
|
will be iterate twice. One to calculate the position and
|
|
another one to do the actual splitting.
|
|
|
|
## Examples
|
|
|
|
Enum.split [1,2,3], 2 #=> { [1,2], [3] }
|
|
Enum.split [1,2,3], 10 #=> { [1,2,3], [] }
|
|
Enum.split [1,2,3], 0 #=> { [], [1,2,3] }
|
|
Enum.split [1,2,3], -1 #=> { [1,2], [3] }
|
|
Enum.split [1,2,3], -5 #=> { [], [1,2,3] }
|
|
|
|
"""
|
|
@spec split(t, integer) :: {list, list}
|
|
def split(collection, count) when is_list(collection) and count >= 0 do
|
|
do_split(collection, count, [])
|
|
end
|
|
|
|
def split(collection, count) when count >= 0 do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } ->
|
|
do_split(pointer, iterator, count, [])
|
|
list when is_list(list) ->
|
|
do_split(list, count, [])
|
|
end
|
|
end
|
|
|
|
def split(collection, count) when count < 0 do
|
|
{ list, count } = iterate_and_count(collection, count)
|
|
split(list, count)
|
|
end
|
|
|
|
@doc """
|
|
Splits `collection` at the first element, for which `fun` returns true.
|
|
Expects an ordered collection.
|
|
|
|
## Examples
|
|
|
|
Enum.split_while [1,2,3,4], fn x -> x == 2 end
|
|
#=> { [1], [2, 3, 4] }
|
|
"""
|
|
@spec split_while(t, (element -> boolean)) :: {list, list}
|
|
def split_while(collection, fun) when is_list(collection) do
|
|
do_split_while(collection, fun, [])
|
|
end
|
|
|
|
def split_while(collection, fun) do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } ->
|
|
do_split_while(pointer, iterator, fun, [])
|
|
list when is_list(list) ->
|
|
do_split_while(list, fun, [])
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Takes the first `count` items from the collection. Expects an ordered
|
|
collection.
|
|
|
|
## Examples
|
|
|
|
Enum.take [1,2,3], 2 #=> [1,2]
|
|
Enum.take [1,2,3], 10 #=> [1,2,3]
|
|
Enum.take [1,2,3], 0 #=> []
|
|
|
|
"""
|
|
@spec take(t, integer) :: list
|
|
def take(collection, count) when is_list(collection) and count >= 0 do
|
|
do_take(collection, count)
|
|
end
|
|
|
|
def take(collection, count) when count >= 0 do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } ->
|
|
do_take(pointer, iterator, count)
|
|
list when is_list(list) ->
|
|
do_take(list, count)
|
|
end
|
|
end
|
|
|
|
def take(collection, count) when count < 0 do
|
|
{ list, count } = iterate_and_count(collection, count)
|
|
take(list, count)
|
|
end
|
|
|
|
@doc """
|
|
Takes the items at the beginning of `collection` while `fun` returns true.
|
|
Expects an ordered collection.
|
|
|
|
## Examples
|
|
|
|
Enum.take_while [1,2,3], fn(x) -> x < 3 end
|
|
#=> [1, 2]
|
|
|
|
"""
|
|
@spec take_while(t, (element -> boolean)) :: list
|
|
def take_while(collection, fun) when is_list(collection) do
|
|
do_take_while(collection, fun)
|
|
end
|
|
|
|
def take_while(collection, fun) do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } ->
|
|
do_take_while(pointer, iterator, fun)
|
|
list when is_list(list) ->
|
|
do_take_while(list, fun)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Iterates the enumerable removing all duplicated items.
|
|
|
|
## Examples
|
|
|
|
Enum.uniq [1,2,3,2,1]
|
|
#=> [1, 2, 3]
|
|
|
|
"""
|
|
@spec uniq(t) :: list
|
|
def uniq(collection) when is_list(collection) do
|
|
do_uniq(collection, [])
|
|
end
|
|
|
|
def uniq(collection) do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } ->
|
|
do_uniq(pointer, iterator, [])
|
|
list when is_list(list) ->
|
|
do_uniq(list, [])
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Zips corresponding elements from two collections into one list
|
|
of tuples. The number of elements in the resulting list is
|
|
dictated by the first enum. In case the second list is shorter,
|
|
values are filled with nil.
|
|
"""
|
|
@spec zip(t, t) :: [{any, any}]
|
|
def zip(coll1, coll2) when is_list(coll1) do
|
|
do_zip(coll1, iterator(coll2))
|
|
end
|
|
|
|
def zip(coll1, coll2) do
|
|
case I.iterator(coll1) do
|
|
{ iterator, pointer } ->
|
|
do_zip(pointer, iterator, iterator(coll2))
|
|
list when is_list(list) ->
|
|
do_zip(list, iterator(coll2))
|
|
end
|
|
end
|
|
|
|
## Helpers
|
|
|
|
defp iterator(collection) when is_list(collection), do: collection
|
|
defp iterator(collection), do: I.iterator(collection)
|
|
|
|
defp to_list({ h, next }, iterator) do
|
|
[h|to_list(iterator.(next), iterator)]
|
|
end
|
|
|
|
defp to_list(:stop, _) do
|
|
[]
|
|
end
|
|
|
|
defp iterate_and_count(collection, count) do
|
|
{ list, total_items } = do_iterate_and_count(collection)
|
|
{ list, max(0, total_items - abs(count)) }
|
|
end
|
|
|
|
defp do_iterate_and_count(collection) when is_list(collection) do
|
|
{ collection, length(collection) }
|
|
end
|
|
|
|
defp do_iterate_and_count(collection) do
|
|
case I.iterator(collection) do
|
|
{ iterator, pointer } ->
|
|
reducer = fn(x, acc) -> { x, acc + 1 } end
|
|
do_map_reduce(pointer, iterator, [], 0, reducer)
|
|
list when is_list(list) ->
|
|
do_iterate_and_count(list)
|
|
end
|
|
end
|
|
|
|
## Implementations
|
|
|
|
## all?
|
|
|
|
defp do_all?([h|t], fun) do
|
|
if fun.(h) do
|
|
do_all?(t, fun)
|
|
else
|
|
false
|
|
end
|
|
end
|
|
|
|
defp do_all?([], _) do
|
|
true
|
|
end
|
|
|
|
defp do_all?({ h, next }, iterator, fun) do
|
|
if fun.(h) do
|
|
do_all?(iterator.(next), iterator, fun)
|
|
else
|
|
false
|
|
end
|
|
end
|
|
|
|
defp do_all?(:stop, _, _) do
|
|
true
|
|
end
|
|
|
|
## any?
|
|
|
|
defp do_any?([h|t], fun) do
|
|
if fun.(h) do
|
|
true
|
|
else
|
|
do_any?(t, fun)
|
|
end
|
|
end
|
|
|
|
defp do_any?([], _) do
|
|
false
|
|
end
|
|
|
|
defp do_any?({ h, next }, iterator, fun) do
|
|
if fun.(h) do
|
|
true
|
|
else
|
|
do_any?(iterator.(next), iterator, fun)
|
|
end
|
|
end
|
|
|
|
defp do_any?(:stop, _, _) do
|
|
false
|
|
end
|
|
|
|
## at!
|
|
|
|
defp do_at!([h|_], 0), do: h
|
|
defp do_at!([_|t], n), do: do_at!(t, n - 1)
|
|
defp do_at!([], _), do: raise Enum.OutOfBoundsError
|
|
|
|
defp do_at!({ h, _next }, _iterator, 0), do: h
|
|
defp do_at!({ _, next }, iterator, n), do: do_at!(iterator.(next), iterator, n - 1)
|
|
defp do_at!(:stop, _iterator, _), do: raise Enum.OutOfBoundsError
|
|
|
|
## count
|
|
|
|
defp do_count([h|t], fun) do
|
|
if fun.(h) do
|
|
1 + do_count(t, fun)
|
|
else
|
|
do_count(t, fun)
|
|
end
|
|
end
|
|
|
|
defp do_count([], _) do
|
|
0
|
|
end
|
|
|
|
defp do_count({ h, next }, iterator, fun) do
|
|
if fun.(h) do
|
|
1 + do_count(iterator.(next), iterator, fun)
|
|
else
|
|
do_count(iterator.(next), iterator, fun)
|
|
end
|
|
end
|
|
|
|
defp do_count(:stop, _, _) do
|
|
0
|
|
end
|
|
|
|
## drop
|
|
|
|
defp do_drop([_|t], counter) when counter > 0 do
|
|
do_drop(t, counter - 1)
|
|
end
|
|
|
|
defp do_drop(list, 0) do
|
|
list
|
|
end
|
|
|
|
defp do_drop([], _) do
|
|
[]
|
|
end
|
|
|
|
defp do_drop({ _, next }, iterator, counter) when counter > 0 do
|
|
do_drop(iterator.(next), iterator, counter - 1)
|
|
end
|
|
|
|
defp do_drop(extra, iterator, 0) do
|
|
to_list(extra, iterator)
|
|
end
|
|
|
|
defp do_drop(:stop, _, _) do
|
|
[]
|
|
end
|
|
|
|
## drop_while
|
|
|
|
defp do_drop_while([h|t], fun) do
|
|
if fun.(h) do
|
|
do_drop_while(t, fun)
|
|
else
|
|
[h|t]
|
|
end
|
|
end
|
|
|
|
defp do_drop_while([], _) do
|
|
[]
|
|
end
|
|
|
|
defp do_drop_while({ h, next } = extra, iterator, fun) do
|
|
if fun.(h) do
|
|
do_drop_while(iterator.(next), iterator, fun)
|
|
else
|
|
to_list(extra, iterator)
|
|
end
|
|
end
|
|
|
|
defp do_drop_while(:stop, _, _) do
|
|
[]
|
|
end
|
|
|
|
## find
|
|
|
|
defp do_find([h|t], ifnone, fun) do
|
|
if fun.(h) do
|
|
h
|
|
else
|
|
do_find(t, ifnone, fun)
|
|
end
|
|
end
|
|
|
|
defp do_find([], ifnone, _) do
|
|
ifnone
|
|
end
|
|
|
|
defp do_find({ h, next }, iterator, ifnone, fun) do
|
|
if fun.(h) do
|
|
h
|
|
else
|
|
do_find(iterator.(next), iterator, ifnone, fun)
|
|
end
|
|
end
|
|
|
|
defp do_find(:stop, _, ifnone, _) do
|
|
ifnone
|
|
end
|
|
|
|
## find_index
|
|
|
|
defp do_find_index([h|t], counter, fun) do
|
|
if fun.(h) do
|
|
counter
|
|
else
|
|
do_find_index(t, counter + 1, fun)
|
|
end
|
|
end
|
|
|
|
defp do_find_index([], _, _) do
|
|
nil
|
|
end
|
|
|
|
defp do_find_index({ h, next }, iterator, counter, fun) do
|
|
if fun.(h) do
|
|
counter
|
|
else
|
|
do_find_index(iterator.(next), iterator, counter + 1, fun)
|
|
end
|
|
end
|
|
|
|
defp do_find_index(:stop, _, _, _) do
|
|
nil
|
|
end
|
|
|
|
## find_value
|
|
|
|
defp do_find_value([h|t], ifnone, fun) do
|
|
fun.(h) || do_find_value(t, ifnone, fun)
|
|
end
|
|
|
|
defp do_find_value([], ifnone, _) do
|
|
ifnone
|
|
end
|
|
|
|
defp do_find_value({ h, next }, iterator, ifnone, fun) do
|
|
fun.(h) || do_find_value(iterator.(next), iterator, ifnone, fun)
|
|
end
|
|
|
|
defp do_find_value(:stop, _, ifnone, _) do
|
|
ifnone
|
|
end
|
|
|
|
## each
|
|
|
|
defp do_each({ h, next }, iterator, fun) do
|
|
fun.(h)
|
|
do_each(iterator.(next), iterator, fun)
|
|
end
|
|
|
|
defp do_each(:stop, _, _) do
|
|
[]
|
|
end
|
|
|
|
## filter
|
|
|
|
defp do_filter({ h, next }, iterator, fun) do
|
|
if fun.(h) do
|
|
[h|do_filter(iterator.(next), iterator, fun)]
|
|
else
|
|
do_filter(iterator.(next), iterator, fun)
|
|
end
|
|
end
|
|
|
|
defp do_filter(:stop, _, _) do
|
|
[]
|
|
end
|
|
|
|
## filter_map
|
|
|
|
defp do_filter_map({ h, next }, iterator, filter, mapper) do
|
|
if filter.(h) do
|
|
[mapper.(h)|do_filter_map(iterator.(next), iterator, filter, mapper)]
|
|
else
|
|
do_filter_map(iterator.(next), iterator, filter, mapper)
|
|
end
|
|
end
|
|
|
|
defp do_filter_map(:stop, _, _, _) do
|
|
[]
|
|
end
|
|
|
|
## join
|
|
|
|
defp do_join([h|t], joiner, nil) do
|
|
do_join(t, joiner, to_binary(h))
|
|
end
|
|
|
|
defp do_join([h|t], joiner, acc) do
|
|
acc = << acc :: binary, joiner :: binary, to_binary(h) :: binary >>
|
|
do_join(t, joiner, acc)
|
|
end
|
|
|
|
defp do_join([], _joiner, acc) do
|
|
acc || ""
|
|
end
|
|
|
|
defp do_join({ h, next }, iterator, joiner, nil) do
|
|
do_join(iterator.(next), iterator, joiner, to_binary(h))
|
|
end
|
|
|
|
defp do_join({ h, next }, iterator, joiner, acc) do
|
|
acc = << acc :: binary, joiner :: binary, to_binary(h) :: binary >>
|
|
do_join(iterator.(next), iterator, joiner, acc)
|
|
end
|
|
|
|
defp do_join(:stop, _, _joiner, acc) do
|
|
acc || ""
|
|
end
|
|
|
|
## map
|
|
|
|
defp do_map({ h, next }, iterator, fun) do
|
|
[fun.(h)|do_map(iterator.(next), iterator, fun)]
|
|
end
|
|
|
|
defp do_map(:stop, _, _) do
|
|
[]
|
|
end
|
|
|
|
## map join
|
|
|
|
defp do_map_join([h|t], mapper, joiner, nil) do
|
|
do_map_join(t, mapper, joiner, to_binary(mapper.(h)))
|
|
end
|
|
|
|
defp do_map_join([h|t], mapper, joiner, acc) do
|
|
acc = << acc :: binary, joiner :: binary, to_binary(mapper.(h)) :: binary >>
|
|
do_map_join(t, mapper, joiner, acc)
|
|
end
|
|
|
|
defp do_map_join([], _mapper, _joiner, acc) do
|
|
acc || ""
|
|
end
|
|
|
|
defp do_map_join({ h, next }, iterator, mapper, joiner, nil) do
|
|
do_map_join(iterator.(next), iterator, mapper, joiner, to_binary(mapper.(h)))
|
|
end
|
|
|
|
defp do_map_join({ h, next }, iterator, mapper, joiner, acc) do
|
|
acc = << acc :: binary, joiner :: binary, to_binary(mapper.(h)) :: binary >>
|
|
do_map_join(iterator.(next), iterator, mapper, joiner, acc)
|
|
end
|
|
|
|
defp do_map_join(:stop, _, _mapper, _joiner, acc) do
|
|
acc || ""
|
|
end
|
|
|
|
## map_reduce
|
|
|
|
defp do_map_reduce({ h, next }, iterator, list_acc, acc, f) do
|
|
{ result, acc } = f.(h, acc)
|
|
do_map_reduce(iterator.(next), iterator, [result|list_acc], acc, f)
|
|
end
|
|
|
|
defp do_map_reduce(:stop, _, list_acc, acc, _f) do
|
|
{ :lists.reverse(list_acc), acc }
|
|
end
|
|
|
|
## partition
|
|
|
|
defp do_partition([h|t], fun, acc1, acc2) do
|
|
if fun.(h) do
|
|
do_partition(t, fun, [h|acc1], acc2)
|
|
else
|
|
do_partition(t, fun, acc1, [h|acc2])
|
|
end
|
|
end
|
|
|
|
defp do_partition([], _, acc1, acc2) do
|
|
{ :lists.reverse(acc1), :lists.reverse(acc2) }
|
|
end
|
|
|
|
defp do_partition({ h, next }, iterator, fun, acc1, acc2) do
|
|
if fun.(h) do
|
|
do_partition(iterator.(next), iterator, fun, [h|acc1], acc2)
|
|
else
|
|
do_partition(iterator.(next), iterator, fun, acc1, [h|acc2])
|
|
end
|
|
end
|
|
|
|
defp do_partition(:stop, _, _, acc1, acc2) do
|
|
{ :lists.reverse(acc1), :lists.reverse(acc2) }
|
|
end
|
|
|
|
## reduce
|
|
|
|
defp do_reduce({ h, next }, iterator, acc, fun) do
|
|
do_reduce(iterator.(next), iterator, fun.(h, acc), fun)
|
|
end
|
|
|
|
defp do_reduce(:stop, _, acc, _) do
|
|
acc
|
|
end
|
|
|
|
## reverse
|
|
|
|
defp do_reverse({ h, next }, iterator, acc) do
|
|
do_reverse(iterator.(next), iterator, [h|acc])
|
|
end
|
|
|
|
defp do_reverse(:stop, _, acc) do
|
|
acc
|
|
end
|
|
|
|
## sort
|
|
|
|
defp do_sort(extra, iterator, fun) do
|
|
case sort_take(extra, iterator, 2, []) do
|
|
{ [y, x], next } -> sort_split(y, x, next, iterator, fun, [], [], fun.(x, y))
|
|
{ other, _ } -> other
|
|
end
|
|
end
|
|
|
|
|
|
defp sort_take({ h, next }, iterator, counter, acc) when counter > 0 do
|
|
sort_take(iterator.(next), iterator, counter - 1, [h|acc])
|
|
end
|
|
|
|
defp sort_take(extra, _iterator, 0, acc) do
|
|
{ acc, extra }
|
|
end
|
|
|
|
defp sort_take(:stop, _, _, acc) do
|
|
{ acc, :stop }
|
|
end
|
|
|
|
|
|
defp sort_split(y, x, { z, next }, iterator, fun, r, rs, bool) do
|
|
cond do
|
|
fun.(y, z) == bool ->
|
|
sort_split(z, y, iterator.(next), iterator, fun, [x | r], rs, bool)
|
|
fun.(x, z) == bool ->
|
|
sort_split(y, z, iterator.(next), iterator, fun, [x | r], rs, bool)
|
|
r == [] ->
|
|
sort_split(y, x, iterator.(next), iterator, fun, [z], rs, bool)
|
|
true ->
|
|
sort_split_pivot(y, x, iterator.(next), iterator, fun, r, rs, z, bool)
|
|
end
|
|
end
|
|
|
|
defp sort_split(y, x, :stop, _iterator, fun, r, rs, bool) do
|
|
sort_merge([[y, x | r] | rs], fun, bool)
|
|
end
|
|
|
|
defp sort_split_pivot(y, x, { z, next }, iterator, fun, r, rs, s, bool) do
|
|
cond do
|
|
fun.(y, z) == bool ->
|
|
sort_split_pivot(z, y, iterator.(next), iterator, fun, [x | r], rs, s, bool)
|
|
fun.(x, z) == bool ->
|
|
sort_split_pivot(y, z, iterator.(next), iterator, fun, [x | r], rs, s, bool)
|
|
fun.(s, z) == bool ->
|
|
sort_split(z, s, iterator.(next), iterator, fun, [], [[y, x | r] | rs], bool)
|
|
true ->
|
|
sort_split(s, z, iterator.(next), iterator, fun, [], [[y, x | r] | rs], bool)
|
|
end
|
|
end
|
|
|
|
defp sort_split_pivot(y, x, :stop, _iterator, fun, r, rs, s, bool) do
|
|
sort_merge([[s], [[y, x | r] | rs]], fun, bool)
|
|
end
|
|
|
|
|
|
defp sort_merge(list, fun, true), do:
|
|
reverse_sort_merge(list, [], fun, true)
|
|
|
|
defp sort_merge(list, fun, false), do:
|
|
sort_merge(list, [], fun, false)
|
|
|
|
|
|
defp sort_merge([t1, [h2 | t2] | l], acc, fun, true), do:
|
|
sort_merge(l, [sort_merge_1(t1, h2, t2, [], fun, false) | acc], fun, true)
|
|
|
|
defp sort_merge([[h2 | t2], t1 | l], acc, fun, false), do:
|
|
sort_merge(l, [sort_merge_1(t1, h2, t2, [], fun, false) | acc], fun, false)
|
|
|
|
defp sort_merge([l], [], _fun, _bool), do: l
|
|
|
|
defp sort_merge([l], acc, fun, bool), do:
|
|
reverse_sort_merge([:lists.reverse(l, []) | acc], [], fun, bool)
|
|
|
|
defp sort_merge([], acc, fun, bool), do:
|
|
reverse_sort_merge(acc, [], fun, bool)
|
|
|
|
|
|
defp reverse_sort_merge([[h2 | t2], t1 | l], acc, fun, true), do:
|
|
reverse_sort_merge(l, [sort_merge_1(t1, h2, t2, [], fun, true) | acc], fun, true)
|
|
|
|
defp reverse_sort_merge([t1, [h2 | t2] | l], acc, fun, false), do:
|
|
reverse_sort_merge(l, [sort_merge_1(t1, h2, t2, [], fun, true) | acc], fun, false)
|
|
|
|
defp reverse_sort_merge([l], acc, fun, bool), do:
|
|
sort_merge([:lists.reverse(l, []) | acc], [], fun, bool)
|
|
|
|
defp reverse_sort_merge([], acc, fun, bool), do:
|
|
sort_merge(acc, [], fun, bool)
|
|
|
|
|
|
defp sort_merge_1([h1 | t1], h2, t2, m, fun, bool) do
|
|
if fun.(h1, h2) == bool do
|
|
sort_merge_2(h1, t1, t2, [h2 | m], fun, bool)
|
|
else
|
|
sort_merge_1(t1, h2, t2, [h1 | m], fun, bool)
|
|
end
|
|
end
|
|
|
|
defp sort_merge_1([], h2, t2, m, _fun, _bool), do:
|
|
:lists.reverse(t2, [h2 | m])
|
|
|
|
|
|
defp sort_merge_2(h1, t1, [h2 | t2], m, fun, bool) do
|
|
if fun.(h1, h2) == bool do
|
|
sort_merge_2(h1, t1, t2, [h2 | m], fun, bool)
|
|
else
|
|
sort_merge_1(t1, h2, t2, [h1 | m], fun, bool)
|
|
end
|
|
end
|
|
|
|
defp sort_merge_2(h1, t1, [], m, _fun, _bool), do:
|
|
:lists.reverse(t1, [h1 | m])
|
|
|
|
## split
|
|
|
|
defp do_split([h|t], counter, acc) when counter > 0 do
|
|
do_split(t, counter - 1, [h|acc])
|
|
end
|
|
|
|
defp do_split(list, 0, acc) do
|
|
{ :lists.reverse(acc), list }
|
|
end
|
|
|
|
defp do_split([], _, acc) do
|
|
{ :lists.reverse(acc), [] }
|
|
end
|
|
|
|
defp do_split({ h, next }, iterator, counter, acc) when counter > 0 do
|
|
do_split(iterator.(next), iterator, counter - 1, [h|acc])
|
|
end
|
|
|
|
defp do_split(extra, iterator, 0, acc) do
|
|
{ :lists.reverse(acc), to_list(extra, iterator) }
|
|
end
|
|
|
|
defp do_split(:stop, _, _, acc) do
|
|
{ :lists.reverse(acc), [] }
|
|
end
|
|
|
|
## split_while
|
|
|
|
defp do_split_while([h|t], fun, acc) do
|
|
if fun.(h) do
|
|
do_split_while(t, fun, [h|acc])
|
|
else
|
|
{ :lists.reverse(acc), [h|t] }
|
|
end
|
|
end
|
|
|
|
defp do_split_while([], _, acc) do
|
|
{ :lists.reverse(acc), [] }
|
|
end
|
|
|
|
defp do_split_while({ h, next } = extra, iterator, fun, acc) do
|
|
if fun.(h) do
|
|
do_split_while(iterator.(next), iterator, fun, [h|acc])
|
|
else
|
|
{ :lists.reverse(acc), to_list(extra, iterator) }
|
|
end
|
|
end
|
|
|
|
defp do_split_while(:stop, _, _, acc) do
|
|
{ :lists.reverse(acc), [] }
|
|
end
|
|
|
|
## qsort (lists)
|
|
|
|
defp do_list_qsort([], acc) do
|
|
acc
|
|
end
|
|
|
|
defp do_list_qsort([h|t], acc) do
|
|
do_list_qsort_part(h, t, {[], [h], []}, acc)
|
|
end
|
|
|
|
defp do_list_qsort_part(_, [], { l, e, g }, acc) do
|
|
do_list_qsort(l, e ++ do_list_qsort(g, acc))
|
|
end
|
|
|
|
defp do_list_qsort_part(x, [h|t], { l, e, g }, acc) do
|
|
cond do
|
|
h < x ->
|
|
do_list_qsort_part(x, t, { [h|l], e, g }, acc)
|
|
h > x ->
|
|
do_list_qsort_part(x, t, { l, e, [h|g] }, acc)
|
|
true ->
|
|
do_list_qsort_part(x, t, { l, [h|e], g }, acc)
|
|
end
|
|
end
|
|
|
|
## qsort (iterator)
|
|
|
|
defp do_qsort({ h, next }, iterator, acc) do
|
|
do_qsort_part(h, iterator.(next), iterator, {[], [h], []}, acc)
|
|
end
|
|
|
|
defp do_qsort(:stop, _iterator, acc) do
|
|
acc
|
|
end
|
|
|
|
defp do_qsort_part(_, :stop, _iterator, { l, e, g }, acc) do
|
|
do_list_qsort(l, e ++ do_list_qsort(g, acc))
|
|
end
|
|
|
|
defp do_qsort_part(x, { h, next }, iterator, { l, e, g }, acc) do
|
|
cond do
|
|
h < x ->
|
|
do_qsort_part(x, iterator.(next), iterator, { [h|l], e, g }, acc)
|
|
h > x ->
|
|
do_qsort_part(x, iterator.(next), iterator, { l, e, [h|g] }, acc)
|
|
true ->
|
|
do_qsort_part(x, iterator.(next), iterator, { l, [h|e], g }, acc)
|
|
end
|
|
end
|
|
|
|
## take
|
|
|
|
defp do_take([h|t], counter) when counter > 0 do
|
|
[h|do_take(t, counter - 1)]
|
|
end
|
|
|
|
defp do_take(_list, 0) do
|
|
[]
|
|
end
|
|
|
|
defp do_take([], _) do
|
|
[]
|
|
end
|
|
|
|
defp do_take({ h, next }, iterator, counter) when counter > 0 do
|
|
[h|do_take(iterator.(next), iterator, counter - 1)]
|
|
end
|
|
|
|
defp do_take(_extra, _iterator, 0) do
|
|
[]
|
|
end
|
|
|
|
defp do_take(:stop, _, _) do
|
|
[]
|
|
end
|
|
|
|
## take_while
|
|
|
|
defp do_take_while([h|t], fun) do
|
|
if fun.(h) do
|
|
[h|do_take_while(t, fun)]
|
|
else
|
|
[]
|
|
end
|
|
end
|
|
|
|
defp do_take_while([], _) do
|
|
[]
|
|
end
|
|
|
|
defp do_take_while({ h, next }, iterator, fun) do
|
|
if fun.(h) do
|
|
[h|do_take_while(iterator.(next), iterator, fun)]
|
|
else
|
|
[]
|
|
end
|
|
end
|
|
|
|
defp do_take_while(:stop, _, _) do
|
|
[]
|
|
end
|
|
|
|
## uniq
|
|
|
|
defp do_uniq([h|t], acc) do
|
|
case :lists.member(h, acc) do
|
|
true -> do_uniq(t, acc)
|
|
false -> [h|do_uniq(t, [h|acc])]
|
|
end
|
|
end
|
|
|
|
defp do_uniq([], _acc) do
|
|
[]
|
|
end
|
|
|
|
defp do_uniq({ h, next }, iterator, acc) do
|
|
case :lists.member(h, acc) do
|
|
true -> do_uniq(iterator.(next), iterator, acc)
|
|
false -> [h|do_uniq(iterator.(next), iterator, [h|acc])]
|
|
end
|
|
end
|
|
|
|
defp do_uniq(:stop, _, _acc) do
|
|
[]
|
|
end
|
|
|
|
## zip
|
|
|
|
defp do_zip([h1|next1], other) do
|
|
{ h2, next2 } = do_zip_next(other)
|
|
[{ h1, h2 }|do_zip(next1, next2)]
|
|
end
|
|
|
|
defp do_zip([], _) do
|
|
[]
|
|
end
|
|
|
|
defp do_zip({ h1, next1 }, iterator, other) do
|
|
{ h2, next2 } = do_zip_next(other)
|
|
[{ h1, h2 }|do_zip(iterator.(next1), iterator, next2)]
|
|
end
|
|
|
|
defp do_zip(:stop, _, _) do
|
|
[]
|
|
end
|
|
|
|
defp do_zip_next([h|t]), do: { h, t }
|
|
defp do_zip_next([]), do: { nil, [] }
|
|
|
|
defp do_zip_next({ iterator, { h, next } }) do
|
|
{ h, { iterator, iterator.(next) } }
|
|
end
|
|
|
|
defp do_zip_next({ _iterator, :stop } = i) do
|
|
{ nil, i }
|
|
end
|
|
end
|
|
|
|
defimpl Enum.Iterator, for: List do
|
|
def iterator(list), do: list
|
|
def count(list), do: length(list)
|
|
end
|
|
|
|
defimpl Enum.Iterator, for: Function do
|
|
def iterator(function) do
|
|
function.()
|
|
end
|
|
|
|
def count(function) do
|
|
{ function, first } = function.()
|
|
do_count(first, function, 0)
|
|
end
|
|
|
|
defp do_count({ _, next }, function, acc) do
|
|
do_count(function.(next), function, acc + 1)
|
|
end
|
|
|
|
defp do_count(:stop, _, acc) do
|
|
acc
|
|
end
|
|
end
|