list

package
v0.0.0-...-b3929ad Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 17, 2021 License: BSD-3-Clause, BSD-3-Clause Imports: 3 Imported by: 0

README

Lisp/Scheme-style lists for Go

Package github.com/exascience/slick/list implements a list library for Go that mimicks functionality that is typically found in Lisp and Scheme languages which is based on pairs or cons cells.

To a large extent, this package follows the SRFI 1 specification for Scheme authored by Olin Shivers, which is part of the Scheme Requests For Implementations library. See https://srfi.schemers.org/srfi-1/srfi-1.html for SRFI 1, and https://srfi.schemers.org for Scheme Requests For Implementation.

Although this package is part of the slick repository, it is intended to be usable independently from the Slick programming language in Go projects, and to be maintained with such use in mind.

Documentation

Overview

Package list implements a list library for Go that mimicks functionality that is typically found in Lisp and Scheme languages which is based on pairs or cons cells.

To a large extent, his package follows the SRFI 1 specification for Scheme authored by Olin Shivers, which is part of the Scheme Requests For Implementations library. See https://srfi.schemers.org/srfi-1/srfi-1.html for SRFI 1, and https://srfi.schemers.org for Scheme Requests For Implementation.

Here are some, but not all details in which package deviates from SRFI 1:

* Go accepts variable number of parameters in function calls through slices. This package does not attempt to use lists for this purpose instead, so there is sometimes (but hopefully not very often) a need to convert between lists and slices. This package provides utility functions to support this: AppendToSlice, ToSlice, and FromSlice.

* Go does not allow for special characters in identifiers. That's why we cannot adopt Scheme's naming convention for linear-update ("destructive") functions whose names end in an exclamation mark (!). We therefore adopt Common Lisp's naming convention where such functions start with "N" (as in "Non-consing") instead, such as in NAppend, NDelete, NReverse, and so on.

* Several functions exist both as single list-parameter variants, which are implemented as methods on *Pair, and as multiple list-parameter variants, which are implemented as standalone functions. This makes it easier to handle the more common single list-parameter cases more efficiently, and use a somewhat more pleasant syntax.

* In Scheme, several functions return either some result value or Scheme's false value (#f) to indicate there is no result. Since Go is statically typed without support for choice types, we instead most of the time opted for returning multiple values, one for the primary return value, and a second boolean value indicating success or failure.

* Cons corresponds to Scheme's cons* or list*. Use NewPair for constructing new pairs.

* A proper list must end in Nil(). If it ends in nil (of type interface{}), then it's considered a dotted list. A dotted list is a non-circular list that does not end in Nil(), but in anything other than a value of type *Pair.

* The selectors Car, Cdr, Caar, Cddr, etc., are "liberal" in that they do not panic if the argument is not a proper list. They are more similar to Common Lisp's corresponding selectors rather than Scheme's in this regard. If you need stricter checks, just use the Car and Cdr fields directly.

* In the various folding functions, intermediate results are passed first, not last.

* Iota is not supported due to a lack of generic number operations in Go.

* Concatenate is not supported because it addresses a very Scheme-specific issue only.

General discussion:

Linear-update ("destructive") functions may alter and recycle cons cells from the argument list. They are allowed to, but not required to, but the implementation attempts to actually recycle the argument lists.

List-filtering functions such as Filter or Delete do not disorder lists. Elements appear in the answer list in the same order as they appear in the argument list.

Because lists are an inherently sequential data structure (unlike, say, slices), list-inspection functions such as Find, FindTail, Any, and Every commit to a left-to-right traversal order of their argument list.

However, constructor functions, such as Tabulate, and many mapping functions do not specify the dynamic order in which their functional argument is applied to its various values.

"Linear update" functions

Functionality is provided both in "pure" and "linear-update" (potentially destructive) forms whenever this makes sense. A "pure" function has no side-effects, and in particular does not alter its arguments in any way. A "linear update" function is allowed -- but not required -- to side-effect its arguments in order to construct its result. "Linear update" functions are typically given names starting with "N". So, for example, NAppend(list1, list2) is allowed to construct its result by simply assigning the Cdr of the last pair of list1 to point to list2 and then returning list1 (unless list1 is the empty list, in which case it would simply return list2).

What this means is that you may only apply linear-update functions to values that you know are "dead" -- values that will never be used again in your program. This must be so, since you can't rely on the value passed to a linear-update function after that function has been called. It might be unchanged; it might be altered.

The "linear" in "linear update" doesn't mean "linear time" or "linear space" or any sort of multiple-of-n kind of meaning. It's a fancy term that type theorists and pure functional programmers use to describe systems where you are only allowed to have exactly one reference to each variable. This provides a guarantee that the value bound to a variable is bound to no other variable. So when you use a variable in a variable reference, you "use it up." Knowing that no one else has a pointer to that value means a system primitive is free to side-effect its arguments to produce what is, observationally, a pure-functional result.

In the context of this library, "linear update" means you, the programmer, know there are no other live references to the value passed to the function -- after passing the value to one of these functions, the value of the old pointer is indeterminate. Basically, you are licensing these functions to alter the data structure if it feels like it -- you have declared you don't care either way.

You get no help from Go or this library in checking that the values you claim are "linear" really are. So you better get it right. Or play it safe and use the non-N functions -- it doesn't do any good to compute quickly if you get the wrong answer.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Any

func Any(predicate func(...interface{}) bool, lists ...*Pair) bool

Any applies the predicate across the lists, returning true if the predicate returns true on any application.

If there are n list arguments, then predicate must be a function taking n arguments and returning a single boolean value.

Any applies predicate to the first elements of the list parameters. If this application returns true, Any immediately returns true. Otherwise, it iterates, applying predicate to the second elements of the list parameters, then the third, and so forth. The iteration stops when a true value is produced or one of the lists runs out of values; in the latter case, Any returns false.

func lessThan(xs ...interface{}) bool {
  return xs[0].(int) < xs[1].(int)
}

Any(lessThan, List(3, 1, 4, 1, 5), List(2, 7, 1, 8, 2)) => true

func AppendLast

func AppendLast(lists ...*Pair) (result *Pair, last *Pair)

AppendLast is like Append, except that the whole result is newly allocated and does not share any structure with any of its arguments. AppendLast returns both the resulting list as well as the last pair of the resulting list. This enables setting the Cdr of the last pair to a value of a type other than *Pair.

func Caaaar

func Caaaar(x interface{}) interface{}

Caaaar returns Car(Car(Car(Car(x))))

func Caaadr

func Caaadr(x interface{}) interface{}

Caaadr returns Car(Car(Car(Cdr(x))))

func Caaar

func Caaar(x interface{}) interface{}

Caaar returns Car(Car(Car(x)))

func Caadar

func Caadar(x interface{}) interface{}

Caadar returns Car(Car(Cdr(Car(x))))

func Caaddr

func Caaddr(x interface{}) interface{}

Caaddr returns Car(Car(Cdr(Cdr(x))))

func Caadr

func Caadr(x interface{}) interface{}

Caadr returns Car(Car(Cdr(x)))

func Caar

func Caar(x interface{}) interface{}

Caar returns Car(Car(x))

func Cadaar

func Cadaar(x interface{}) interface{}

Cadaar returns Car(Cdr(Car(Car(x))))

func Cadadr

func Cadadr(x interface{}) interface{}

Cadadr returns Car(Cdr(Car(Cdr(x))))

func Cadar

func Cadar(x interface{}) interface{}

Cadar returns Car(Cdr(Car(x)))

func Caddar

func Caddar(x interface{}) interface{}

Caddar returns Car(Cdr(Cdr(Car(x))))

func Cadddr

func Cadddr(x interface{}) interface{}

Cadddr returns Car(Cdr(Cdr(Cdr(x))))

func Caddr

func Caddr(x interface{}) interface{}

Caddr returns Car(Cdr(Cdr(x)))

func Cadr

func Cadr(x interface{}) interface{}

Cadr returns Car(Cdr(x))

func Car

func Car(x interface{}) interface{}

Car returns nil if x is Nil(), x.Car if x is a non-nil *Pair, and otherwise nil.

func Cdaaar

func Cdaaar(x interface{}) interface{}

Cdaaar returns Cdr(Car(Car(Car(x))))

func Cdaadr

func Cdaadr(x interface{}) interface{}

Cdaadr returns Cdr(Car(Car(Cdr(x))))

func Cdaar

func Cdaar(x interface{}) interface{}

Cdaar returns Cdr(Car(Car(x)))

func Cdadar

func Cdadar(x interface{}) interface{}

Cdadar returns Cdr(Car(Cdr(Car(x))))

func Cdaddr

func Cdaddr(x interface{}) interface{}

Cdaddr returns Cdr(Car(Cdr(Cdr(x))))

func Cdadr

func Cdadr(x interface{}) interface{}

Cdadr returns Cdr(Car(Cdr(x)))

func Cdar

func Cdar(x interface{}) interface{}

Cdar returns Cdr(Car(x))

func Cddaar

func Cddaar(x interface{}) interface{}

Cddaar returns Cdr(Cdr(Car(Car(x))))

func Cddadr

func Cddadr(x interface{}) interface{}

Cddadr returns Cdr(Cdr(Car(Cdr(x))))

func Cddar

func Cddar(x interface{}) interface{}

Cddar returns Cdr(Cdr(Car(x)))

func Cdddar

func Cdddar(x interface{}) interface{}

Cdddar returns Cdr(Cdr(Cdr(Car(x))))

func Cddddr

func Cddddr(x interface{}) interface{}

Cddddr returns Cdr(Cdr(Cdr(Cdr(x))))

func Cdddr

func Cdddr(x interface{}) interface{}

Cdddr returns Cdr(Cdr(Cdr(x)))

func Cddr

func Cddr(x interface{}) interface{}

Cddr returns Cdr(Cdr(x))

func Cdr

func Cdr(x interface{}) interface{}

Cdr returns nil if x is Nil(), x.Cdr if x is a non-nil *Pair, and otherwise nil.

func Count

func Count(predicate func(...interface{}) bool, lists ...*Pair) (result int)

Count applies predicate element-wise to the elements of the lists, and a count is tallied of the number of elements that produce a true value. This count is returned. Count is guaranteed to apply predicate to the list elements in a left-to-right order. The counting stops when the shortest list expires.

func lessThan(xs ...interface{}) bool {
  return x[0].(int) < x[1].(int)
}

Count(lessThan, List(1, 2, 4, 8), List(2, 4, 6, 8, 10, 12, 14, 16)) => 3

At least one of the argument lists must be finite:

Count(lessThan, List(3, 1, 4, 1), Circular(1, 10)) => 2

func Equal

func Equal(x, y interface{}) bool

Equal determines list equality.

Proper list A equals proper list B if they are of the same length, and their corresponding elements are ==.

It is an error to apply Equal to circular lists.

func Every

func Every(predicate func(...interface{}) bool, lists ...*Pair) bool

Every applies the predicate across the lists, returning true if the predicate returns true on every application.

If there are n list arguments, then the predicate must be a function taking n arguments and returning a single boolean value.

Every applies predicate to the first elements of the list parameters. If this application returns false, Every immediately returns fales. Otherwise, it iterates, applying predicate to the second elements of the list parameters, then the third, and so forth. The iteration stops when a false values is produced or one of the lists runs out of values; in the latter case, Every returns true.

func Fold

func Fold(f func(intermediate interface{}, elements ...interface{}) interface{}, init interface{}, lists ...*Pair) (result interface{})

Fold is the fundamental list iterator.

If n list arguments are provided, then the f function must take n+1 parameters: the "seed" or fold state, which is initially init, and then one element from each list. The fold operation terminates when the shortest list runs out of values. At least one of the list arguments must be finite.

func FoldRight

func FoldRight(f func(intermediate interface{}, elements ...interface{}) interface{}, init interface{}, lists ...*Pair) (result interface{})

FoldRight is the fundamental list recursion operator.

If n list arguments are provided, then the f function must take n+1 parameters: the "seed" or fold state, which is initially init, and then one element from each list. The fold operation terminates when the shortest list runs out of values. At least one of the list arguments must be finite.

func ForEach

func ForEach(f func(elements ...interface{}), lists ...*Pair)

ForEach is like Map, but ForEach calls f for its side effects rather than for its values. ForEach is guaranteed to call f on the elements of the lists in order from left to right. At least one of the argument lists must be finite.

func Index

func Index(predicate func(...interface{}) bool, lists ...*Pair) (result int)

Index returns the index of the leftmost elements that satisfy predicate.

If there are n list arguments, then predicate must be a function taking n arguments and returning a single boolean value.

Index applies predicate to the first elements of the list parameters. If this application returns true, Index immediately returns zero. Otherwise, it iterates, applying predicate to the second elements of the list parameters, then the third, and so forth. When it finds a tuple of list elements that cause predicate to return true, it stops and returns the zero-based index of that position in the lists.

The iteration stops when one of the lists runs out of values; in this case, Index returns -1.

func lessThan(xs ...interface{}) bool {
  return xs[0].(int) < xs[1].(int)
}

Index(lessThan, List(3, 1, 4, 1, 5, 9, 2, 5, 6), List(2, 7, 1, 8, 2)) => 1

func equal(xs ...interface{}) bool {
  return xs[0].(int) == xs[1].(int)
}

Index(equal, List(3, 1, 4, 1, 5, 9, 2, 5, 6), List(2, 7, 1, 8, 2))    => -1

func IsCircular

func IsCircular(x interface{}) bool

IsCircular returns true if x is a circular list.

A circular list is a value such that for every n >= 0, Cdr^n(x) is a pair.

Terminology: The opposite of circular is finite.

func IsDotted

func IsDotted(x interface{}) bool

IsDotted returns true if x is a finite, non-Nil()-terminated list.

That is, there exists an n >= 0 such that Cdr^n(x) is not of type *Pair. This includes non-pair values which are considered to be dotted lists of length 0.

func IsDottedEnd

func IsDottedEnd(x interface{}) bool

IsDottedEnd returns true if x is not of type *Pair.

func IsEnd

func IsEnd(x interface{}) bool

IsEnd returns true if x is not of type *Pair, or if x is Nil().

func IsNilPair

func IsNilPair(x interface{}) bool

IsNilPair returns true if x is of type *Pair and Nil(). If x is not of type *Pair, IsNilPair panics.

func IsPair

func IsPair(x interface{}) bool

IsPair returns true if x is of type *Pair.

func IsProper

