Click to change color scheme

Notes on codes, projects and everything

The Little Pythoner, Part 2

In the last part, I implemented a couple of primitive functions so that they can be applied in the following chapters. The second chapter of the book, is titled “Do it again, and again, and again…”. The title already hints that readers will deal with repetitions throughout the chapter.

First of everything is the definition of lat. This is defined as a list of atoms. So practically it is a subset of a normal list (which can be constructed with any s-expressions). The first actual function to build, is to check if each of the elements are atoms. Most of the primitives are now stored in primitives.py, hence the import statement. Also there’s no input type check anymore, and it is assumed that all the input are of the correct type unless otherwise specified.

from primitives import *

def test_islat():
    test_cases = ((('Jack', 'Sprat', 'could', 'eat', 'no', 'chicken', 'fat'), True),
                  ((('Jack', ), 'Sprat', 'could', 'eat', 'no', 'chicken', 'fat'), False),
                  (('Jack', ('Sprat', 'could'), 'eat', 'no', 'chicken', 'fat'), False),
                  ((), True),
                  (((('tomato', 'sauce'),), (('bean', ), 'sauce'), ('and', (('flying', ), ), 'sauce')),
                   False))

    for l, expected in test_cases:
        result = islat(l)
        assert result == expected
        
        
def islat(l):
    return (True if isnull(l)
            else islat(cdr(l)) if isatom(car(l))
            else False)

Not sure the reason behind the weird order of ternary operator in Python, however it works just fine.

Next we are given another problem, which is to write a function to tell if an atom is a member of a given list.

def test_ismember():
    test_cases = (('tea', ('coffee', 'tea', 'or', 'milk'), True),
                  ('poached', ('fried', 'eggs', 'and', 'scrambled', 'eggs'), False),
                  ('meat', ('mashed', 'potatoes', 'and', 'meat', 'gravy'), True),
                  ('liver', ('bagels', 'and', 'lox'), False))

    for atom, lat, expected in test_cases:
        result = ismember(atom, lat)
        assert result == expected
        

def ismember(a, lat):
    return (False if isnull(lat)
            else or_(iseq(car(lat), a),
                     ismember(a, cdr(lat))))

So there’s a common pattern that can be observed here. An exit condition is always defined first, which makes sense because otherwise the function will be looping infinitely. In these cases, the exit point is whenever the list of atom is fully consumed. Both of the function also cares about the first atom in the list, and defer the rest to a recursive copy of itself to figure out.

Then we reach the end of second chapter (The book itself spent most of the pages tracing examples to each line of the code).

leave your comment

name is required

email is required

have a blog?

This blog uses scripts to assist and automate comment moderation, and the author of this blog post does not hold responsibility in the content of posted comments. Please note that activities such as flaming, ungrounded accusations as well as spamming will not be entertained.