lazychains package

Module contents

class lazychains.Chain(head, tail)

A chain of items is made up of a singly linked list of Chain records. Each Chain record either holds a single item and a pointer to the subsequent chain of items or will be an end-of-chain marker or holds an iterator that will be run on demand.

Usually they are less compact than arrays or tuples, which is why they are less popular. However their ability to share “tails” can sometimes help us avoid unnecessary copying, saving time and store. Also, they can easily represent a large or even infinite sequence that is expanded on demand. Here’s a typical idioms showing how to efficiently represent all the lines from a file:

lines = lazychain( open( 'myfile.txt', 'r' ) )
while lines:
    #
    # Process one or more lines (not shown)
    ...
    lines = lines.tail()

Note how this idiom ‘walks’ the chain, overwriting the lines variable so as to allow the Chain records to be swiftly reclaimed by the store manager.

dest() Tuple[T, Chain[T]]

Returns both the head and tail in one operation. This performs the expansion check exactly once, so is potentially slightly more efficent - but more importantly it often makes the code easier to read.

expand()

Force the expansion of the chain, returning the chain.

expanded_len()

Returns the number of expanded nodes in the chain. It is not expected that this would be used in typical programming but it is handy for debugging.

filter(predicate)

This works exactly like the built in function filter except that it returns a Chain rather than an iterable. If you only need an iterable returned just do this: filter( f, chain )

head() T

Returns the first item in the chain.

is_expanded() bool

Returns True if the Chain node is expanded and either the member is already cached or the node represents the empty list. Returns False if the Chain node will need to run an iterator to determine its contents. This should hardly ever be used in application programs. However it can be useful when trying to debug.

lazycall(f, *args)

This is useful when writing recursive lazy functions. It returns a Chain node that represents a deferred call of f( self, *args ). The call must return a Chain. N.B. It may return another lazy-chain but, if so, it will recursively be forced.

len_is_at_least(n: int) bool

Returns True if the chain is at least n items in length. This avoids expanding the whole chain. Otherwise returns False.

len_is_at_most(n: int) bool

Returns True if the chain has length at most n. This avoids expanding the whole chain. Otherwise returns False.

len_is_less_than(n: int) bool

Returns True if the length of the chain is less than n. This avoids expanding the whole chain. Otherwise returns False.

len_is_more_than(n: int) bool

Returns True if the chain is more than n in length. This avoids expanding the whole chain. Otherwise returns False.

map(f, *iterables)

This works exactly like the built in function map except that it returns a Chain rather than an iterable. If you only need an iterable returned just do this: map( f, chain, *iterables )

new(x: T) Chain[T]

Allocates a new chain node with x as its head and the current chain as its tail.

tail() Chain[T]

Returns the chain that represents all but the first item. Chains form singly linked lists, so this is a fast operation that always yields the same result when applied to the same chain.

zip(*iterables, strict=False)

This works exactly like the built in function zip except that it returns a Chain of tuples rather than an iterable of tuples. If you only need an iterable returned just do this: zip( chain, *iterables, strict )

lazychains.chain(it: Iterable[T] = ()) Chain[T]

Returns a fully expanded chain based on the iterable/iterator. This is useful when you need the chain to be independent of changes in the underlying iterable.

lazychains.lazycall(f, *args) Chain[T]

This method implements a lazy call of a function f that returns a Chain. This is useful when processing potentially infinite lists. Strictly speaking it takes a function f that, when applied to *args, returns a Chain. When that Chain is expanded, the function f is automatically called and the resulting Chain overwrites it. If necessary the Chain is repeatedly expanded.

lazychains.lazychain(it: Iterable[T] = ()) Chain[T]

Returns an unexpanded chain based on the iterable/iterator. Using this constructor allows you to work with very large or even infinite chains. Note that because this is lazy, subsequent changes to the underlying iterable maybe incorporated into the chain.