func IsProper(x interface{}) bool

IsProper returns true iff x is a proper list -- a finite, Nil()-terminated list.

More carefully: The empty list (that is, (*Pair)(nil)) is a proper list. A pair whose Cdr is a proper list is also a proper list.

Note that this definition rules out circular lists. This function detects circular lists and returns false in this case.

Lists that end in nil (of type interface{}) are not proper, but dotted lists.

func IsProperEnd

func IsProperEnd(x interface{}) bool

IsProperEnd returns true if x is of type *Pair and Nil().

func NAppendLast

func NAppendLast(lists ...*Pair) (result *Pair, last *Pair)

NAppendLast is the linear-update variant of AppendLast -- it is allowed, but not required, to alter cons cells in the argument lists to construct the result list, including the last list.

func PairFold

func PairFold(f func(intermediate interface{}, sublists ...*Pair) interface{}, init interface{}, lists ...*Pair) (result interface{})

PairFold is analogous to Fold, but f is applied to successive sublists of the lists, rather than successive elements -- that is, f is applied to the pairs making up the lists, giving this recursion.

For a finite list, the f function may reliably assign to the Cdr of each pair it is given without altering the sequence of execution.

At least one of the list arguments must be finite.

func PairFoldRight

func PairFoldRight(f func(intermediate interface{}, sublists ...*Pair) interface{}, init interface{}, lists ...*Pair) (result interface{})

PairFoldRight holds the same relationship with FoldRight that PairFold holds with Fold.

At least one of the list arguments must be finite.

func PairForEach

func PairForEach(f func(sublists ...*Pair), lists ...*Pair)

PairForEach is like ForEach, but f is applied to successive sublists of the argument lists. That is, f is applied to the cons cells of the lists, rather than the lists' elements. These applications occur in left-to-right order.

The f function may reliably assign to the Cdr of the pairs it is given without altering the sequence of execution.

At least one of the list arguments must be finite.

func SetEqual

func SetEqual(lists ...*Pair) bool

SetEqual returns true iff every list_i is set-equal to list_i+1, using == to compare elements.

Set-equal simply means that list_i is a subset of list_i+1, and list_i+1 is a subset of list_i.

SetEqual(List("b", "e", "a"), List("a", "e", "b"), List("e", "e", "b", "a")) => true

// Trivial cases
SetEqual() => true
SetEqual(List(1)) => true

func SetLessThanEqual

func SetLessThanEqual(lists ...*Pair) bool

SetLessThanEqual returns true iff every list_i is a subset of list_i+1, using == to compare elements.

List A is a subset of list B if every element in A is equal to some element of B.

SetLessThanEqual(List(1), List(1, 2, 1), List(1, 2, 3, 3)) => true

// Trivial cases
SetLessThanEqual() => true
SetLessThanEqual(List(1)) => true

func Unfold

func Unfold(
	predicate func(interface{}) bool,
	element func(interface{}) interface{},
	nextSeed func(interface{}) interface{},
	seed interface{},
	tailGen func(interface{}) interface{}) (result interface{})

Unfold is the fundamental recursive list constructor, just as FoldRight is the fundamental recursive list consumer.

Unfold is best described by its basic recursion:

Unfold(predicate, element, nextSeed, seed, tailGen) ==
  if predicate(seed) {
    return tailGen(seed)
  }
  return NewPair(element(seed), Unfold(predicate, element, nextSeed, nextSeed(seed), tailGen))

predicate   Determines when to stop unfolding.
element     Maps each seed value to the corresponding list element.
nextSeed    Maps each seed value to the next seed value.
seed        The "state" value for the Unfold.
tailGen     Creates the tail of the list; defaults to func(_ interface{}) interface{} { return Nil() }

In other words, we use nextSeed to generate a sequence of seed values

seed, nextSeed(seed), nextSeed^2(seed), nextSeed^3(seed), ...

These seed values are mapped to list elements by element, producing the elements of the result list in a left-to-right order. predicate says when to stop.

While Unfold may seem a bit abstract to novice functional programmers, it can be used in a number of ways:

// List of squares: 1^2, ..., 10^2
Unfold(func(x interface{}) bool {return x.(int) > 10},
       func(x interface{}) interface{} {return x.(int) * x.(int)},
       func(x interface{}) interface{} {return x.(int)+1},
       1, nil)

// Copy a proper list.
Unfold(IsNilPair, Car, Cdr, list, nil)

// Copy a possibly non-proper list.
Unfold(IsEnd, Car, Cdr, list, nil)

// Append head onto tail.
Unfold(IsNilPair, Car, Cdr, head, func(_ interface{}) interface{} { return tail })

func UnfoldRight

func UnfoldRight(
	predicate func(interface{}) bool,
	element func(interface{}) interface{},
	nextSeed func(interface{}) interface{},
	seed interface{},
	tail interface{}) (result interface{})

UnfoldRight is the fundamental iterative list constructor, just as Fold is the fundamental iterative list consumer.

UnfoldRight constructs a list with the following loop:

func loop(seed, list interface{}) interface{} {
  if predicate(seed) {
    return list
  }
  return loop(nextSeed(seed), NewPair(element(seed), list))
}
loop(seed, tail)

predicate   Determines when to stop unfolding.
element     Maps each seed value to the corresponding list element.
nextSeed    Maps each seed value to the next seed value.
seed        The "state" value for the UnfoldRight.
tail        list terminator

In other words, we use nextSeed to generate a sequence of seed values

seed, nextSeed(seed), nextSeed^2(seed), nextSeed^3(seed), ...

These seed values are mapped to list elements by element, producing the elements of the result list in a right-to-left order. predicate says when to stop.

While UnfoldRight may seem a bit abstract to novice functional programmers, it can be used in a number of ways:

// List of squares: 1^2, ..., 10^2
UnfoldRight(func (x interface{}) bool {return x.(int) == 0},
            func (x interface{}) interface{} {return x.(int) * x.(int)},
            func (x interface{}) interface{} {return x.(int) - 1},
            10, Nil())

// Reverse a proper list.
UnfoldRight(IsNilPair, Car, Cdr, list, Nil())

// AppendReverse(revHead, tail)
UnfoldRight(IsNilPair, Car, Cdr, revHead, tail)

Types

type Pair

type Pair struct {
	Car, Cdr interface{}
}

Pair is the core tuple type from which list- and tree-like data structures can be created.

func Append

func Append(lists ...*Pair) (result *Pair)

Append returns a list consisting of the elements of the first list followed by the elements of the other lists.

Append(List(1), List(2))          => (1 2)
Append(List(1), List(2, 3, 4))    => (1 2 3 4)
Append(List(1, List(2)), List(3)) => (1 (2) 3)

The resulting list is always newly allocated, except that it shares structure with the final list.

func AppendMap

func AppendMap(f func(elements ...interface{}) *Pair, lists ...*Pair) (result *Pair)

AppendMap maps f over the elements of the lists, just as in Map. However, the results of the applications are appended together to make the final result. AppendMap uses Append to append the results together. At least one of the list arguments must be finite.

func AppendTabulate

func AppendTabulate(length int, init func(int) *Pair) (result *Pair)

AppendTabulate applies init to each integer i, where 0 <= i < length, and uses Append to append together the results. No guarantee is made about the dynamic order in which init is applied to these integers.

func Circular

