Skip to content

pguillory/superlist

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SuperList

The Enum and List modules are powerful tools for working with lists in Elixir, but they generally are designed for manipulating a single list at a time. Sometimes you need to iterate over multiple lists simultaneously. For example, to merge two lists, taking the maximum value between the two at each offset:

Enum.zip(list1, list2)
|> Enum.map(fn {element1, element2} ->
  max(element1, element2)
end)

While this works, it is verbose and inefficient. The Enum.zip call creates tuples that we immediately match and throw away. If you've attempted to optimize similar code, you may have written something like:

def max_of_lists([element1 | list1], [element2 | list2]) do
  [max(element1, element2) | max_of_lists(list1, list2)]
end

def max_of_lists([], []) do
  []
end

Don't clutter your codebase with ad-hoc iteration code like this! Write it more cleanly with SuperList:

SuperList.map(list1, list2, &max/2)

So it's like Enum.zip_with, or what?

Yeah, these are equivalent both in semantics and performance:

Enum.zip_with(list1, list2, func)
SuperList.map(list1, list2, func)

However things change with 3+ lists. Enum supports variable numbers of arguments by accepting a list with a variable number of elements, whereas Superlist defines multiple implementations with variable arities.

Enum.zip_with([list1, list2, list3], fn [element1, element2, element3] ->
  func.(element1, element2, element3)
end)
SuperList.map(list1, list2, list3, func)

In the above example with 3 lists, SuperList is not only cleaner but about 3.8x faster.

Supported functions

Enum function SuperList equivalent
Enum.each SuperList.each
Enum.map SuperList.map
Enum.map_reduce SuperList.map_reduce
Enum.reduce SuperList.reduce
Enum.unzip SuperList.unzip
Enum.zip SuperList.zip
SuperList.map(list1, func/1)
SuperList.map(list1, list2, func/2)
SuperList.map(list1, list2, list3, func/3)
# ...etc.

Code generation

These optimizations are achieved using code generation. Implementations for each function are defined accepting from 1 up to 25 lists. The upper limit can be increased using a compile time setting.

Installation

If available in Hex, the package can be installed by adding superlist to your list of dependencies in mix.exs:

def deps do
  [
    {:superlist, "~> 0.1.0"}
  ]
end

About

Optimized operations on multiple lists

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages