ext.ex (2009B)
1 defmodule EarmarkParser.Enum.Ext do 2 @moduledoc ~S""" 3 Some extensions of Enum functions 4 """ 5 6 @doc ~S""" 7 `reduce_with_end` is like `Enum.reduce` for lists, but the reducer function is called for 8 each element of the list with the tuple `{:element, element}` and the accumulator and once 9 more at the end with `:end` and the accumulator 10 11 iex(1)> reducer = 12 ...(1)> fn {:element, nil}, {partial, result} -> {[], [Enum.sum(partial)|result]} 13 ...(1)> {:element, val}, {partial, result} -> {[val|partial], result} 14 ...(1)> :end, {partial, result} -> [Enum.sum(partial)|result] |> Enum.reverse 15 ...(1)> end 16 ...(1)> [1, 2, nil, 4, 1, 0, nil, 3, 2, 2] 17 ...(1)> |> reduce_with_end({[], []}, reducer) 18 [3, 5, 7] 19 20 **N.B.** that in the treatment of `:end` we can change the shape of the accumulator w/o any 21 penalty concerning the complexity of the reducer function 22 """ 23 def reduce_with_end(collection, initial_acc, reducer_fn) 24 def reduce_with_end([], acc, reducer_fn) do 25 reducer_fn.(:end, acc) 26 end 27 def reduce_with_end([ele|rest], acc, reducer_fn) do 28 reduce_with_end(rest, reducer_fn.({:element, ele}, acc), reducer_fn) 29 end 30 31 @doc ~S""" 32 33 Like map_reduce but reversing the list 34 35 iex(2)> replace_nil_and_count = fn ele, acc -> 36 ...(2)> if ele, do: {ele, acc}, else: {"", acc + 1} 37 ...(2)> end 38 ...(2)> ["y", nil, "u", nil, nil, "a", nil] |> reverse_map_reduce(0, replace_nil_and_count) 39 { ["", "a", "", "", "u", "", "y"], 4 } 40 41 """ 42 def reverse_map_reduce(list, initial, fun) do 43 _reverse_map_reduce(list, initial, [], fun) 44 end 45 46 # Helpers {{{ 47 defp _reverse_map_reduce(list, acc, result, fun) 48 defp _reverse_map_reduce([], acc, result, _fun), do: {result, acc} 49 defp _reverse_map_reduce([fst|rst], acc, result, fun) do 50 {new_ele, new_acc} = fun.(fst, acc) 51 _reverse_map_reduce(rst, new_acc, [new_ele|result], fun) 52 end 53 # }}} 54 end 55 # SPDX-License-Identifier: Apache-2.0