func Circular(element interface{}, moreElements ...interface{}) (result *Pair)

Circular constructs a circular list of the elements.

Circular(1, 2) => (1 2 1 2 1 2 ...)

func Cons

func Cons(element1, element2 interface{}, moreElements ...interface{}) (result *Pair)

Cons is like List, but the last argument provides the tail of the constructed list.

Cons(1, 2, 3, 4) => (1 2 3 . 4)

func FilterMap

func FilterMap(f func(elements ...interface{}) (interface{}, bool), lists ...*Pair) (result *Pair)

FilterMap is like Map, but only when f returns true as a second value, the first value is saved. At least one of the list arguments must be finite.

func FromSlice

func FromSlice(slice interface{}) (result *Pair)

FromSlice uses Go's reflect package to convert the slice to a list.

func List

func List(elements ...interface{}) (result *Pair)

List returns a newly allocated list of its arguments.

func Map

func Map(f func(elements ...interface{}) interface{}, lists ...*Pair) (result *Pair)

Map applies f element-wise to the elements of the lists and returns a list of the results, in order. f is a function taking as many arguments as there are list arguments and returning a single value. Map is guaranteed to call f on the elements of the lists in order from left to right.

func sum(xs ...interface{}) interface{} {
  result := 0
  for x := range xs {
    result += x.(int)
  }
  return result
}

Map(sum, List(1, 2, 3), List(4, 5, 6)) => (5 7 9)

Map terminates when the shortest list runs out. At least one of the arguments must be finite:

Map(sum, List(3, 1, 4, 1), Circular(1, 0)) => (4 1 5 1)

func NAppend

func NAppend(lists ...*Pair) (result *Pair)

NAppend is the linear-update variant of Append -- it is allowed, but not required, to alter cons cells in the argument lists to construct the result list. The last list is never altered; the result list shares structure with this parameter.

func NAppendMap

func NAppendMap(f func(elements ...interface{}) *Pair, lists ...*Pair) (result *Pair)

NAppendMap maps f over the elements of the lists, just as in Map. However, the results of the applications are appended together to make the final result. NAppendMap uses NAppend to append the results together. At least one of the list arguments must be finite.

func NAppendTabulate

func NAppendTabulate(length int, init func(int) *Pair) (result *Pair)

NAppendTabulate applies init to each integer i, where 0 <= i < length, and uses NAppend to append together the results. No guarantee is made about the dynamic order in which init is applied to these integers.

func NFilterMap

func NFilterMap(f func(elements ...interface{}) (interface{}, bool), lists ...*Pair) (result *Pair)

NFilterMap is the linear-update variant of FilterMap.

func NMap

func NMap(f func(elements ...interface{}) interface{}, lists ...*Pair) (result *Pair)

NMap is the linear-update variant of Map. NMap is allowed, but not required, to alter the cons cells of the first list to construct the result. The remaining lists must have at least as many elements as the first list.

func NSetDifference

func NSetDifference(list *Pair, moreLists ...*Pair) *Pair

NSetDifference is the linear-update variant of SetDifference. It is allowed, but not required, to use the cons cells in its first list parameter to construct its answer.

func NSetDifferenceAndIntersection

func NSetDifferenceAndIntersection(list *Pair, moreLists ...*Pair) (difference, intersection *Pair)

NSetDifferenceAndIntersection is the linear-update variant of SetDifferenceAndIntersection. It is allowed, but not required, to use the cons cells in its first list parameter to construct its answer.

func NSetIntersection

func NSetIntersection(list *Pair, moreLists ...*Pair) *Pair

NSetIntersection is the linear-update variant of SetIntersection. It is allowed, but not required, to use the cons cells in its first list parameter to construct its answer.

func NSetUnion

func NSetUnion(lists ...*Pair) *Pair

NSetUnion is the linear-update variant of SetUnion.

func NSetXor

func NSetXor(lists ...*Pair) *Pair

NSetXor is the linear-update variant of SetXor. It is allowed, but not required, to use the cons cells in its first list parameter to construct its answer.

func NewList

func NewList(length int, element interface{}) (result *Pair)

NewList returns a list of the given length, whose elements are all the value element.

func NewPair

func NewPair(car, cdr interface{}) *Pair

NewPair returns &Pair{Car: car, Cdr: cdr}

func Nil

func Nil() *Pair

Nil returns (*Pair)(nil), but should be easier on the eyes.

func SetDifference

func SetDifference(list *Pair, moreLists ...*Pair) *Pair

SetDifference returns the difference of the lists, using == for comparing elements. That is, it returns all the elements of the first list that are not equal to any element from one of the other list parameters.

Elements that are repeated multiple times in the first list will occur multiple times in the result. The order in which elements appear in the result is the same as they appear in the first list -- that is, SetDifference essentially filters the first list, without disarranging element order. The result may share a common tail with the first list.

SetDifference(List("a", "b", "c", "d", "e"), List("a", "e", "i", "o", "u")) => ("b" "c" "d")

SetDifference(List("a", "b", "c")) => ("a" "b" "c")  // Trivial case

func SetDifferenceAndIntersection

func SetDifferenceAndIntersection(list *Pair, moreLists ...*Pair) (difference, intersection *Pair)

SetDifferenceAndIntersection returns two values -- the difference (as if by SetDifference) and the intersection (as if by SetIntersection) of the lists. It can be implemented more efficiently than calling SetDifference and SetIntersection separately.

Either of the answer lists may share a common tail with the first list. This operation essentially partitions the first list.

func SetIntersection

func SetIntersection(list *Pair, moreLists ...*Pair) *Pair

SetIntersection returns the intersection of the lists, using == to compare elements.

The intersection of lists A and B is comprised of every element of A that is equal to some element of B: a == b, for a in A, and b in B. Note this implies that an element which appears in B and multiple times in list A will also appear multiple times in the result.

The order in which elements appear in the result is the same as they appear in the first list -- that is, SetIntersection essentially filters the first list, without disarranging element order. The result may share a common tail with the first list.

In the n-ary case, the two-argument list-intersection operation is simply folded across the argument lists.

SetIntersection(List("a", "b", "c", "d", "e"), List("a", "e", "i", "o", "u")) => ("a" "e")

// Repeated elements in the first list are preserved.
SetIntersection(List("a", "x", "y", "a"), List("x", "a", "x", "z")) => ("a" "x" "a")

SetIntersection(List("a", "b", "c")) => ("a" "b" "c")  // Trivial case

func SetUnion

func SetUnion(lists ...*Pair) *Pair

SetUnion returns the union of the lists, using == to compare elements.

The union of lists A and B is constructed as follows:

* If A is the empty list, the answer is B (or a copy of B).

* Otherwise, the result is initialised to be list A (or a copy of A).

* Proceed through the elements of list B in a left-to-right order. If b is such an element of B, compare every element r of the current result list to b: r == b. If all comparisons fail, b is consed onto the front of the result.

In the n-ary case, the two-argument list-union operation is simply folded across the argument lists.

SeUnion(List("a", "b", "c", "d", "e"), List("a", "e", "i", "o", "u"))
 => ("u" "o" "i" "a" "b" "c" "d" "e")

// Repeated elements in the first list are preserved.
SetUnion(List("a", "a", "c"), List("x", "a", "x")) => ("x" "a" "a" "c")

