SourceForge.net Logo

HERESY – Haskellesque lazy-list and functional tools with a Common Lisp slant.


 

Abstract

Heresy is a list of utilities for lazy and functional programming in Common Lisp.  Function names are chosen and decorated so as to resemble Haskell and/or CL equivalents; but to allow full import of exported symbols into a typical namespace.

 

The lazy-list functions provided are for when the memory/cpu profile of lazy list evaluation, or list-based solutions requiring lazy evaluation (such as self-referencing lists, or sequences with issues running beyond some termination point) are desired.  Expressive interoperation/conversion between CL sequences and lazy-lists, and control over degree of “laziness” are design goals.  Lists can be heterogeneous (containing elements of different types), and functions named after Haskell list equivalents may be altered for lispiness.

 

 

The code comes with a BSD-style license so you can basically do with it whatever you want.

 

Heresy library source can be downloaded at http://sourceforge.net/projects/cl-heresy/

 

Heresy is listed at http://www.cl-user.net/asp/libs/Heresy

 

Mailing List:  http://common-lisp.net/cgi-bin/mailman/listinfo/heresy-devel

 

 

Highlights/Examples

Naming conventions:

 

Generally, function names and call signatures are chosen to replicate a Haskell, CL or both - generally with a trailing / .

 

(list/ 3 2 1 0)                                                     ; returns lazy-list (3 2 1 0)

