Alex Vong 92f580152f Add a minimal scm module to provide Scheme-like data types and procedures.
primitives.py implements a minimal subset of Scheme primitives.
srfi_1.py implements a minimal subset of SRFI-1: List Library.

* devscripts/scm/__init__.py: New file.
* devscripts/scm/primitives.py: New file.
* devscripts/scm/srfi_1.py: New file.
2015-09-13 18:14:05 +08:00

356 lines
9.5 KiB
Python

from __future__ import unicode_literals
from .primitives import *
"""
Module implemeting a minimal subset of SRFI-1: List Library
In Scheme:
append caar cadr cdar cddr caaar caadr cadar caddr cdaar cdadr cddar cdddr
caaaar caaadr caadar caaddr cadaar cadadr caddar cadddr cdaaar cdaadr
cdadar cdaddr cddaar cddadr cdddar cddddr concatenate drop-while every
filter first fold iota last length list list_ref lset-difference map reduce
reverse take-while
In Python:
append caar cadr cdar cddr caaar caadr cadar caddr cdaar cdadr cddar cdddr
caaaar caaadr caadar caaddr cadaar cadadr caddar cadddr cdaaar cdaadr
cdadar cdaddr cddaar cddadr cdddar cddddr concatenate drop_while every
filter first fold iota last length list list_ref lset_difference map reduce
reverse take_while
"""
# Use these procedures with caution,
# as too much car/cdr-ing may hinder readability
def caar(obj): return car(car(obj))
def cadr(obj): return car(cdr(obj))
def cdar(obj): return cdr(car(obj))
def cddr(obj): return cdr(cdr(obj))
def caaar(obj): return car(car(car(obj)))
def caadr(obj): return car(car(cdr(obj)))
def cadar(obj): return car(cdr(car(obj)))
def caddr(obj): return car(cdr(cdr(obj)))
def cdaar(obj): return cdr(car(car(obj)))
def cdadr(obj): return cdr(car(cdr(obj)))
def cddar(obj): return cdr(cdr(car(obj)))
def cdddr(obj): return cdr(cdr(cdr(obj)))
def caaaar(obj): return car(car(car(car(obj))))
def caaadr(obj): return car(car(car(cdr(obj))))
def caadar(obj): return car(car(cdr(car(obj))))
def caaddr(obj): return car(car(cdr(cdr(obj))))
def cadaar(obj): return car(cdr(car(car(obj))))
def cadadr(obj): return car(cdr(car(cdr(obj))))
def caddar(obj): return car(cdr(cdr(car(obj))))
def cadddr(obj): return car(cdr(cdr(cdr(obj))))
def cdaaar(obj): return cdr(car(car(car(obj))))
def cdaadr(obj): return cdr(car(car(cdr(obj))))
def cdadar(obj): return cdr(car(cdr(car(obj))))
def cdaddr(obj): return cdr(car(cdr(cdr(obj))))
def cddaar(obj): return cdr(cdr(car(car(obj))))
def cddadr(obj): return cdr(cdr(car(cdr(obj))))
def cdddar(obj): return cdr(cdr(cdr(car(obj))))
def cddddr(obj): return cdr(cdr(cdr(cdr(obj))))
def length(lst):
"""compute length of lst"""
def length_loop(lst, count):
if lst is nil:
return count
else:
return length_loop(cdr(lst),
count + 1)
return length_loop(lst, 0)
def list_ref(lst, k):
"""return the k^th element of lst"""
if k == 0:
return car(lst)
else:
return list_ref(cdr(lst), k - 1)
def iota(count):
"""return lst from 0 to (count - 1)"""
def iota_loop(loop_count):
if loop_count == count:
return nil
else:
return cons(loop_count,
iota_loop(loop_count + 1))
return iota_loop(0)
def _any(proc, arg_lst):
"""
any for procedures that take a single argument
Apply proc to every element in arg_lst
Return True is any of the result is True
Otherwise, return False
"""
if arg_lst is nil:
return False
elif proc(car(arg_lst)):
return True
else:
return _any(proc, cdr(arg_lst))
def _every(proc, arg_lst):
"""
every for procedures that take a single argument
Apply proc to every element in arg_lst
Return True is every result is True
Otherwise, return False
"""
if arg_lst is nil:
return True
elif not proc(car(arg_lst)):
return False
else:
return _every(proc, cdr(arg_lst))
def _map(proc, lst):
"""
map for procedures that take a single argument
Apply proc to every element in arg_lst and return the resulting lst
"""
if lst is nil:
return nil
else:
return cons(proc(car(lst)),
_map(proc, cdr(lst)))
def map(proc, *tuple_of_lst):
"""
map for procedures that take any number of arguments, including 1
Apply proc to the n^th element in lst from lst_of_lst
and return the resulting lst
"""
lst_of_lst = tuple_to_lst(tuple_of_lst)
if _every(is_null, lst_of_lst):
return nil
elif _any(is_null, lst_of_lst):
raise IndexError("some of the lists are differed in length!")
else:
return cons(apply(proc,
_map(car, lst_of_lst)),
apply(map, cons(proc,
_map(cdr, lst_of_lst))))
def _fold(proc, init, lst):
"""
fold for procedures that take a single argument
If lst is the empty lst, return init
Otherwise, apply proc to the first element of lst and init in this order
Now, the result becomes the new init
"""
if lst is nil:
return init
else:
return _fold(proc, proc(car(lst), init), cdr(lst))
def reduce(proc, default, lst):
"""
If lst is the empty lst, return default
Otherwise, apply proc to the second element in lst
and the first element from lst in this order
Now, the result becomes the element after the remaining first element
"""
if lst is nil:
return default
elif cdr(lst) is nil:
return car(lst)
else:
return _fold(proc, car(lst), cdr(lst))
def any(proc, *tuple_of_lst):
"""
any for procedures that take any number of arguments, including 1
Apply proc to the n^th element in lst from lst_of_lst
Return True is any of the result is True
Otherwise, return False
"""
lst_of_lst = tuple_to_lst(tuple_of_lst)
return reduce(lambda x, y: x or y,
False,
apply(_map, cons(proc,
lst_of_lst)))
def every(proc, *tuple_of_lst):
"""
every for procedures that take any number of arguments, including 1
Apply proc to the n^th element in lst from lst_of_lst
Return True is any of the result is True
Otherwise, return False
"""
lst_of_lst = tuple_to_lst(tuple_of_lst)
return reduce(lambda x, y: x and y,
True,
apply(_map, cons(proc,
lst_of_lst)))
def reverse(lst):
"""reverse a given lst"""
return _fold(cons, nil, lst)
def filter(proc, lst):
"""
Apply proc to elements in lst
Remove those evaluated to False and return the resulting lst
"""
def filter_loop(proc, lst, accum):
if lst is nil:
return accum
elif proc(car(lst)):
return filter_loop(proc, cdr(lst), cons(car(lst), accum))
else:
return filter_loop(proc,
cdr(lst),
accum)
return reverse(filter_loop(proc, lst, nil))
def first(lst):
"""return the first element of lst, usually used with last"""
return car(lst)
def last(lst):
"""return the last element of lst, usually used with first"""
return car(reverse(lst))
def _append(lst1, lst2):
"""
append for procedure that takes a single argument
Append 2 lst into a single lst
"""
return _fold(cons, lst2, reverse(lst1))
def append(*tuple_of_lst):
"""
append for procedures that take any number of arguments, including 1
Append any number of lst into a single lst
"""
lst_of_lst = tuple_to_lst(tuple_of_lst)
return reduce(_append, nil, reverse(lst_of_lst))
def concatenate(lst_of_lst):
"""concatenate lst_of_lst into a single lst"""
return apply(append, lst_of_lst)
def fold(proc, init, *tuple_of_lst):
"""
fold for procedures that take any number of arguments, including 1
If every element in lst_of_lst is the empty lst, return init
Otherwise, apply proc to the first element of every element in lst_of_lst
and init in this order
Now, the result becomes the new init
"""
lst_of_lst = tuple_to_lst(tuple_of_lst)
if _every(is_null, lst_of_lst):
return init
elif _any(is_null, lst_of_lst):
raise IndexError("some of the lists are differed in length!")
else:
return apply(fold, cons(proc,
cons(apply(proc,
append(_map(car, lst_of_lst),
list(init))),
_map(cdr, lst_of_lst))))
def lset_difference(comparator, lst, *tuple_of_lst):
def _lset_difference(comparator, lst1, lst2):
"""treat lst1 and lst2 as sets and compute lst1 \ lst2"""
return filter(lambda x: _every(lambda y: not comparator(x, y),
lst2),
lst1)
lst_of_lst = tuple_to_lst(tuple_of_lst)
if lst_of_lst is nil:
return lst
else:
return apply(lset_difference,
cons(comparator,
cons(_lset_difference(comparator,
lst,
car(lst_of_lst)),
cdr(lst_of_lst))))
def drop_while(pred, lst):
"""
While predicate evaluates to True, drops the element
Return the lst if predicate evaluates to False or if lst is empty
"""
if lst is nil:
return nil
elif not pred(car(lst)):
return lst
else:
return drop_while(pred, cdr(lst))
def take_while(pred, lst):
"""
While predicate evaluates to True, takes the element
Return the empty lst if predicate evaluates to False or if lst is empty
"""
if lst is nil:
return nil
elif not pred(car(lst)):
return nil
else:
return cons(car(lst), take_while(pred, cdr(lst)))