// Trivial cases
SetUnion() => ()
SetUnion(List("a", "b", "c")) => ("a", "b", "c")

func SetXor

func SetXor(lists ...*Pair) *Pair

SetXor returns the exclusive-or of the sets, using == to compare elements. If there are exactly two lists, this is all the elements that appear in exactly one of the two lists. The operation is associative, and thus extends to the n-ary case -- the elements that appear in an odd number of the lists. The result may share a common tail with any of the list parameters.

More precisely, for two lists A and B, A xor B is a list of

* every element a of A such that there is no element b of B such that a == b, and

* every element b of B such that there is no elmente a of A such that b == a.

In the n-ary case, the binary-xor operation is simply folded across the lists.

SetXor(List("a", "b", "c", "d", "e"), List("a", "e", "i", "o", "u")) => ("d" "c" "b" "i" "o" "u")

// Trivial cases
SetXor() => ()
SetXor(List("a", "b", "c")) => ("a", "b", "c")

func Tabulate

func Tabulate(length int, init func(int) interface{}) (result *Pair)

Tabulate returns a list of the given length. Element i of the list, where 0 <= i < lenght, is produced by init(i). No guarantee is made about the dynamic order in which init is applied to these indices.

Tabulate(4, func(x interface{}) interface{} {return x.(int)+1}) => (1 2 3 4)

func Unzip

func Unzip(n int, lists ...*Pair) (result []*Pair)

Unzip takes several lists, where every list must contain at least n elements, and returns a slice of length n of lists. The first result list contains the first element of each list, the second result list contains the second element of each list, and so on.

Unzip(2, List(1, "one"), List(2, "two"), List(3, "three"))
 => [(1 2 3), ("one" "two" "three")]

func Zip

func Zip(lists ...*Pair) (result *Pair)

Zip returns a list as long as the shortest of the argument lists, each element of which is an n-element list comprised of the corresponding elements from the parameter lists, where n is the number of lists passed to Zip.

Zip(List("one", "two", "three"),
    List(1, 2, 3),
    List("odd", "even", "odd", "even", "odd", "even", "odd", "even"))
 => (("one" 1 "odd") ("two" 2 "even") ("three" 3 "odd"))

At least one of the argument lists must be finite:

Zip(List(3, 1, 4, 1), Circular(false, true))
 => ((3 false) (1 true) (4 false) (1 true))

func (*Pair) ACons

func (alist *Pair) ACons(key, value interface{}) *Pair

ACons conses a new alist entry mapping key -> value onto alist.

func (*Pair) ACopy

func (alist *Pair) ACopy() *Pair

ACopy makes a fresh copy of alist. This means copying each pair that forms an assocation as well as the spine of the list.

func (*Pair) ADelete

func (alist *Pair) ADelete(key interface{}) *Pair

ADelete deletes all assocations from alist with the given key, using ==.

The return value may share common tails with the alist argument. The alist is not disordered -- elements that appear in the result alist occur in the same order as they occur in the argument list.

func (*Pair) Adjoin

func (list *Pair) Adjoin(elements ...interface{}) *Pair

Adjoin adds the elements not already in the list parameter to the result list. The result shares a common tail with the list parameter. The new elements are added to the front of the list, but no guarantees are made about their order. Elements are compared using ==.

The list parameter is always a suffix of the result -- even if the list parameter contains repeated elements, these are not reduced.

Adjoin(List("a", "b", "c", "d", "c", "e"), "a", "e", "i", "o", "u")
 => ("u" "o" "i" "a" "b" "c" "d" "c" "e")

func (*Pair) Any

func (list *Pair) Any(predicate func(interface{}) bool) bool

Any applies the predicate across the list, returning true if the predicate returns true on any application.

func isInteger(x interface{}) bool {
  _, ok := x.(int)
  return ok
}

List("a", 3, "b", 2.7).Any(isInteger)   => true
List("a", 3.1, "b", 2.7).Any(isInteger) => false

func (*Pair) Append

func (list *Pair) Append(lists ...*Pair) (result *Pair)

Append returns a list consisting of the elements of the first list followed by the elements of the other lists.

List(1).Append(List(2))          => (1 2)
List(1).Append(List(2, 3, 4))    => (1 2 3 4)
List(1, List(2)).Append(List(3)) => (1 (2) 3)

The resulting list is always newly allocated, except that it shares structure with the final list.

func (*Pair) AppendMap

func (list *Pair) AppendMap(f func(element interface{}) *Pair) (result *Pair)

AppendMap maps f over the elements of the list, just as in Map. However, the results of the applications are appended together to make the final result. AppendMap uses Append to append the results together. The list argument must be finite.

func (*Pair) AppendReverse

func (list *Pair) AppendReverse(tail *Pair) (result *Pair)

AppendReverse returns Append(list.Reverse(), tail).

It is provided because it is a common operation -- a common list-processing style calls for this exact operation to transfer values accumulated in reverse order onto the front of another list, and because the implementation is significantly more efficient than the simple composition it replaces. (But note that this pattern of iterative computation followed by a reverse can frequently be rewritten as a recursion, disposing with the Reverse and AppendReverse steps, and shifting temporary, intermediate storage from the heap to the stack.)

func (*Pair) AppendToSlice

func (list *Pair) AppendToSlice(slice interface{}) (result interface{})

AppendToSlice uses Go's reflect package to append each element of the list to the given slice.

List(1, 2, 3).AppendToSlice([]int(nil)) => [1, 2, 3]

func (*Pair) Assoc

func (alist *Pair) Assoc(key interface{}) (result interface{}, ok bool)

Assoc finds the first pair in alist whose Car field is key, and returns that pair and true. If no pair in alist has key as its Car, then nil and false are returned. Assoc uses == for comparing key against the cars in alist.

func (*Pair) Break

func (list *Pair) Break(predicate func(interface{}) bool) (prefix *Pair, suffix interface{})

Break splits the list into the longest initial prefix whose elements all do not satisfy predicate, and the remaining tail.

func even(x interface{}) bool {
  return x.(int)%2 == 0
}

List(3, 1, 4, 1, 5, 9).Break(even) =>
  (3 1)
  (4 1 5 9)

func (*Pair) Copy

func (list *Pair) Copy() (result *Pair)

Copy copies the spine of the argument.

func (*Pair) Count

func (list *Pair) Count(predicate func(interface{}) bool) (result int)

Count applies predicate element-wise to the elements of list, and a count is tallied of the number of elements that produce a true value. This count is returned. Count is guaranteed to apply predicate to the list elements in a left-to-right order. The list must be finite.

func even(x interface{}) bool {
  return x.(int)%2 == 0
}

List(3, 1, 4, 1, 5, 9, 2, 5, 6).Count(even) => 3

func (*Pair) Delete

func (list *Pair) Delete(x interface{}) (result *Pair)

Delete finds all elements of list that are equal (==) to x, and deletes them from the list.

The list is not disordered -- elements that appear in the result list occur in the same order as they occur in the argument list. The result may share a common tail with the argument list.

Note that fully general element deletion can be performed with the Remove and NRemove methods, for example:

func even(x interface{}) bool {
  return x.(int)%2 == 0
}

// Delete all the even elements from list:
list.Remove(even)

func (*Pair) DeleteDuplicates

func (list *Pair) DeleteDuplicates() (result *Pair)

