;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*- ;;;; ************************************************************************* ;;;; FILE IDENTIFICATION ;;;; ;;;; Name: pipes.lisp ;;;; Purpose: Pipes based on ideas from Norvig's PAIP book ;;;; Programmer: Kevin M. Rosenberg ;;;; Date Started: Apr 2000 ;;;; ;;;; $Id: pipes.lisp,v 1.3 2002/10/10 16:23:48 kevin Exp $ ;;;; ;;;; This file, part of KMRCL, is Copyright (c) 2002 by Kevin M. Rosenberg ;;;; ;;;; KMRCL users are granted the rights to distribute and use this software ;;;; as governed by the terms of the Lisp Lesser GNU Public License ;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL. ;;;; ************************************************************************* (in-package :kmrcl) (defmacro make-pipe (head tail) "create a pipe by eval'ing head and delaying tail." `(cons ,head #'(lambda () ,tail))) (defun pipe-tail (pipe) "return tail of pipe or list, and destructively update the tail if it is a function." ;; This assumes that pipes will never contain functions as values... (if (functionp (rest pipe)) (setf (rest pipe) (funcall (rest pipe))) (rest pipe))) (defun pipe-head (pipe) (first pipe)) (defun pipe-elt (pipe i) "ith element of pipe, 0 based." (if (= i 0) (pipe-head pipe) (pipe-elt (pipe-tail pipe) (- i 1)))) (defconstant +empty-pipe+ nil) (defun enumerate (pipe &key count key (result pipe)) "go through all or count elements of pipe, possibly applying the key function. " (if (or (eq pipe +empty-pipe+) (eql count 0)) result (progn (unless (null key) (funcall key (pipe-head pipe))) (enumerate (pipe-tail pipe) :count (if count (1- count)) :key key :result result)))) (defun pipe-display (pipe &optional count) (enumerate pipe :count count)) (defun pipe-force (pipe) (enumerate pipe)) ;;; incorrect version-- as in Norvig. ;(defun filter-pipe (predicate pipe) ; "keep only items in (non-null) pipe satisfying predicate" ; (if (funcall predicate (head pipe)) ; (make-pipe (head pipe) (filter-pipe predicate (tail pipe))) ; (pipe-filter predicate (tail pipe)))) (defun pipe-filter (predicate pipe) "keep only items in (non-null) pipe satisfying predicate" (if (eq pipe +empty-pipe+) +empty-pipe+ (let ((head (pipe-head pipe)) (tail (pipe-tail pipe))) (if (funcall predicate head) (make-pipe head (pipe-filter predicate tail)) (pipe-filter predicate tail))))) (defun pipe-map (fn pipe) "Map fn over pipe, delaying all but the first fn call, collecting res