Files
n8n-openai-adapter/lib/enum.ex
T

542 lines
14 KiB
Elixir

defprotocol Enum::Iterator, [iterator(collection)], only: [List, Record], as: I
# Evalutes the items in the given collection according to the
# Enum::Iterator protocol. Most functions in this module
# will automatically retrieve the protocol given the collection
# and iterator, for example:
#
# Enum.map [1,2,3], fun(x, do: x * 2)
#
# However, one can use their own iteration function for any
# collection by passing the iterator function as the first
# argument:
#
# Enum.map my_iteration_function, [1,2,3], fun(x, do: x * 2)
#
# ## The protocol
#
# When `Enum.map` is invoked without the iterator function,
# it invokes `Enum::Iterator.iterator(collection)` with the
# given collection in order to retrieve the default iterator
# for that collection. You can implement the protocol for any
# data type you wish. Elixir ships with a default iterator
# for lists, implemented as follow:
#
# defimpl Enum::Iterator, for: List do
# def iterator(_), do: iterate(&1)
#
# defp iterate([h|t]) do
# { h, t }
# end
#
# defp iterate([]) do
# __STOP_ITERATOR__
# end
# end
#
# The __STOP_ITERATOR__ is a special Elixir token that
# marks when iteration should finish.
defmodule Enum do
# Invokes the given `fun` for each item in the `collection`
# checking if all results evalutes to true. If any does not,
# abort and return false. Otherwise, true.
#
# ## Examples
#
# Enum.all? [2,4,6], fn(x, do: rem(x, 2) == 0)
# #=> true
#
# Enum.all? [2,3,4], fn(x, do: rem(x, 2) == 0)
# #=> false
#
# If no function is given, it defaults to checking if
# all items in the collection evalutes to true.
#
# Enum.all? [1,2,3] #=> true
# Enum.all? [1,nil,3] #=> false
#
def all?(collection, fun // fn(x, do: x)) do
all?(I.iterator(collection), collection, fun)
end
def all?(iterator, collection, fun) do
do_all?(iterator.(collection), iterator, fun)
end
# Invokes the given `fun` for each item in the `collection`
# checking if any of the results returns true. If one does,
# aborts and returns true. If not, returns false.
#
# ## Examples
#
# Enum.any? [2,4,6], fn(x, do: rem(x, 2) == 1)
# #=> false
#
# Enum.any? [2,3,4], fn(x, do: rem(x, 2) == 1)
# #=> true
#
# If no function is given, it defaults to checking if
# any item in the collection evalutes to true.
#
# Enum.any? [false,false,false] #=> false
# Enum.any? [false,true,false] #=> true
#
def any?(collection, fun // fn(x, do: x)) do
any?(I.iterator(collection), collection, fun)
end
def any?(iterator, collection, fun) do
do_any?(iterator.(collection), iterator, fun)
end
# Receives a list of enums and a function and recursively
# invokes the function with an accumulutaor plus each item
# in each enum as argument in reverse order. Therefore, the
# arity of the fun must be 1 + number of enums.
#
# This is used internally by `for` and should not be used directly.
def __for__(lists, fun) do
iterators = lc list in lists, do: { list, I.iterator(list) }
[{h,iterator}|t] = iterators
first_comprehension_each iterator, iterator.(h), t, [], fun
end
# Invokes the `fun` for each item in collection
# and returns the first the function returns a truthy
# value. If no item is found, returns `ifnone`.
#
# ## Examples
#
# Enum.detect [2,4,6], fn(x, do: rem(x, 2) == 1)
# # => nil
#
# Enum.detect [2,4,6], 0, fn(x, do: rem(x, 2) == 1)
# # => 0
#
# Enum.detect [2,3,4], fn(x, do: rem(x, 2) == 1)
# # => 3
#
def detect(collection, ifnone // nil, fun) do
detect(I.iterator(collection), collection, ifnone, fun)
end
def detect(iterator, collection, ifnone, fun) do
do_detect(iterator.(collection), iterator, ifnone, fun)
end
# Similar to detect, but returns the value of the function
# invocation instead of the element iterated.
#
# ## Examples
#
# Enum.detect_value [2,4,6], fn(x, do: rem(x, 2) == 1)
# # => nil
#
# Enum.detect_value [2,4,6], 0, fn(x, do: rem(x, 2) == 1)
# # => 0
#
# Enum.detect_value [2,3,4], fn(x, do: rem(x, 2) == 1)
# # => true
#
def detect_value(collection, ifnone // nil, fun) do
detect_value(I.iterator(collection), collection, ifnone, fun)
end
def detect_value(iterator, collection, ifnone, fun) do
do_detect_value(iterator.(collection), iterator, ifnone, fun)
end
# Invokes the given `fun` for each item in the `collection`.
# Returns the `collection` itself.
#
# ## Examples
#
# Enum.each ['some', 'example'], fn(x, do: IO.puts x)
#
def each(collection, fun) do
each(I.iterator(collection), collection, fun)
end
def each(iterator, collection, fun) do
do_each(iterator.(collection), iterator, fun)
collection
end
# Returns if the collection is empty or not.
#
# ## Examples
#
# Enum.empty? [] #=> true
# Enum.empty? [1,2,3] #=> false
#
def empty?(collection) when is_list(collection) do
collection == []
end
def empty?(collection) do
empty?(I.iterator(collection), collection)
end
def empty?(iterator, collection) do
iterator.(collection) == __STOP_ITERATOR__
end
# Invokes the given `fun` for each item in the `collection`.
# Returns only the items the function evalutes to true.
#
# ## Examples
#
# Enum.filter [1, 2, 3], fn(x, do: rem(x, 2) == 0)
# #=> [2]
#
def filter(collection, fun) do
filter(I.iterator(collection), collection, fun)
end
def filter(iterator, collection, fun) do
do_filter(iterator.(collection), iterator, fun)
end
# Iterates the collection from left to right passing an
# accumulator as parameter. Returns the accumulator.
#
# ## Examples
#
# Enum.foldl [1, 2, 3], 0, fn(x, acc, do: x + acc)
# #=> 6
#
def foldl(collection, acc, f) when is_list(collection) do
:lists.foldl(f, acc, collection)
end
def foldl(collection, acc, f) do
foldl(I.iterator(collection), collection, acc, f)
end
def foldl(iterator, collection, acc, f) do
do_foldl(iterator.(collection), iterator, acc, f)
end
# Join the given `collection` according to `joiner`.
# Joiner can be either a binary or a list and the
# result will be of the same type of joiner.
#
# All items in the collection must be convertable
# to binary, otherwise an error is raised.
#
# ## Examples
#
# Enum.join([1,2,3], " = ") #=> "1 = 2 = 3"
# Enum.join([1,2,3], ' = ') #=> '1 = 2 = 3'
#
def join(collection, joiner) do
join(I.iterator(collection), collection, joiner)
end
def join(iterator, collection, joiner) when is_list(joiner) do
binary_to_list join(iterator, collection, list_to_binary(joiner))
end
def join(iterator, collection, joiner) do
do_join(iterator.(collection), iterator, joiner, nil)
end
# TODO: Write docs + tests
def keyfind(collection, key, position) when is_list(collection) do
Erlang.lists.keyfind(key, position, collection)
end
# Invokes the given `fun` for each item in the `collection`.
# Returns the result of all function calls.
#
# ## Examples
#
# Enum.map [1, 2, 3], fn(x, do: x * 2)
# #=> [2, 4, 6]
#
def map(collection, fun) when is_list(collection) do
:lists.map(fun, collection)
end
def map(collection, fun) do
map(I.iterator(collection), collection, fun)
end
def map(iterator, collection, fun) do
do_map(iterator.(collection), iterator, fun)
end
# Invokes the given `fun` for each item in the `collection`
# while also keeping an accumulator. Returns a tuple where
# the first element is the iterated collection and the second
# one is the final accumulator.
#
# ## Examples
#
# Enum.mapfoldl [1, 2, 3], 0, fn(x, acc, do: { x * 2, x + acc })
# #=> { [2, 4, 6], 6 }
#
def mapfoldl(collection, acc, f) when is_list(collection) do
:lists.mapfoldl(f, acc, collection)
end
def mapfoldl(collection, acc, fun) do
mapfoldl(I.iterator(collection), collection, acc, fun)
end
def mapfoldl(iterator, collection, acc, fun) do
do_mapfoldl(iterator.(collection), iterator, acc, fun)
end
# Iterates the given function n times, passing values from 1
# to n.
#
# ## Examples
#
# Enum.times 3, fn(x, do: IO.puts x)
# 1
# 2
# 3
#
def times(times, function) when times >= 0 do
case is_function(function, 0) do
match: true
do_times_0(times, 1, function)
else:
do_times_1(times, 1, function)
end
times
end
# Iterates the given function n times, passing values from 1
# to n. Also has an accumulator similar to fold to store the
# value between computations.
#
# ## Examples
#
# Enum.times 5, 0, fn(x, acc, do: acc + x)
# #=> 15
#
def times(times, acc, function) when times >= 0 do
do_times_2(times, 1, function, acc)
end
## Implementations
## all?
defp do_all?({ h, next }, iterator, fun) do
case fun.(h) do
match: false
false
match: nil
false
else:
do_all?(iterator.(next), iterator, fun)
end
end
defp do_all?(__STOP_ITERATOR__, _, _) do
true
end
## any?
defp do_any?({ h, next }, iterator, fun) do
case fun.(h) do
match: false
do_any?(iterator.(next), iterator, fun)
match: nil
do_any?(iterator.(next), iterator, fun)
else:
true
end
end
defp do_any?(__STOP_ITERATOR__, _, _) do
false
end
## detect
defp do_detect({ h, next }, iterator, ifnone, fun) do
case fun.(h) do
match: false
do_detect(iterator.(next), iterator, ifnone, fun)
match: nil
do_detect(iterator.(next), iterator, ifnone, fun)
else:
h
end
end
defp do_detect(__STOP_ITERATOR__, _, ifnone, _) do
ifnone
end
## detect_value
defp do_detect_value({ h, next }, iterator, ifnone, fun) do
case fun.(h) do
match: false
do_detect_value(iterator.(next), iterator, ifnone, fun)
match: nil
do_detect_value(iterator.(next), iterator, ifnone, fun)
match: other
other
end
end
defp do_detect_value(__STOP_ITERATOR__, _, 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_ITERATOR__, _, _) do
[]
end
## filter
defp do_filter({ h, next }, iterator, fun) do
case fun.(h) do
match: false
do_filter(iterator.(next), iterator, fun)
match: nil
do_filter(iterator.(next), iterator, fun)
else:
[h|do_filter(iterator.(next), iterator, fun)]
end
end
defp do_filter(__STOP_ITERATOR__, _, _) do
[]
end
## foldl
defp do_foldl({ h, next }, iterator, acc, fun) do
do_foldl(iterator.(next), iterator, fun.(h, acc), fun)
end
defp do_foldl(__STOP_ITERATOR__, _, acc, _) do
acc
end
## join
# The first item is simply stringified unless ...
defp do_join({ h, next }, iterator, joiner, nil) do
do_join(iterator.(next), iterator, joiner, to_binary(h))
end
# The first item is __STOP_ITERATOR__, then we return an empty string;
defp do_join(__STOP_ITERATOR__, _, _joiner, nil) do
""
end
# All other items are concatenated to acc, by first adding the joiner;
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
# Until we have to stop iteration, then we return acc.
defp do_join(__STOP_ITERATOR__, _, _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_ITERATOR__, _, _) do
[]
end
## mapfoldl
defp do_mapfoldl({ h, next }, iterator, acc, f) do
{ result, acc } = f.(h, acc)
{ rest, acc } = do_mapfoldl(iterator.(next), iterator, acc, f)
{ [result|rest], acc }
end
defp do_mapfoldl(__STOP_ITERATOR__, _, acc, _f) do
{ [], acc }
end
## times
defp do_times_0(limit, counter, _function) when counter > limit do
end
defp do_times_0(limit, counter, function) do
function.()
do_times_0(limit, 1 + counter, function)
end
defp do_times_1(limit, counter, _function) when counter > limit do
end
defp do_times_1(limit, counter, function) do
function.(counter)
do_times_1(limit, 1 + counter, function)
end
defp do_times_2(limit, counter, _function, acc) when counter > limit do
acc
end
defp do_times_2(limit, counter, function, acc) do
new_acc = function.(counter, acc)
do_times_2(limit, 1 + counter, function, new_acc)
end
## comprehensions
defp first_comprehension_each(iterator, { h, next }, t, acc, fun) do
first_comprehension_each iterator, iterator.(next), t, next_comprehension(t, acc, fun, [h]), fun
end
defp first_comprehension_each(_iterator, __STOP_ITERATOR__, _t, acc, _fun) do
List.reverse(acc)
end
defp next_comprehension([{h,iterator}|t], acc, fun, args) do
next_comprehension_each iterator, iterator.(h), t, acc, fun, args
end
defp next_comprehension([], acc, fun, args) do
apply fun, [acc|args]
end
defp next_comprehension_each(iterator, { h, next }, t, acc, fun, args) do
next_comprehension_each iterator, iterator.(next), t, next_comprehension(t, acc, fun, [h|args]), fun, args
end
defp next_comprehension_each(_iterator, __STOP_ITERATOR__, _t, acc, _fun, _args) do
acc
end
end
defimpl Enum::Iterator, for: List do
def iterator(_), do: iterate(&1)
defp iterate([h|t]) do
{ h, t }
end
defp iterate([]) do
__STOP_ITERATOR__
end
end