DeleteDuplicates removes duplicate elements from the list argument. If there are multiple equal (==) elements in the argument list, the result list contains the first or leftmost of these elements in the result. The order of these surviving elements is the same as in the original list -- DeleteDuplicates does not disorder the list (hence it is useful for "cleaning up" assocation lists).

The result of DeleteDuplicates may share common tails between argument and result lists -- for example, if the list argument contains only unique elements, it may simply return exactly this list.

Be aware that, in general, DeleteDuplicates runs in time O(n^2) for n-element lists. Uniquifying long lists can be accomplished in O(n lg n) time by sorting the list to bring equal elements together, then using a linear-time algorithm to remove equal elements. Alternatively, one can use algorithms based on element-marking, with linear-time results.

func (*Pair) Drop

func (list *Pair) Drop(k int) (result interface{})

Drop returns all but the first k elements of the list.

List(1, 2, 3, 4, 5).Drop(2) => (3 4 5)

x may be any value -- a proper, circular, or dotted list.

Cons(1, 2, 3, "d").Drop(2) => (3 . "d")
Cons(1, 2, 3, "d").Drop(3) => "d"

For a legal k, Take and Drop partition the list in a manner which can be inverted with Append:

Append(x.Take(k), x.Drop(k)) => x

Drop is exactly equivalent to performing k Cdr operations on x; the returned value shares a common tail with x.

func (*Pair) DropRight

func (list *Pair) DropRight(k int) (result *Pair)

DropRight returns all but the last k elements of list.

List(1, 2, 3, 4, 5).DropRight(2) => (1 2 3)

list may be any finite list, either proper or dotted.

Cons(1, 2, 3, "d").DropRight(2) => (1)
Cons(1, 2, 3, "d").DropRight(0) => (1 2 3)

For a legal k, TakeRight and DropRight partition the list in a manner which can be inverted with Append:

Append(x.DropRight(k), x.TakeRight(k)) => x

If the argument list is a list of non-zero length, DropRight is guaranteed to return a freshly-allocated list, even in the case where nothing is dropped.

func (*Pair) DropWhile

func (list *Pair) DropWhile(predicate func(interface{}) bool) (result *Pair)

DropWhile drops the longest initial prefix of list whose elements all satisfy predicate, and returns the rest of the list.

func even(x interface{}) bool {
  return x.(int)%2 == 0
}

List(2, 18, 3, 10, 10, 22, 9).DropWhile(even) => (3 10 22 9)

The circular-list case may be viewed as "rotating" the list.

func (*Pair) Every

func (list *Pair) Every(predicate func(interface{}) bool) bool

Every applies the predicate across the list, returning true if the predicate returns true on every application.

func (*Pair) Filter

func (list *Pair) Filter(predicate func(x interface{}) bool) (result *Pair)

Filter returns all the elements of list that satisfy the predicate. The list is not disordered -- elements that appear in the result list occur in the same order as they occur in the argument list. The returned list may share a common tail with the argument list. The dynamic order in which the various applications of predicate are made is not specified.

func even(x interface{}) bool {
  return x.(int)%2 == 0
}

list.List(0, 7, 8, 8, 43, -4).Filter(even) => (0 8 8 -4)

func (*Pair) FilterMap

func (list *Pair) FilterMap(f func(element interface{}) (interface{}, bool)) (result *Pair)

FilterMap is like Map, but only when f returns true as a second value, the first value is saved.

List("a", 1, "b", 3, "c", 7).FilterMap(func(x interface{}) (interface{}, bool) {
  if n, ok := x.(int); ok {
    return n*n, true
  }
  return nil, false
})                    => (1 9 49)

The list argument must be finite.

func (*Pair) Find

func (list *Pair) Find(predicate func(interface{}) bool) (result interface{}, ok bool)

Find returns the first element of list that satisfies predicate. It returns a second value of true if such an element is found, and false otherwise.

func even(x interface{}) bool {
  return x.(int)%2 == 0
}

List(3, 1, 4, 1, 5, 9).Find(even) => 4, true

func (*Pair) FindTail

func (list *Pair) FindTail(predicate func(interface{}) bool) (result *Pair)

FindTail returns the first pair whose Car satisfies predicate. If no pair does, return Nil().

FindTail can be viewed as a general-predicate variant of the Member function.

Examples:

func even(x interface{}) bool {
  return x.(int)%2 == 0
}

List(3, 1, 37, -8, -5, 0, 0).FindTail(even) => (-8 -5 0 0)
List(3, 1, 37, -5).FindTail(even) => ()

In the circular-list case, this function "rotates" the list.

FindTail is essentially DropWhile, where the sense of the predicate is inverted: FindTail searches until it finds an element satisfying the predicate; DropWhile searches until it finds an element that doesn't satisfy the predicate.

func (*Pair) Fold

func (list *Pair) Fold(f func(intermediate, element interface{}) interface{}, init interface{}) (result interface{})

Fold is the fundamental list iterator.

If list == (e_1 e_2 ... e_n), then this method returns

f(f(f(f(init, e1), e2), ...), e_n)

That is, it obeys the recursion

list.Fold(f, init)  == list.Cdr.Fold(f, f(init, list.Car))
Nil().Fold(f, init) == init

The list argument must be finite.

Examples:

func add(x, y interface{}) interface{} {
  return x.(int) + y.(int)
}

list.Fold(add, 0)          // Add up the elements of list.

func cons(tail, head interface{}) interface{} {
  return NewPair(head, tail)
}

list.Fold(cons, Nil())     // Reverse list.

// How many strings in list?
list.Fold(func(count, x interface{}) interface{} {
  if _, ok := x.(string); ok {
    return count.(int) + 1
  }
  return count.(int)
})

// Length of the longest string in list:
list.Fold(func(maxLen, x interface{}) interface{} {
  if s, ok := x.(string); ok && len(s) > maxLen.(int) {
    return len(s)
  }
  return maxLen
})

func (*Pair) FoldRight

func (list *Pair) FoldRight(f func(intermediate, element interface{}) interface{}, init interface{}) (result interface{})

FoldRight is the fundamental list recursion operator.

If list == (e_1 e_2 ... e_n), then this method returns

f(f(f(f(init, e_n), ...), e_2), e_1)

That is, it obeys the recursion

list.FoldRight(f, init)  == f(list.Cdr.FoldRight(f, init), list.Car)
Nil().FoldRight(f, init) == init

The list argument must be finite.

Examples:

func cons(tail, head interface{}) interface{} {
  return NewPair(head, tail)
}

list.FoldRight(cons, Nil())     // Copy list.

// Filter the even numbers out of list.
list.FoldRight(func(l, x interface{}) interface{} {
  if x.(int)%2 == 0 {
    return NewPair(x, l)
  }
  return l
}, Nil())

func (*Pair) ForEach

func (list *Pair) ForEach(f func(element interface{}))

ForEach is like Map, but ForEach calls f for its side effects rather than for its values. ForEach is guaranteed to call f on the elements of the list in order from left to right. The list argument must be finite.

v := make([]int, 5)
List(0, 1, 2, 3, 4).ForEach(func (e interface{}) {
  i := e.(int)
  v[i] = i
})
v => [0, 1, 2, 3, 4]

func (*Pair) Index

func (list *Pair) Index(predicate func(interface{}) bool) (result int)

