Table of Contents

Introduction

As I read Steele's Common Lisp: The Language I had a strong sense of the way in which Common Lisp was developed in the context of Lisp machines. It makes for an interesting way of thinking about computing languages: as a way of using the computer rather than as a means for writing programs. The parallel I've been drawing is with Emacs Lisp primarily as a way of editing text (rather than writing programs).

There is another parallel to be drawn with command shells as ways of using a computer. It seems weaker. Compared with Common Lisp's explicit design process, Bash, MSDOS, and other shells are more ad hoc aggregations of utilities rather than formal designs: grep & find & cat & ls & cd don't share a strong coherance. Knowing the options of one does not create strong intuitions into the others.

As I dug deeper into the Common Lisp: The Language the consistent construction of various sequence and list functions, it became apparent that this was a byproduct of the Lisp Machine context. Consistent user interface design is orthogonal to the form of the user interface. Command line, GUI, or programming language all have the same requirements.

The idea of data structures such as list or sequence living in user space feels radical today. But it seems simply a byproduct of decades of WIMP dominance – years of familiarity and ad hockery means Bash pipes or Perl regex's feel normal parts of user space.

Basic Semantics

Destructive versus Non-Destructive

Matching value versus applied predicate

List functions ending

Definition

<<list-append-import>>
<<operators-import>>

# Helpers

<<identity>>

<<operators>>

# List Basics

<<cons>>

<<first>>

<<rest>>

<<list-append>>

# Non Destructive List functions

<<copy-list>>

<<remove>>

Helpers

Operators

Python does not allow passing operators such as < or + as functions. Instead it provides the operators module which is included here to allow tests to be passed to list functions.

import operator
less_than = operator.lt
lt = operator.lt
less_than_or_equal = operator.le
le = operator.le
equal = operator.eq
eq = operator.eq
non_equal = operator.ne
ne = operator.ne
greater_than_or_equal = operator.ge
ge = operator.ge

Identity

Python does not have an identity function. Including one is not considered Pythonic and writing one's own is considered the Python way.

def identity(x):
    return x

List Basics

cons

Where to start but with cons. It's not that Python doesn't have the ability to add to lists, it's just that trying to translate from front to rear addition is probably not the best place to start.

def cons (element, a_list):
    """
    Adds an elment to the *front* of a_list.
    If a_list is not a list, cons creates a list of one element holding a_list.
    """
    if not type(a_list) == list:
        a_list = [a_list]
    a = [element]
    a.extend(a_list)
    return a

first

def first(a_list):
    """
    Returns the first elment of a list.
    Returns False if the list is empty.
    """
    if len(a_list)==0:
        return False
    else:
        return a_list[0]

rest

def rest(a_list):
    """
    Returns a list minus its first elment.
    Returns false if list is empty.
    Returns the empty list if list has one element.
    """
    if len(a_list)==0:
        return False
    elif len(a_list)==1:
        return []
    else:
        return a_list[1:]

list_append

This is a rewrite of an earlier version that was written for gps_py that only took two lists. Python already has an append function.

def list_append(list_1, list_2):
    return list_1 + list_2
from functools import reduce as reduce
def list_append(*args):
    return reduce(list.__add__, args)

Non-Destructive

copy_list

To me, copy-list is where the idea of Common Lisp as a language for users really hit home. There were many idioms for copying a list in other lisp dialects. By abstracting over those, copy-list provides an explicit mechanism for users to express an intent to bypass destructive semantics.

def copy_list(ls):
    return [i for i in ls]

local_iterate

This is a function designed to work as a local closure within other functions. It replaces list comprehensions in order to implement the _count property.

def iterate():
    c = 0
    ret = []
    for i in seq:
        if c < _count:
            if test(key(i),item):
                c += 1
            else:
                ret += [i]
        else:
            ret += [i]
    return ret

The version when test_not is True.

def iterate_not():
    c = 0
    ret = []
    for i in seq:
        if c < _count:
            if not test(key(i),item):
                c += 1
            else:
                ret += [i]
        else:
            ret += [i]
    return ret

remove

def remove(item, sequence, from_end=False, test=equal, test_not=False, start=None, end=None, count=False, key=identity):

    # chop the front off the sequence if only
    # using part of it
    if start:
        front = sequence[:start]
    else:
        front = []
    # chop the rear off the sequence if only
    # using part of it
    if end:
        rear = sequence[rear:]
    else:
        rear = []

    # make a copy of the sequence for local mutation
    # and cut to length
    seq = copy_list(sequence)[start:end]

    # reverse the target sequence if requested
    if from_end:
        seq = seq.reverse()

    # If there's no count, let it be seq length
    if not count:
        _count = len(seq)
    else:
        _count = count

    # Add the iterate closure
    <<local-iterate>>

    # Add iterate_not closure
    <<local-terate-not>>

    # Main logic
    if not test_not:
        middle = iterate()
        # return values in the same order
        if from_end:
            middle.reverse()
        return list_append(front,middle,rear)
    else:
        middle = iterate_not()
        # return values in the same order        
        if from_end:
            middle.reverse()
        return list_append(front,middle,rear)

remove_if

def remove_if(pred, seq):
    return [i for i in pred if not pred(i)]

Author: benrudgers

Created: 2017-03-16 Thu 17:19

Validate