UNPKG

fl

Version:

Functional lazy sequence operators

841 lines (548 loc) 17.2 kB
# fl.js Functional lazy operators and sequences. * Uses standard [Iterator][] protocol * Accepts ES6 generators as lazy sequences * Designed to collaborate with modules such as **[pim.js][]**, which provides persistent immutable data structures ## API ### General value operators #### identity ```js identity( value ) ``` Returns `value`. #### increment ```js increment( value ) ``` Returns `value + 1`. #### decrement ```js decrement( value ) ``` Returns `value - 1`. #### isEven ```js isEven( value ) ``` Returns `value % 2 === 0`. #### isOdd ```js isOdd( value ) ``` Returns `value % 2 === 1 || value % 2 === -1`. #### sum ```js sum( ...addends ) ``` Returns the sum of `0` and zero or more `addends`. #### multiply ```js multiply( ...factors ) ``` Returns the product of `1` and zero or more `factors`. #### toArray ```js Sequence().toArray() // >>> array toArray( sequence ) // >>> array ``` Iterates through a sequence and returns its elements in an array. #### complement ```js complement( predicate ) ``` Returns a function that returns the logical negation of the result of applying `predicate`. ```js complement( isEven )(3); true ``` #### compose ```js compose( ...fns ) ``` Returns the composition of a series of functions `fns`, such that `compose(f,g,h)(x)` is equivalent to `f(g(h(x)))`. #### partial ```js partial( fn, ...args ) ``` Given a function `fn` and successive arguments `args` fewer in number than the expected number of parameters for `fn`, returns a function that will return the result of applying `fn` to the concatentation of `args` and its own arguments. ```js addFive = partial( sum, 5 ); addFive(2); 7 rest = partial( dropWhile, function ( value, index ) { return index < 1; }); // >>> function toArray( rest( range(4) ) ); [ 1, 2, 3 ] toArray( rest( function* () { yield 2; yield 4; yield 6; yield 8; })); [ 4, 6, 8 ] ``` #### apply ```js apply( fn, sequence ) ``` Returns the result of applying `fn` with each element in `sequence` as arguments. #### compare The default comparator function used with `sort`. Determines the ordinal relation between arguments `x` and `y`. ```js compare( x, y ) // >>> number ``` Returns `0` if `x` and `y` are of equal precedence, returns `-1` if `x` precedes `y`, or returns `1` if `y` precedes `x`. If `x` and `y` are logical sequences, then the comparison is evaluated recursively over the respective elements of both sequences. #### comparator Creates a boolean valued comparison operator. ```js comparator( predicate ) // >>> function ``` Returns a predicate that returns `true` if applying `predicate` to each adjacent pairing of its arguments also returns `true`, and returns `false` otherwise. ```js nonconsecutive = comparator( compare ); apply( nonconsecutive, 'fireman' ); // >>> true apply( nonconsecutive, 'balance' ); // >>> true apply( nonconsecutive, 'ladders' ); // >>> false ``` #### increasing ```js increasing( ...values ) // >>> boolean ``` Returns `true` if arguments are provided in a strictly increasing order (`<`) as determined by `compare`; otherwise returns `false`. Logically equivalent to `comparator( (x,y) => compare(x,y) < 0 )`. ```js apply( increasing, [0,1,4,9] ); // >>> true apply( increasing, [1,1,2,3] ); // >>> false apply( increasing, 'gist' ); // >>> true apply( increasing, 'bees' ); // >>> false apply( increasing, Sequence.range().take(5) ); // >>> true ``` #### decreasing ```js decreasing( ...values ) // >>> boolean ``` Returns `true` if arguments are provided in a strictly decreasing order (`>`) as determined by `compare`; otherwise returns `false`. ```js apply( decreasing, [9,4,1,0] ); // >>> true apply( decreasing, [3,2,1,1] ); // >>> false apply( decreasing, 'wronged' ); // >>> true apply( decreasing, 'sniffed' ); // >>> false ``` #### nonincreasing ```js nonincreasing( ...values ) // >>> boolean ``` Returns `true` if arguments are provided in a monotonically decreasing order (`>=`) as determined by `compare`; otherwise returns `false`. ```js apply( nonincreasing, 'sniffed' ); // >>> true ``` #### nondecreasing ```js nondecreasing( ...values ) // >>> boolean ``` Returns `true` if arguments are provided in a monotonically increasing order (`<=`) as determined by `compare`; otherwise returns `false`. ```js apply( nondecreasing, 'bees' ); // >>> true ``` ### Sequential functions #### first ```js Sequence().first() // >>> * first( sequence ) // >>> * ``` Returns the first element in `sequence`. ```js first( range(4) ); 0 first([]); undefined ``` Logically equivalent to `take( 1, sequence )().next().value`. #### rest ```js Sequence().rest() // >>> Sequence rest( sequence ) // >>> function ``` Returns a logical sequence of the elements that follow the `first` element of `sequence`. ```js toArray( rest([ 0, 1, 2, 3 ]) ); [ 1, 2, 3 ] toArray( rest([ 1 ]) ); [] toArray( rest( [] ) ); [] Sequence.range( 3, Infinity ).rest().take(4).toArray(); [ 4, 5, 6, 7 ] ``` Logically equivalent to `drop( 1, sequence )`. #### range Defines a sequence of numbers from `start` up to but excluding `end` in increments of `step`. ```js Sequence.range() Sequence.range( end ) Sequence.range( start, end ) Sequence.range( start, end, step ) // >>> Sequence range() range( end ) range( start, end ) range( start, end, step ) // >>> function ``` Defaults for arguments not provided are: `start = 0`, `end = Infinity`, `step = 1`. Returns a `Sequence` or sequence generator function. ```js toArray( range(4) ); [ 0, 1, 2, 3 ] toArray( take( 5, range() ) ); [ 0, 1, 2, 3, 4 ] toArray( range(-5) ); [] toArray( range(3,9,2) ); [ 3, 5, 7 ] ``` #### iterate Given a nominally pure function `f` and *seed* value `x`, defines an infinite sequence of `x`, `f(x)`, `f(f(x))`, etc. ```js Sequence.iterate( fn, seed ) // >>> Sequence iterate( fn, seed ) // >>> function ``` Returns a `Sequence` or sequence generator function. ```js toArray( take( 5, iterate( increment, 42 ) ) ); [ 42, 43, 44, 45, 46 ] ``` #### repeat Given a value `x`, defines a repeating infinite sequence of `x`, or a finite sequence of `x` repeated up to `limit` times. ```js Sequence.repeat( value ) Sequence.repeat( value, limit ) // >>> Sequence repeat( value ) repeat( value, limit ) // >>> function ``` Returns a `Sequence` or sequence generator function. ```js toArray( take( 5, repeat(42) ) ); [ 42, 42, 42, 42, 42 ] Sequence.repeat( 'foo', 5 ); [ 'foo', 'foo', 'foo', 'foo', 'foo' ] ``` #### cycle Defines an infinite sequence that repeats the items in `sequence` indefinitely. ```js Sequence.cycle() // >>> Sequence cycle( sequence ) // >>> function ``` Returns a `Sequence` or sequence generator function. ```js toArray( take( 10, cycle([3,4,5,6]) ) ); [ 3, 4, 5, 6, 3, 4, 5, 6, 3, 4 ] Sequence([3,4,5,6]).take(10).toArray(); [ 3, 4, 5, 6, 3, 4, 5, 6, 3, 4 ] ``` #### filter Applies a nominally pure `predicate` to each item in `sequence` and keeps those items for which `predicate` returns logical true. ```js Sequence().filter( predicate ) // >>> Sequence filter( predicate, sequence ) // >>> function ``` Returns a `Sequence`, or sequence generator function, consisting of each element `x` in `this` or `sequence` for which the expression `!!predicate(x)` would equal `true`. ```js Sequence(["all", "your", "base", "are", "belong"]).filter( function (word) { return word.length < 4; }); [ 'all', 'are' ] ``` #### remove Performs a `filter` on the `complement` of the provided `predicate`. #### map Applies `fn` to successive items of one or more sequences. ```js Sequence().map( fn, ...sequences ) // >>> Sequence map( fn, sequence, ...sequences ) // >>> function ``` The arity of `fn` should correspond with the number of sequences to be mapped. Returns a `Sequence`, or sequence generator function, consisting of the result of applying `fn` to successive items taken from `sequence` and any additional `sequences`, in parallel, until any of `sequence` or `sequences` is exhausted. ```js toArray( map( increment, [1,2,3] ) ); [ 2, 3, 4 ] Sequence([1,2,3]).map( increment ).toArray(); [ 2, 3, 4 ] toArray( map( sum, [1,2,3], [10,20,30] ) ); [ 11, 22, 33 ] Sequence([1,2,3]).map( sum, [10,20,30], [100,200,300] ).toArray(); [ 111, 222, 333 ] Sequence([1,2,3]).map( sum, [10,20] ).toArray(); [ 11, 22 ] Sequence( iterate increment, 1 ).map( sum, range(10,50,10) ).toArray(); [ 11, 22, 33, 44 ] ``` #### reduce Iteratively calls `fn`, with two arguments: 1. `seed` on the first iteration; thereafter, the result of the previous iteration 2. each successive value in `sequence`. If no `seed` is provided, the first value of the sequence is used as the `seed` and reduction proceeds on the remainder of the sequence. ```js Sequence().reduce( fn, seed ) Sequence().reduce( fn ) // >>> * reduce( fn, seed, sequence ) reduce( fn, sequence ) // >>> * ``` Returns the result of the final iteration of `fn`. If the length of `sequence` is zero, returns the result of calling `fn` with no arguments. If the length of `sequence` is `1`, the single element of `sequence` is returned. ```js function factorial (n) { return Sequence.range(n).map( increment ).reduce( multiply ); } ``` #### reductions Produces a sequence of the intermediate values in a `reduce` operation. ```js Sequence().reductions( fn, seed ) Sequence().reductions( fn ) // >>> Sequence reductions( fn, seed, sequence ) reductions( fn, sequence ) // >>> function ``` Returns a `Sequence`, or sequence generator function, of reductions. ```js Sequence.range(1,10).reductions( multiply ).toArray(); [ 1, 2, 6, 24, 120, 720, 5040, 40320, 362880 ] function factorialSequence (n) { return Sequence.range(n).map( increment ).reductions( multiply ); } factorialSequence(10).toArray(); [ 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800 ] ``` #### take Defines a sequence of up to `amount` items taken successively from `sequence`, or ```js Sequence().take( amount ) // >>> Sequence take( amount, sequence ) // >>> function ``` Returns a `Sequence` or sequence generator function. #### takeWhile Defines a sequence of items taken successively from `sequence` so long as the expression `!!predicate( item )` remains equal to `true`. ```js Sequence().takeWhile( predicate ) // >>> Sequence takeWhile( predicate, sequence ) // >>> function ``` Returns a `Sequence` or sequence generator function. ```js Sequence .range() .takeWhile( function (x) { return Math.log(x) < 1; }) .toArray(); [ 0, 1, 2 ] ``` #### drop ```js Sequence().drop( amount ) // >>> Sequence drop( amount, sequence ) // >>> function ``` Returns a sequence of the items in `sequence` that would be excluded from the subsequence returned by an equivalent `take` operation. #### dropWhile ```js Sequence().dropWhile( predicate ) // >>> Sequence dropWhile( predicate, sequence ) // >>> function ``` Returns a sequence of the items in `sequence` that would be excluded from the subsequences returned by an equivalent `takeWhile` operation. #### concat Concatenates sequences. ```js Sequence().concat( ...sequences ) // >>> Sequence concat( sequence, ...sequences ) // >>> function ``` Returns a single `Sequence`, or a sequence generator function, that is the concatentation of each sequence in the order they are provided. ```js Sequence.range(3).concat( range(5) ).toArray(); [ 0, 1, 2, 0, 1, 2, 3, 4 ] Sequence('abc').concat('def', 'ghi').toArray(); [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' ] toArray( concat() ); [] ``` #### splitAt Divides a `sequence` into two sequences, where the first contains the first `n` elements of `sequence`, and the second contains the remaining elements of `sequence`. ```js Sequence().splitAt( n ) // >>> array splitAt( n, sequence ) // >>> array ``` Returns a two-element array containing both partitions of `sequence`. ```js Sequence('qwerty') .splitAt(4) .map( Sequence ) .map( function (s) { return s.toArray().join(''); }); [ 'quer', 'ty' ] ``` #### splitWith Divides a `sequence` into two sequences, where the first contains elements, starting from the head of `sequence`, that return logical true when `predicate` is applied to the element. ```js Sequence().splitWith( predicate ) // >>> array splitWith( predicate, sequence ) // >>> array ``` Returns a two-element array containing both partitions of `sequence`. ```js function isConsonant (char) { return /[^aeiouy]/i.test(char); } Sequence('schtick') .splitWith( isConsonant ) .map( Sequence ) .map( function (s) { return s.toArray().join(''); }); [ 'scht', 'ick' ] ``` #### partition Partitions `sequence` into subsequences of `size` elements at offsets `stride` elements apart, where `stride` defaults to `size` such that partitions do not overlap. If the final partition is shorter than `size`, then: if a `padding` sequence is provided, that partition is filled with elements from `padding` until it grows to `size`; if no `padding` exists, the short partition is dropped. ```js Sequence().partition( size, stride, padding ) Sequence().partition( size, stride ) Sequence().partition( size ) // >>> Sequence partition( size, stride, padding, sequence ) partition( size, stride, sequence ) partition( size, sequence ) // >>> function ``` Returns a `Sequence` of partitions, or a sequence generator function that returns an iterator over the partitions, where each partition is itself a sequence generator function that returns an iterator over the elements of the partition. ```js Sequence.range(12).partition(4).map( toArray ).toArray(); [ [ 0, 1, 2, 3 ], [ 4, 5, 6, 7 ], [ 8, 9, 10, 11 ] ] Sequence.range(11).partition(4).map( toArray ).toArray(); [ [ 0, 1, 2, 3 ], [ 4, 5, 6, 7 ] ] Sequence.range(16).partition(4,6).map( toArray ).toArray(); [ [ 0, 1, 2, 3 ], [ 6, 7, 8, 9 ], [ 12, 13, 14, 15 ] ] Sequence.range(14).partition(4, 6, ['a']).map( toArray ).toArray(); [ [ 0, 1, 2, 3 ], [ 6, 7, 8, 9 ], [ 12, 13, 'a' ] ] Sequence.range(14).partition(4, 6, 'abc').map( toArray ).toArray(); [ [ 0, 1, 2, 3 ], [ 6, 7, 8, 9 ], [ 12, 13, 'a', 'b' ] ] Sequence.range(4).partition(10).map( toArray ).toArray(); [] Sequence.range(4).partition(10,10,[]).map( toArray ).toArray(); [ [ 0, 1, 2, 3 ] ] ``` #### partitionBy Applies `fn` to each value in `sequence`, partitioning it into subsequences each time `fn` returns a new value. ```js Sequence().partitionBy( fn ) // >>> Sequence partitionBy( fn, sequence ) // >>> function ``` Returns a `Sequence` of partitions, or a sequence generator function that returns an iterator over the partitions, where each partition is itself a sequence generator function that returns an iterator over the elements of the partition. ```js Sequence .range(1,6) .partitionBy( function (x) { return x === 3 } ) .map( toArray ) .toArray(); [ [ 1, 2 ], [ 3 ], [ 4, 5 ] ] Sequence( [1,1,1,2,2,3,3] ).partitionBy( isOdd ).map( toArray ).toArray(); [ [ 1, 1, 1 ], [ 2, 2 ], [ 3, 3 ] ] Sequence( [1,1,1,2,2,3,3] ).partitionBy( isEven ).map( toArray ).toArray(); [ [ 1, 1, 1 ], [ 2, 2 ], [ 3, 3 ] ] Sequence("Leeeeeerrrrooyyy") .partitionBy( identity ) .map( toArray ) .toArray(); [ [ 'L' ], [ 'e', 'e', 'e', 'e', 'e', 'e' ], [ 'r', 'r', 'r', 'r' ], [ 'o', 'o' ], [ 'y', 'y', 'y' ] ] ``` #### interleave ```js Sequence.interleave( ...sequences ) Sequence().interleave( ...sequences ) // >>> Sequence interleave( ...sequences ) // >>> function ``` Given one or more `sequences`, defines a sequence consisting of the first item from each of the `sequences`, continuing with the second item from each of the `sequences`, etc., stopping after any one of the `sequences` has been exhausted. ```js Sequence.interleave( range(), [7,8,9] ).toArray(); [ 0, 7, 1, 8, 2, 9 ] // no `3` ``` #### interpose Places a `value` between each element in a `sequence`. ```js Sequence().interpose( value ) // >>> Sequence interpose( value, sequence ) // >>> function ``` Returns a new logical sequence containing the `value` interposed within the elements of the original sequence. ```js Sequence("dmtr").interpose('e').toArray(); [ 'd', 'e', 'm', 'e', 't', 'e', 'r' ] ``` #### sort Sorts the contents of a finite, homogeneous logical sequence, as directed by a pure function `comparator` if provided, or by `compare` otherwise. Elements of the sequence which are themselves sequences are compared by recursively comparing the respective elements of both nested sequences. ```js Sequence().sort( comparator ) Sequence().sort() // >>> Sequence sort( comparator, sequence ) sort( sequence ) // >>> function ``` Returns a new logical sequence containing the sorted elements of the original sequence. ```js Sequence([ 'abduct', 'abacus', 'abated', 'abate' ]) .map( Sequence ) .sort( compare ) .map( toArray ) .map( function (a) { return a.join(''); } ) .toArray(); [ 'abacus', 'abate', 'abated', 'abduct' ] ``` * * * ### &#x1f44b; [Iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/The_Iterator_protocol [pim.js]: http://npmjs.org/package/pim