Index returns the index of the leftmost element that satisfies predicate.

func even(x interface{}) bool {
  return x.(int)%2 == 0
}

List(3, 1, 4, 1, 5, 9).Index(even) => 2

func (*Pair) Last

func (list *Pair) Last() (result interface{})

Last returns the last element of the finite list. If list is nil or dotted, Last returns Nil().

func (*Pair) LastPair

func (list *Pair) LastPair() (result *Pair)

LastPair returns the last pair in the finite list. If list is nil or dotted, LastPair returns Nil().

func (*Pair) Length

func (list *Pair) Length() (result int)

Length returns the length of the argument. It is an error to pass a value which is not a proper list (finite and Nil()-terminated). In particular, this means Length may diverge or panic when Length is applied to a circular list.

The length of a proper list is a non-negative integer n such that Cdr applied n times to the list produces the empty list.

func (*Pair) Map

func (list *Pair) Map(f func(element interface{}) interface{}) (result *Pair)

Map applies f element-wise to the elements of list and returns a list of the results, in order. Map is guaranteed to call f on the elements of the list in order from left to right. The list argument must be finite.

List(List(1, 2), List(3, 4), List(5, 6)).Map(Cadr) => (2 4 6)

List(1, 2, 3, 4, 5).Map(func (n interface{}) interface{} {return x.(int)+1}) => (2 3 4 5 6)

count := 0
List("a", "b").Map(func(_ interface{}) interface{} {
   count++
   return count
})                  => (1 2)

func (*Pair) Member

func (list *Pair) Member(x interface{}) (result *Pair)

Member returns the first sublist of list whose Car is x, where the sublists of list ar the non-empty lists returned by list.Drop(i) for i less than the length of list. If x does not occur in list, then nil is returned. Member uses == to compare x with the elements of the list.

List(1, 2, 3).Member(1) => (1 2 3)
List(1, 2, 3).Member(2) => (2 3)
List(2, 3, 4).Member(1) => ()

Note that fully general list searching may be performed with the Find and FindTail functions.

func (*Pair) NADelete

func (alist *Pair) NADelete(key interface{}) *Pair

NADelete is the linear-update variant of ADelete.

func (*Pair) NAppend

func (list *Pair) NAppend(lists ...*Pair) (result *Pair)

NAppend is the linear-update variant of Append -- it is allowed, but not required, to alter cons cells in the argument lists to construct the result list. The last list is never altered; the result list shares structure with this parameter.

func (*Pair) NAppendMap

func (list *Pair) NAppendMap(f func(element interface{}) *Pair) (result *Pair)

NAppendMap maps f over the elements of the list, just as in Map. However, the results of the applications are appended together to make the final result. NAppendMap uses NAppend to append the results together. The list argument must be finite.

Example:

List(1, 3, 8).NAppendMap(func(x interface{}) *Pair {
  return List(x, -(x.(int)))
})                              => (1 -1 3 -3 8 -8)

func (*Pair) NAppendReverse

func (list *Pair) NAppendReverse(tail *Pair) (result *Pair)

NAppendReverse is the linear-update variant of AppendReverse.

func (*Pair) NBreak

func (list *Pair) NBreak(predicate func(interface{}) bool) (prefix *Pair, suffix interface{})

NBreak is the linear-update variant of Break.

func (*Pair) NDelete

func (list *Pair) NDelete(x interface{}) (result *Pair)

NDelete is the linear-update variant of Delete.

func (*Pair) NDeleteDuplicates

func (list *Pair) NDeleteDuplicates() (result *Pair)

NDeleteDuplicates is the linear-update variant of DeleteDuplicates.

func (*Pair) NDropRight

func (list *Pair) NDropRight(k int) (result *Pair)

NDropRight is the linear-update variant of DropRight.

func (*Pair) NFilter

func (list *Pair) NFilter(predicate func(x interface{}) bool) (result *Pair)

NFilter is the linear-update variant of Filter.

func (*Pair) NFilterMap

func (list *Pair) NFilterMap(f func(element interface{}) (interface{}, bool)) (result *Pair)

NFilterMap is the linear-update variant of FilterMap.

func (*Pair) NMap

func (list *Pair) NMap(f func(element interface{}) interface{}) (result *Pair)

NMap is the linear-update variant of Map.

func (*Pair) NPartition

func (list *Pair) NPartition(predicate func(x interface{}) bool) (in, out *Pair)

NPartition is the linear-update variant of Partition.

func (*Pair) NRemove

func (list *Pair) NRemove(predicate func(x interface{}) bool) (result *Pair)

NRemove is the linear-update variant of Remove.

func (*Pair) NReverse

func (list *Pair) NReverse() (result *Pair)

NReverse is the linear-update variant of Reverse. The list must be a proper list.

func (*Pair) NReverseLast

func (list *Pair) NReverseLast() (result, last *Pair)

NReverseLast is the linear-update variant of ReverseLast.

func (*Pair) NSpan

func (list *Pair) NSpan(predicate func(interface{}) bool) (prefix *Pair, suffix interface{})

NSpan is the linear-update variant of Span.

func (*Pair) NSplitAt

func (list *Pair) NSplitAt(k int) (prefix *Pair, suffix interface{})

NSplitAt is the linear-update variant of SplitAt.

func (*Pair) NTake

func (list *Pair) NTake(k int) (result *Pair)

NTake is the linear-update variant of Take.

If x is circular, NTake may return a shorter-than-expected list.

func (*Pair) NTakeWhile

func (list *Pair) NTakeWhile(predicate func(interface{}) bool) (result *Pair)

NTakeWhile is the linear-update variant of TakeWhile.

func (*Pair) NonCircularLength

func (list *Pair) NonCircularLength() (result int, nonCircular bool)

NonCircularLength returns the length of the argument and true if list is a proper list. If list is circular, though, NonCircularLength returns -1 and false.

The length of a proper list is a non-negative integer n such that Cdr applied n times to the list produces the empty list.

func (*Pair) PairFold

func (list *Pair) PairFold(f func(intermediate interface{}, sublist *Pair) interface{}, init interface{}) (result interface{})

PairFold is analogous to Fold, but f is applied to successive sublists of the list, rather than successive elements -- that is, f is applied to the pairs making up the list, giving this recursion:

list.PairFold(f, init)  == { tail := list; list.Cdr.PairFold(f, f(init, tail)) }
Nil().PairFold(f, init) == init

The list argument must be finite. The f function may reliably assign to the Cdr of each pair it is given without altering the sequence of execution.

Example:

// Destructively reverse a list.
list.PairFold(func(tail interface{}, pair *Pair) interface{} {
  pair.Cdr = tail
  return pair
}, Nil())

func (*Pair) PairFoldRight

func (list *Pair) PairFoldRight(f func(intermediate interface{}, sublist *Pair) interface{}, init interface{}) (result interface{})

PairFoldRight holds the same relationship with FoldRight that PairFold holds with Fold.

PairFoldRight obeys the recursion:

list.PairFoldRight(f, init)  == f(list.Cdr.PairFoldRight(f, init), list)
Nil().PairFoldRight(f, init) == init

Example:

func cons(tail interface{}, pair *Pair) interface{} {
  return NewPair(pair, tail)
}

List(1, 2, 3).PairFoldRight(cons, Nil()) => ((1 2 3) (2 3) (3))

