Lambda
Lambda is a library for performing parallel, lazy map
, reduce
, and filter
operations on slices in one pipeline. The slice can be set by a generating function, and parallel execution is supported. It is expected that all function arguments will be pure functions (functions with no side effects that can be cached by their arguments). It is capable of handling large amounts of data with minimal overhead, and its parallel execution capabilities allow for even faster processing times. Additionally, the library is easy to use and has a clean, intuitive API. Here is some performance review.
Getting Started
To install Lambda, run the following command:
go get github.com/koss-null/lambda
Then, import the library into your Go code (basically you need the pipe package):
import "github.com/koss-null/lambda/pkg/pipe"
You can then use the pipe
package to create a pipeline of operations on a slice:
res := pipe.Slice(a).
Map(func(x int) int { return x * x }).
Map(func(x int) int { return x + 1 }).
Filter(func(x int) bool { return x > 100 }).
Filter(func(x int) bool { return x < 1000 }).
Parallel(12).
Do()
To see more examples of how to use Lambda, check out the examples/main.go
file. You can also run this file with go run examples/main.go
.
Basic information
The Pipe
type is an interface that represents a lazy, potentially infinite sequence of data. The Pipe
interface provides a set of methods that can be used to transform and filter the data in the sequence.
The following functions can be used to create a new Pipe
:
πΈ Slice([]T) *Pipe
: creates aPipe
of a given typeT
from a slice.πΈ Func(func(i int) (T, bool)) *Pipe
: creates aPipe
of typeT
from a function. The function should return the value of the element at thei
th position in thePipe
, as well as a boolean indicating whether the element should be included (true
) or skipped (false
).πΈ Take(n int) *Pipe
: if it's aFunc
-madePipe
, expectsn
values to be eventually returned.πΈ Gen(n int) *Pipe
: if it's aFunc
-madePipe
, generates a sequence from[0, n)
and applies the function to it.π± TBD:Cycle(data []T) *Pipe
: creates a newPipe
that cycles through the elements of the provided slice indefinitely.π± TBD:Range(start, end, step T) *Pipe
: creates a newPipe
that generates a sequence of values of typeT
fromstart
toend
(exclusive) with a fixedstep
value between each element.T
can be any numeric type, such asint
,float32
, orfloat64
.
The following functions can be used to transform and filter the data in the Pipe
:
πΈ Map(fn func(x T) T) *Pipe
: applies the functionfn
to every element of thePipe
and returns a newPipe
with the transformed data.πΈ Filter(fn func(x T) bool) *Pipe
: applies the predicate functionfn
to every element of thePipe
and returns a newPipe
with only the elements that satisfy the predicate.πΈ Reduce(fn func(x, y T) T) T
: applies the binary functionfn
to the elements of thePipe
and returns a single value that is the result of the reduction.πΈ Sum(sum func(x, y) T) T
: makes parallel reduce with associative functionsum
.πΈ Sort(less func(x, y T) bool) *Pipe
: sorts the elements of thePipe
using the providedless
function as the comparison function.
The following functions can be used to retrieve a single element or perform a boolean check on the Pipe
without executing the entire pipeline:
πΈ Any(fn func(x T) bool) bool
: returnstrue
if any element of thePipe
satisfies the predicatefn
, andfalse
otherwise.πΈ First() T
: returns the first element of thePipe
, ornil
if thePipe
is empty.πΈ Count() int
: returns the number of elements in thePipe
. It does not execute the entire pipeline, but instead simply returns the number of elements in thePipe
.π± TBD:IsAny() bool
: returnstrue
if thePipe
contains any elements, andfalse
otherwise.π± TBD:MoreThan(n int) bool
: returnstrue
if thePipe
contains more thann
elements, andfalse
otherwise.
The Parallel(n int) *Pipe
function can be used to specify the level of parallelism in the pipeline, by setting the number of goroutines to be executed on (4 by default).
Finally, the Do() []T
function is used to execute the pipeline and return the resulting slice of data. This function should be called at the end of the pipeline to retrieve the final result.
In addition to the functions described above, the pipe
package also provides several utility functions that can be used to create common types of Pipe
s, such as Range
, Repeat
, and Cycle
. These functions can be useful for creating Pipe
s of data that follow a certain pattern or sequence.```
Examples
Basic example:
res := pipe.Slice(a).
Map(func(x int) int { return x * x }).
Map(func(x int) int { return x + 1 }).
Filter(func(x int) bool { return x > 100 }).
Filter(func(x int) bool { return x < 1000 }).
Parallel(12).
Do()
Func
and Take
:
Example using p := pipe.Func(func(i int) (int, bool) {
if i < 10 {
return i * i, true
}
return 0, false
}).Take(5).Do()
// p will be [0, 1, 4, 9, 16]
Filter
and Map
:
Example using p := pipe.Slice([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}).
Filter(func(x int) bool { return x % 2 == 0 }).
Map(func(x int) string { return strconv.Itoa(x) }).
Do()
// p will be ["2", "4", "6", "8", "10"]
Map
and Reduce
:
Example using p := pipe.Slice([]int{1, 2, 3, 4, 5}).
Map(func(x int) int { return x * x }).
Reduce(func(x, y int) string {
return strconv.Itoa(x) + "-" + strconv.Itoa(y)
})
// p will be "1-4-9-16-25"
Map
and Reduce
with the underlying array type change:
Example of p := pipe.Slice([]int{1, 2, 3, 4, 5, 6, 7, 8, 9})
strP := pipe.Map(p, func(x int) string { return strconv.Itoa(x) })
result := pipe.Reduce(strP, func(x, y string) int { return len(x) + len(y) }).Do()
// result will be 45
Sort
:
Example using p := pipe.Func(func(i int) (float32, bool) {
return float32(i) * 0.9, true
}).
Map(func(x float32) float32 { return x * x }).
Gen(100500).
Sort(pipe.Less[float32]).
Parallel(12).
Do()
// p will contain the elements of p sorted in ascending order
Example of infine sequence generation:
Here is an example of generating an infinite sequence of random float32
values greater than 0.5
:
p := pipe.Func(func(i int) (float32, bool) {
rnd := rand.New(rand.NewSource(42))
rnd.Seed(int64(i))
return rnd.Float32(), true
}).
Filter(func(x float32) bool { return x > 0.5 })
To generate a specific number of values, you can use the Take
method:
p = p.Take(65000)
To accumulate the elements of the Pipe
, you can use the Reduce
method:
sum := p.Sum(pipe.Sum[float32])
//also you can: sum := p.Reduce(func(x, y float32) float32 { return x + y})
// sum will be the sum of the first 65000 random float32 values greater than 0.5
Range
(not implemented yet) and Map
:
Example using p := pipe.Range(10, 20, 2).Map(func(x int) int { return x * x }).Do()
// p will be [100, 144, 196, 256, 324]
Repeat
(not implemented yet) and Map
:
Example using p := pipe.Repeat("hello", 5).Map(func(s string) int { return len(s) }).Do()
// p will be [5, 5, 5, 5, 5]
Cycle
(not implemented yet) and Filter
:
Example using p := pipe.Cycle([]int{1, 2, 3}).Filter(func(x int) bool { return x % 2 == 0 }).Take(4).Do()
// p will be [2, 2, 2, 2]
Is this package stable?
In short: not yet. However, for each release I manually test everything that has been modified since the previous release, and I have a growing set of unit tests. While it may not be suitable for use in production environments, it should be stable enough for use in pet projects. I will provide more convincing quality guarantees and benchmarks with the v1.0.0 release.
Contributions
I am currently accepting any well-written tests. You are welcome to use any frameworks you prefer. Bug fixes are also welcome. I plan to do some refactoring in the future, so please communicate with the owner before implementing any new features to ensure that they will be accepted.
What's next?
I hope to provide some roadmap of the project soon. Also I am going to craft some unit-tests and may be set up github pipelines eventually. Feel free to fork, inspire and use! I will try to supply all version tags by some manual testing and quality control at least.
Supported functions list
πΈ Slice([]T) *Pipe
: creates aPipe
of a given typeT
from a slice.πΈ Func(func(i int) (T, bool)) *Pipe
: creates aPipe
of typeT
from a function. The function should return the value of the element at thei
th position in thePipe
, as well as a boolean indicating whether the element should be included (true
) or skipped (false
). This function can be used to generate elements on demand, rather than creating a slice beforehand.πΈ Take(n int) *Pipe
: if it's aFunc
-madePipe
, expectsn
values to be eventually returned. This function can be used to limit the number of elements generated by the function.πΈ Gen(n int) *Pipe
: if it's aFunc
-madePipe
, generates a sequence from[0, n)
and applies the function to it. This function can be used to generate a predetermined number of elements using the function.πΈ Parallel(n int) *Pipe
: sets the number of goroutines to be executed on (4 by default). This function can be used to specify the level of parallelism in the pipeline.πΈ Map(fn func(x T) T) *Pipe
: applies the functionfn
to every element of thePipe
and returns a newPipe
with the transformed data. This function can be used to apply a transformation to each element in thePipe
.πΈ Filter(fn func(x T) bool) *Pipe
: applies the predicate functionfn
to every element of thePipe
and returns a newPipe
with only the elements that satisfy the predicate. This function can be used to select a subset of elements from thePipe
.πΈ Reduce(fn func(x, y T) T) T
: applies the binary functionfn
to the elements of thePipe
and returns a single value that is the result of the reduction. This function can be used to combine the elements of thePipe
into a single value.πΈ Sum(sum func(x, y) T) T
: makes parallel reduce with associative functionsum
.πΈ Do() []T
: executes thePipe
and returns the resulting slice of data.πΈ First() T
: returns the first element of thePipe
, ornil
if thePipe
is empty. This function can be used to retrieve the first element of thePipe
without executing the entire pipeline.πΈ Any(fn func(x T) bool) bool
: returnstrue
if any element of thePipe
satisfies the predicatefn
, andfalse
otherwise. This function can be used to check if any element in thePipe
satisfies a given condition.πΈ Count() int
: returns the number of elements in thePipe
. It does not execute the entire pipeline, but instead simply returns the number of elements in thePipe
.πΈ Sort(less func(x, y T) bool) *Pipe
: sorts the elements of thePipe
using the providedless
function as the comparison functionπ± TBD:IsAny() bool
: returnstrue
if thePipe
contains any elements, andfalse
otherwise. This function can be used to check if thePipe
is empty.π± TBD:MoreThan(n int) bool
: returnstrue
if thePipe
contains more thann
elements, andfalse
otherwise.π± TBD:Reverse() *Pipe
: reverses the underlying slice.