(map/ #'+ (list/ 1 2 3) (list/ 4 5 6))                     ; returns lazy-list (5 7 9)

(nub-by/ ‘equal (list/ 1 4 2 3 2 1))                     ; loose equivalent of haskell’s nubBy

(tail/ (list/ 1 2 3 4)) or (cdr/ (list/ 1 2 3 4))          ; returns tail of the list

 

 

Interoperability:

 

Lazy-list functions accept lazy-lists as well as CL strings, lists and arrays (leveraging optimizations where possible).  Multi-dimension arrays are turned into lazy-lists of lazy-lists.

 

(map/ #'list "Hello" (list 1 2 3) #(4 5 6) (list/ “X” “Y” “Z”))     ; returns lazy-list ((#\H 1 4 "X") (#\e 2 5 "Y") (#\l 3 6 "Z") )

 

to-array, to-string, to-list evaluate lazy-list (run to its end) into their respective CL types.

 

(to-array (map/ #'+ (list/ 1 2 3) (list/ 4 5 6)))    ; returns array #(5 7 9)

(to-list (map/ #'+ (list/ 1 2 3) (list/ 4 5 6)))        ; returns CL proper list (5 7 9)

 

 

Self-referencing lists:

 

The clichéd Fibonacci example:

(Request 1000000th element in Fibonacci sequence using self-referencing list)

 

(nth/ 1000000

 (self-ref-list/ fib (list*/ 1 1 (map/ #'+ fib (tail/ fib)))))

 

; self-ref-list internally memoized (to allow order-n solution); but with deferred creation (until extract time) to prevent storage bloat by holders of list reference.

 

 

Control over laziness:

 

 (defparameter my-list '(1 2 3 4 5))

 

 (eager (tail/ (tail/ my-list))) ; returns lazy-list that internally points to subset of my-list list '(3 4)

 (lazy (tail/ (tail/ my-list)))   ; returns lazy-list that has traversal through 2 elements deferred (until first request) – closure internally references my-list '(1 2 3 4)

 

 

Convenient, functional lazy parsing/decomposition of data structures:

 

This example “picks apart” the contents of a CSV (comma separated value) file, which can be a lazy-list of chars.  It handles the case of commas in strings, and doesn’t use them as the basis of a list.  The result is a lazy-list of lazy-lists representing the comma-separated sections.

 

(The algorithm basically converts each line’s chars into (cons char is-quote), then scanl/’s these to mark each char as “in quotes”, to use as a basis for split only on commas that are not in quotes, i.e. ‘(#\, . NIL)  )

 

REPL> (defparameter csv-file

      "1,2, 3 , I contain \" Quoted, commas, \" you see, 99

      g, \"hijk\"lmn

      third_line,stuff here")

 

REPL> (map/ (curried #'map/ (composed #'to-string (curried #'map/ #'car)))

          (map/ (composed

                 (lambda (line) (split-down-on-test/ (curried #'equal '(#\, . nil)) line))

                 (curried #'scanl1/ (lambda (a b) (cons (car b) (if (cdr a) (not (cdr b)) (cdr b)))))

                 (curried #'map/ (lambda (elt) (cons elt (eql elt #\")))))

                (split-down-on-test/ (curried #'eql #\newline) csv-file)))

Returns the result:

 

(LIST/

 (LIST/ "1" "2" " 3 " " I contain \" Quoted, commas, \" you see" " 99")

 (LIST/ "g" " \"hijk\"lmn")

 (LIST/ "third_line" "stuff here"))

 

 

 

Installation:

Install from www.sourceforge.net

Heresy is asdf-compliant.

(use-package :heresy) is recommended as the functions are intended to be used frequently, and the symbols were chosen so as to not clash with known libraries.

 

Performance:

Heresy’s lazy-list operations are generally functions that accept values and functions – in a straight-up example, map/ (plus expansion of its emitted list) cannot match the CPU performance of a straight-up mapcar/.  Loop/while can generally outrun the expanded map/ and filter/ equivalent.  In typical use, any given expression may be composed of both lazy-list and CL functions.

 

What lazy-list use can do is alter the memory profile of a solution, or relocate the performance hit of expensive list traversals to the user rather than the definer.

 

The memory advantages may be realized through creation of lazy-lists that “contain” large objects – this list may be declared in terms of functional constructs without the static memory requirement of keeping them all in RAM.  If the end-user of this list filters them, or uses them in a loop, or uses/discards them as the basis for a subsequent computation, the system may only have one in RAM (and uncollectable) at a time.

 

 

(defun make-lazy-list-of-huge-results (inputs)

  (map/ #'make-huge-result inputs))

; returns lazy-list of huge-results

 

(let ((huge-results (make-lazy-list-of-huge-results inputs)))  ; minimal memory footprint, no huge results actually created yet.

  (map/ #'make-small-result huge-results))   ; huge-results created and discarded one at a time.

 

 

Thread-Safety:

Thread-safety is designed in; but presently deactivated.  It will generally entail function call contexts similar to Eager and Lazy ; but come with a significant performance cost wherever used (first-time traversal of certain memoized sequences, or operations that work internally in terms of them, will pay a locking pentalty).

 

Thread-safety will be reactivated in a future release, along with a dependency upon Bordeaux-threads.

 

In heresy’s current non-thread-safe guise, it is advisable to have any lazy-list or fragment thereof only available for read, evaluation or traversal by one thread at a time.  To be extra-safe, consider only transferring frozen containers (proper lists, arrays) between threads.

 

Functions:

Note:  Unless otherwise specified, functions accepting lists as parameters will accept any lazy-list, proper list, or other CL sequence, any of which can be heterogeneous.  In this regard, strings are considered lists of type character.  Lazy-lists that are direct wrappers for strings, vectors or arrays will maintain a reference to the entire array, even if they represent only a subset of the array’s values.  (Where retention of the original container is undesirable, they, or a derived result result, should be evaluated into a new non-lazy container).

 

Unless otherwise specified, functions return lazy-lists (although where possible, these are internally indicated as being based on a static container for optimizations in other functions).

 

Where possible, functions accepting lists switch on/are optimized for lists or sequences, or for lazy-lists that happen to be wrapping lazy-lists.

 

Interoperability:

 

[Function]
string-from-chars/ chars-list => result

Equivalent to to-string

Evaluates the provided char-list (which can be a lazy-list or CL sequence) to its end, concatenating all chars into a single string.  Call will fail if any elements in its input list are non-characters.

 [Function]
to-list list => CL proper list

Returns the input list in proper-list form – evaluating all values to the end.

 

[Function]
to-array list &rest array-params => CL Array

Returns the input list in array form – evaluating all values to the end.  Any (optional) array-params provided are fed to an internal call to common-lisp::make-array – see the CL spec for documentation on its &key parameters.

 

[Generic function]
to-string chars-list => string

Equivalent to sstring-from-chars/

Evaluates the provided char-list (which can be a lazy-list or CL sequence) to its end, concatenating all chars into a single string.  Call will fail if any elements in its input list are non-characters.

 

 

Meat and Potatoes:

 

 

[Function]
and/ list => last element in list or NIL

Returns nil if any element in the provided list is nil, otherwise returns the last element


[Function]
append/ &rest lists => lazy-list of inputs chained consecutively

Lazy equivalent of CL’s append – returns a lazy-list of its input lists (which can be CL sequences or lazy-lists) chained end to end.  Internally implemented in terms of concat/


[Function]
assoc/ item alist &rest assoc-rest-params => found result or NIL

Equivalent of CL’s assoc – accepts a lazy-list of cons-cells, returns a found result or nil.  The &key params in assoc-rest-params work as equivalents to those in CL’s assoc.

 

[Function]
car/, cdr/, cadr/, c****r/ &list => Result

Lazy-list substitutes for CL’s car, cdr, cadr, etc.  Implemented up to 5 composing characters.

Any car components result in instant traversal to obtain the first value in a lazy-list.

Any cdr components to the function called request traversal based on the current “laziness’ context.

 

In Eager context: cdr components traverse immediately, returning the new head of the list.

In Lazy context: cdr components return the original list + deferred traversal – true traversal only occurs on first traversal of the result.  Performance Warning:  In a Lazy context, cdr/ (or composed equivalent) of a lazy-list stores the original list + the traverser, so lazy cdr/ of a list that’s holding a certain amount of RAM won’t alter the required RAM.


[Function]
composed &rest functions => composition of functions

Composes functions provided as parameters.

(composed #'aa #'bb) returns a function that calls #'bb on its params, then passes the result to a call to #'aa


[Function]
concat/ list-of-lists => lazy-list of inputs chained consecutively

Returns the provided list-of-lists concatenated consecutively, as a lazy-list.  Uses various internal optimizations based upon their input types.  Input parameter list-of-lists is only traversed as the result traversal reaches the end of each sub-list’s values.


[Function]
curried function &rest largs => function

Returns a function that applies largs + its parameters to function


[Function]
drop-while/ test list => lazy-list

Returns a list that has elements dropped from list while function test applied to the list element is non-NIL, or end is reached.

In Eager context: Traverses list immediately, returning only from first where test returns NIL.

In Lazy context: Does not traverse list on call – first traversal of result will trigger test to find first non-NIL.  (Storage warning:  result will keep references to original list + the test function).


[Function]
drop/ to-drop list => lazy-list

Identical to nthcdr/

Returns lazy-list with first to-drop elements skipped/traversed from list, or empty list if end reached..

In Eager context: Traverses list immediately, returning sub-list after drop-list traversals..

In Lazy context: Does not traverse list on call – first traversal of result will trigger traversal of list until to-drop elements are skipped or end is found.  (Storage warning:  result will internally keep references to original list + to-drop).


[Macro]
eager declaration* statement* => result

Enters an "Eager" context - calls to functions such as tail/ do traversal before returning. This context uses a special variable, and extends into sub-calls until overridden.  Eager context is the default.  Relevant functions are documented with their respective context effects.


[Function]
filter/ predicate list => lazy-list

Traversal of the resultant list skips any list elements for which running predicate against the element fails.

(Storage warning:  resultant list will internally keep references to original list + predicate).


[Function]
first/ list => value

Lazy-list equivalent of CL’s first function.  Returns first element in a list, or NIL if nothing present.


[Function]
foldl/ function first list => result

Equivalent of Haskell’s foldl – better explained on the net.


[Function]
foldl1/ function list => result

Equivalent of Haskell’s foldl1 – foldl with the first in the list used as first


[Function]
foldr/ function first list => result

Equivalent of Haskell’s foldr – better explained on the net.


[Function]
foldr1/ function list => result

Equivalent of Haskell’s foldr1 – foldr with the first in the list used as first


[Function]
grouped-cdrs-by-car/ list-of-cons-pairs &key test => key->values-list pairs list, lookup function

Basically – a very useful orthogonal way to interact functionally with a hash-table.


Takes a lazy-list or sequence of cons pairs, of the form (first . second) - returns a list of conses of the form (first . (second 0 second1 second2 second3....)) as the first value, where the seconds are matches on first.

 

Second return value is a function, that returns a list of seconds based on a search key/first as first value, found (T or NIL) as second.

 

In Eager context: grouped-cdrs-by-car/ calculates the internal hash immediately.

In Lazy context:  The creation of the internal hash is deferred – it is created either on traversal of the resultant list, or execution of the second return value.

 

Example:

REPL> (grouped-cdrs-by-car/ '((1 . 2) (2 . 4) (2 . 5) (3 . 9) (2 . 7)))

(LIST/ (1 2) (2 4 5 7) (3 9)); first result value = lazy-list of (cons key values-list)

#<Function>                  ; second result value is a lookup function

 

REPL> (defparameter x

(multiple-value-list

(grouped-cdrs-by-car/ '((1 . 2) (2 . 4) (2 . 5) (3 . 9) (2 . 7)))))

X   ; x now contains first and second return-values consecutively in a CL proper list.

 

REPL> (first x)

(LIST/ (1 2) (2 4 5 7) (3 9))

 

REPL> (funcall (second x) 2)

(4 5 7)    ; list or values supplied under key 2

T          ; Found

 

REPL> (funcall (second x) 1)

(2)        ; Values under key 1

T          ; Found

 

REPL> (funcall (second x) 7)

NIL        ; empty list (not found)

NIL        ; Not found


[Function]
grouped-seconds-by-first/ list-of-list-pairs &key test => result

Equivalent to grouped-cdrs-by-car/, except that the input pairs come as a list of lists, instead of a list of conses.


[Function]
head-tail/ list => first element as first return-value, rest of list as second return-value

Returns head and tail of a lazy-list as return-values

 

Example:

REPL> (head-tail '(1 2 3 4 5))

1

(list/ 2 3 4 5 6) ; result is a lazy-list regardless of input; but will generally

                  ; lazy-list implementation optimal to the input, e.g. list-based

; for proper list input.

 


[Function]
head/ list => result

Returns head of a list – equivalent of car/


[Function]
intersperse/ val list => result

Returns a lazy-list with val interspersed between elements of list.


[Function]
iterate/ from-previous element &optional end-before-func => result

Equivalent of Haskell’s iterate – returns an infinite list of element, then function from-previous successively applied to each list elemenet.


[Function]
iteratex/ input-to-contribution initial-input => result

Rigged to default to iterate/ behavior, except input-to-contribution MUST return a list, even if that list is a list of one value.


[Macro]
lazy declaration* statement* => result


Enters an "Eager" context - calls to functions such as tail/ don’t do do traversal before returning value; but at traversal time of the result. This context uses a special variable, and extends into sub-calls until overridden.  Eager context is the default.  Relevant functions are documented with their respective context effects.



[Function]
length/ list => integer length

Traverses its input list to the end, counting the elements.  Where possible, (input is an array or other considerations) length/ attempts to avoid traversal.


[Function]
list*/ &rest list-list-terminated => result

Identical to prepend/

Equivalent of CL’s list* - returns a lazy-list of all input parameters, except for the last which must be a list or lazy-list and has its elements chained onto the end.


[Function]
list/ &rest rest => result

Equivalent of CL’s list function – returns a lazy-list comprising all input parameters (in implementation, this list will be marked as proper-list-based and will enjoy certain optimizations in functions using it).


[Macro]
loop-over/ symbol lazy-list declaration* statement* => NIL

Runs *statement* with symbol taking on the value of each element in lazy-list.  Note:  Only accepts lazy-lists.


[Function]
map/ function first &rest other-lazy-lists => lazy-list

Quintessential map function – takes multiple lists/lazy-lists, traverses and passes the current from each as parameters to function.  Stops on reaching the end of any of its input lists (i.e. the shortest).


[Function]
memoized/ list => result

Memoizes list nodes for traversal and (where underlying list dictates) value.  Any links traversed or values made available/extracted will be stored in memory for future accesses.   Generally detects lists that are already memoized, or that are based on a static structure (e.g. proper list) and returns its input list.

 

[Function]
non-null/ list => Boolean, NIL or something else

NIL if list contains contents, non-NIL if empty


[Function]
nth/ index list => value

Traverses index elements through list and returns the evaluated value at that location.


[Function]
nthcdr/ to-drop list => result lazy-list

Identical to drop/

Returns lazy-list with first to-drop elements skipped/traversed from list, or empty list if end reached..

In Eager context: Traverses list immediately, returning sub-list after drop-list traversals..

In Lazy context: Does not traverse list – first traversal of result will trigger test to find first non-NIL.  (Storage warning:  result will internally keep references to original list + to-drop).


[Function]
nub-by/ equality list => lazy-list of uniques

Equality has to be one of the valid inputs to :test field in CL make-hash-table (‘eq, ‘eql, ‘equal, ‘equalp)

Returns a memoized list with uniqueness guaranteed by hash-table test.  List is scanned on an as-needed basis to satisfy initial requests to result.


[Function]
nub/ list => result

Basically nub-by/ with its first parameter as ‘eql


[Function]
null/ list => Boolean, NIL or something else

Returns non-NIL if lazy-list is non-empty.  For lazy-lists that aren’t based on a fixed container, this may cause traversal through a single element to determine end.


[Function]
or/ list => NIL or first non-NIL element in list

Returns first non-NIL in list.  For lazy-lists, traverses only to the first non-NIL element.  Where relevant, causes evaluation (to value) of traversed elements.


[Function]
position/ item list &key test => result

Analogous to CL’s position function – traverses list, comparing value found with item using test (which defaults to ‘eql).


[Function]
prepend/ &rest list-list-terminated => result

Identical to list*/

Equivalent of CL’s list* - returns a lazy-list of all input parameters, except for the last which must be a list or lazy-list and has its elements chained onto the end.


[Function]
rcurried function &rest rargs => function

Returns a function that applies its parameters + rargs to function


[Function]
scanl/ function first list => list of intermediate results

Equivalent of Haskell’s scanl – better explained on the net.


[Function]
scanl1/ function list => list of intermediate results

Equivalent of Haskell’s scanl1 – better explained on the net.


[Function]
scanr/ function first list => list of intermediate results

Equivalent of Haskell’s scanr – better explained on the net.


[Function]
scanr1/ function list => list of intermediate results

Equivalent of Haskell’s scanr1 – better explained on the net.


 [Function]
second/ list => result

            Analogous to CL’s second, returns the second element in supplied sequence/lazy-list, nil if list is smaller than length 2.


[Macro]
self-ref-list/ ref-name declaration* statement* => lazy-list

Defines a lexical context in which ref-name is assigned the list defined by statement, which may refer to itself via ref-name.

The forms in statement are executed in a lazy evaluation context.  This is due to a convention amongst lazy-list functions that traverse lists, that they don’t actually attempt traversal until the first request against them.  While the lazy context can be overridden/the list otherwise traversed, any attempt to do so before the full statement form is evaluated will fail.

The evaluated list is memoized/cached.

 

The forms in statement are executed to create the underlying memoized list only on first traversal.  This is a performance optimization to accommodate most CL implementations, where callers of a function keep that reference ineligible for garbage collection.

Consider the clichéd Fibonacci example:

 

(nth/ 1000000

 (self-ref-list/ fib (list*/ 1 1 (map/ #'+ fib (tail/ fib)))))

 

This example requires the memorization to keep this from becoming the recursive solution (performance wise).  However, if the caller to nth/ were to keep the pointer to the head, every calculated result would be kept ineligible for garbage collection.  Thus, the forms inside the self-ref-list are only evaluated upon the first access by nth/’s implementation starting traversal, and the head of the memoized chain is not kept by the caller, only the ability to request the list be recreated from scratch.

In this Fibonacci example, due to the implementation’s details, as nth/ traverses; only the “previous” and “current” numerical values are kept ineligible for garbage collection.

 

In short, it works the way it does to allow control over or minimization of the memory footprint in calculating solutions based upon self-referencing lists.


[Function]
sort-by/ ordering list => list

Sorts list via dual-input function ordering (returning if first param should be before second).

 

In Eager context: Performs actual sort and creation of stored list of sorted values at point of call, returning a CL proper-list based lazy-list.

In Lazy context: Defers creation of persistent sorted list until first element is traversed.  Future advances through list traverse a persistent proper list.  (Performance note:  Re-traversing the result list from the start causes a rerun of the sort algorithm/creation of a new persistent sorted result.  This is of concern if double-scanning the result list).

 


[Function]
sort/ list => result

Basically sort-by/ with ordering parameter set to #'<

 

[Function]
split-at/ index list => list from before index, list after

First result is pre-index, second result is list for at predicate true and later.  First result stores a persistent linked-list of values, except in lazy mode when this linked list is created only on first traversal.

In Eager context: First return value is linked-list based lazy-list, second reads from input list from index onward.

In Lazy context:  First traversal of either result will cause the traversal through list to determine the actual split lists.  From then on, the result lists will approximate their Eager counterparts, albeit not being typed for any optimizations based on their contents.

 

 [Function]
split-when/ predicate list => list from before predicate true, list after

First result is pre-predicate-true, second result is list for at predicate true and later.  First result stores a persistent linked-list of values, except in lazy mode when this linked list is created only on first traversal.

In Eager context: First return value is linked-list based lazy-list, second reads from input list from predicate-true point onward.

In Lazy context:  First traversal of either result will cause the traversal through list to determine the actual split lists.  From then on, the result lists will approximate their Eager counterparts, albeit not being typed for any optimizations based on their contents.


[Function]
split-down-on-test/ test list &key keep-split-causing-elements keep-empty-non-split process-split-causing-element process-non-split-causing-elements-list => List of split lists, possibly including the split-causing elements

With default parameters, split-down-on-test/ returns a lazy-list of lazy-lists containing contiguous elements of list for which test returns nil.

The parameter keep-split-causing-elements defaults to NIL; but with a non-NIL value causes the items for which test return non-NIL to be included in the result list – albeit on their own, not in group lists.

The parameter keep-empty-non-split defaults to T –NIL empty lists (between adjacent separators, or when separators at start or end) are culled.

The parameter process-split-causing-element accepts a function that can be used to transform the elements retained by keep-split-causing-elements

The parameter process-non-split-causing-elements-list allows transformation of the contiguous non-splitting element lists.

 

 

Example:

REPL> (split-down-on-test/ #'evenp (list 1 3 2 5 5 7 8 7 9))

(LIST/  (LIST 1 3) (LIST/ 5 5 7) (LIST/ 7 9))

 

REPL> (split-down-on-test/ #'evenp (list 1 3 2 5 5 7 8 7 9) :keep-split-causing-elements t)

(LIST/ (LIST/ 1 3) 2 (LIST/ 5 5 7) 8 (LIST/ 7 9))

 

REPL> (split-down-on-test/ #'evenp (list 1 3 2 5 5 7 8 7 9) :keep-split-causing-elements t :process-split-causing-element (lambda (x) (* x 1000)))

(LIST/ (LIST/ 1 3) 2000 (LIST/ 5 5 7) 8000 (LIST/ 7 9))

 

REPL> (split-down-on-test/ #'evenp (list 1 3 2 5 5 7 8 7 9) :keep-split-causing-elements t :process-non-split-causing-elements-list (curried #'map/ (curried #'* 10)))

(LIST/ (LIST/ 10 30) 2 (LIST/ 50 50 70) 8 (LIST/ 70 90))

 

Performance:  See split-on-test from which split-down-on-test’s actions are composed.  By necessity, traversal through the result items necessitates the scan/creation of the prior.  Each non-split list returned is cached/memoized.


[Function]
split-on-test/ test list => List representing values before test true, list representing after test true, call returning value at test true

Returns 2 lists (before and after test-true), and a function (as third return) that returns the value at test-true, and a T/Nil if test-true were actually found (emptiness of the 2 lists is not evidence one way or another).

The first/”before” list is cached/memoized on scan.

The second/”after” return list (after test) is not memoized; but causes the full evaluation of the “before” upon traversal.

The third value, the call-for-value-at-test, causes the evaluation of the “before” list upon traversal.

 

Performance: Basically, split-on-test is lazy in terms of traversal of list or application of test; but, by necessity, the 2nd and 3rd return values require that this traversal have taken place.

 


[Function]
split-positional/ positional list => Lazy-list of split results

Splits input list to list of sub-lists based upon the position or positions described in positional.

Parameter positional can be one of:

Integer:  Returns a list of lists with the first terminating prior to integer in positional, the remainder, (if any), will be in a second list.

Function:  A function that accepts an integer and returns a T or NIL, with T indicating a split point.

List/lazy-list of integers, in order – indicates a sequence of split points.

The result list of split-positional is always comprised of the contents of list, with the positional driving only the sub-lists breakdown.  Each split-point indicates the start of a sub-list, with the exception of the first if it is not indicated by positional.

 

Example:

REPL> (split-positional/ '(5 7 8) '(0 10 20 30 40 50 60 70 80 90 100 110 120))

(LIST/ (LIST/ 0 10 20 30 40) (LIST/ 50 60) (LIST/ 70) (LIST/ 80 90 100 110 120))  ; Indices dictated by positional

 

REPL> (split-positional/ (composed #'zerop (rcurried #'mod 3)) '(0 10 20 30 40 50 60 70 80 90 100 110 120))

(LIST/ (LIST/ 0 10 20) (LIST/ 30 40 50) (LIST/ 60 70 80) (LIST/ 90 100 110) (LIST/ 120))  ; indices validated against mod-3 function provided to positional

 


 [Function]
tail/ list => result list

Basically equivalent of cdr/ or (drop/ 1 list) – returns the sub-list after traversing in by one.

In Eager Context: Traverses immediately, returns a list referencing only the remainder.

In Lazy Context: Defers traversal until first traversal of result list.  Holds a reference to list plus traversal logic.  Performance Warning:  tail/ in a lazy context will keep a reference to all storage retained by list.


[Function]
tails/ list => Lazy-list of lists

Returns a list of lists, where each sub-list represents a successive cdr of the preceding one, up to and including an empty list for the last.  Doesn’t really cost more than traversal.

 

Example:

REPL> (tails/ '(1 2 3 4 5))

(LIST/

 (LIST/ 1 2 3 4 5)

 (LIST/ 2 3 4 5)

 (LIST/ 3 4 5)

 (LIST/ 4 5)

 (LIST/ 5)

 (LIST/))


[Function]
take-while/ test list => List

Returns a list composed of elements of list up until (but not including) the first element for which test returns non-NIL.

Performance note:  Result list maintains a reference to list (keeping it ineligible for garbage collection).  Convert result to a fixed container (one traversal + new storage) if this is undesirable.


[Function]
take/ to-take list => List

Returns a list of the first to-take elements in list if available.

Performance note:  Result list maintains a reference to list (keeping it ineligible for garbage collection).  Convert result to a fixed container (one traversal + new storage) if this is undesirable.


[Function]
third/ list => result

Equivalent of CL’s third – returns the third element in an input list/lazy-list/sequence if available.