The list argument must be finite.

func (*Pair) PairForEach

func (list *Pair) PairForEach(f func(sublist *Pair))

PairForEach is like ForEach, but f is applied to successive sublists of the argument list. That is, f is applied to the cons cells of the list, rather than the list's elements. These applications occur in left-to-right order.

The f function may reliably assign to the Cdr of the pairs it is given without altering the sequence of execution.

List(1, 2, 3).PairForEach(func (x interface{}) { fmt.Println(x) }) =>
   (1 2 3)
   (2 3)
   (3)

The list argument must be finite.

func (*Pair) Partition

func (list *Pair) Partition(predicate func(x interface{}) bool) (in, out *Pair)

Partition partitions the elements of list with predicate pred, and returns two values: the list of in-elements and the list of out-elements. The lists are not disordered -- elements occur in the result lists in the same order as they occur in the argument list. The dynamic order in which the various applications of predicate are made is not specified. One of the returned lists may share a common tail with the argument list.

func isString(x interface{}) bool {
  _, ok := x.(string)
  return ok
}

list.List("one", 2, 3, "four", "five", 6).Partition(isString) =>
   ("one" "four" "five")
   (2 3 6)

func (*Pair) Reduce

func (list *Pair) Reduce(f func(intermediate, element interface{}) interface{}, init interface{}) (result interface{})

Reduce is a variant of Fold.

init should be a "right identity" of the function f -- that is, for any value x acceptable to f, f(init, x) == x.

Reduce has the following definition:

If list == Nil(), return init;
Otherwise, return list.Cdr.Fold(f, list.Car)

...in other words, we compute list.Fold(f, init)

Note that init is used only in the empy-list case. You typically use Reduce when applying f is expensive and you'd like to avoid the extra application incurred when Fold applies f to the head of list and the identity value, redundantly producing the same value passed in to f. For example, if f involves searching a file directory or performing a database query, this can be significant. In general, however, Fold is useful in many contexts where Reduce is not (consider the examples given in the Fold documentation -- only one of them uses a function with a right identity. The others may not be performed with Reduce).

func max(x, y interface{}) interface{} {
  if x.(int) > y.(int) {
    return x
  }
  return y
}

// Take the max of a list of non-negative integers.
nums.Reduce(max, 0)

func (*Pair) ReduceRight

func (list *Pair) ReduceRight(f func(intermediate, element interface{}) interface{}, init interface{}) (result interface{})

ReduceRight is the fold-right variant of Reduce.

It obeys the following definition:

Nil().ReduceRight(f, init) == init
List(e_1).ReduceRight(f, init) == f(init, e_1) == e_1
List(e_1, e_2, ...).ReduceRight(f, init) == f(List(e_2, ...).Reduce(f, init), e_1)

...in other words, we compute list.FoldRight(f, init)

func append(intermediate, element interface{}) interface{} {
  return Append(intermediate.(*Pair), element.(*Pair))
}

listOfLists.ReduceRight(append, Nil())

func (*Pair) Ref

func (list *Pair) Ref(n int) (result interface{})

Ref returns the nth element of list.

(This is the same as the Car of list.Drop(n).) Ref panics if n >= l, where l is the length of list.

func (*Pair) Remove

func (list *Pair) Remove(predicate func(x interface{}) bool) (result *Pair)

Remove returns list without the elements that satisfy predicate:

func (list *Pair) Remove(predicate func(x interface{}) bool) *Pair {
  return list.Filter(func (x interface{}) bool {return !predicate(x)})
}

The list is not disordered -- elements that appear in the result list occur in the same order as they occur in the argument list. The returned list may share a common tail with the argument list. The dynamic order in which the various applications of predicate are made is not specified.

func even(x interface{}) bool {
  return x.(int)%2 == 0
}

list.List(0, 7, 8, 8, 43, -4).Remove(even) => (7 43)

func (*Pair) Reverse

func (list *Pair) Reverse() (result *Pair)

Reverse returns a newly allocated list consisting of the elements of list in reverse order. The list must be a proper list.

func (*Pair) ReverseLast

func (list *Pair) ReverseLast() (result, last *Pair)

ReverseLast is like Reverse, except that both the resulting list as well as the last pair of the resulting list is returned. This enables setting the Cdr of the last pair to a value other than Nil().

func (*Pair) Span

func (list *Pair) Span(predicate func(interface{}) bool) (prefix *Pair, suffix interface{})

Span splits the list into the longest initial prefix whose elements all satisfy predicate, and the remaining tail.

func even(x interface{}) bool {
  return x.(int)%2 == 0
}

List(2, 18, 3, 10, 22, 9).Span(even) =>
  (2 18)
  (3 10 22 9)

func (*Pair) SplitAt

func (list *Pair) SplitAt(k int) (prefix *Pair, suffix interface{})

SplitAt splits the list at index k, returning a list of the first k elements, and the remaining tail.

func (*Pair) String

func (list *Pair) String() string

func (*Pair) Take

func (list *Pair) Take(k int) (result *Pair)

Take returns the first k elements of the list.

List(1, 2, 3, 4, 5).Take(2) => (1 2)

x may be any value -- a proper, circular, or dotted list.

Cons(1, 2, 3, "d").Take(2) => (1 2)
Cons(1, 2, 3, "d").Take(3) => (1 2 3)

For a legal k, Take and Drop partition the list in a manner which can be inverted with Append:

Append(x.Take(k), x.Drop(k)) => x

If the argument list is a list of non-zero length, Take is guaranteed to return a freshly-allocated list, even in the case where the entire list is taken.

func (*Pair) TakeRight

func (list *Pair) TakeRight(k int) (result interface{})

TakeRight returns the last k elements of list.

List(1, 2, 3, 4, 5).TakeRight(2) => (4 5)

list may be any finite list, either proper or dotted.

Cons(1, 2, 3, "d").TakeRight(2) => (2 3 . "d")
Cons(1, 2, 3, "d").TakeRight(0) => "d"

For a legal k, TakeRight and DropRight partition the list in a manner which can be inverted with Append:

Append(x.DropRight(k), x.TakeRight(k)) => x

TakeRight's return value is guaranteed to share a common tail with list.

func (*Pair) TakeWhile

func (list *Pair) TakeWhile(predicate func(interface{}) bool) (result *Pair)

TakeWhile returns the longest initial prefix of list whose elements all satisfy predicate.

func even(x interface{}) bool {
  return x.(int)%2 == 0
}

List(2, 18, 3, 10, 22, 9).TakeWhile(even) => (2 18)

func (*Pair) ToSlice

func (list *Pair) ToSlice() (result []interface{})

ToSlice uses Go's reflect package to convert the list to a slice.

If you need a slice of a particular type, use AppendToSlice to append to a nil value of that slice type instead.

func (*Pair) Unzip

func (list *Pair) Unzip(n int) (result []*Pair)

Unzip takes a list, which must contain at least n elements, and returns a slice of length n of lists. The first result list contains the first element of the list, the second result list contains the second element the list, and so on.

List(1, "one").Unzip(2) => [(1), ("one")]

func (*Pair) Zip

func (list *Pair) Zip() (result *Pair)

Zip returns a list of the same length, each element of which is a one-element list comprised of the corresponding elements from list. The list must be finite.

List(1, 2, 3).Zip() => ((1) (2) (3))

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL