61f852bc8c6918ce04552ef67ca33e1765973956
[pipes.git] / src.lisp
1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
2 ;;;; *************************************************************************
3 ;;;; FILE IDENTIFICATION
4 ;;;;
5 ;;;; Name:          pipes.lisp
6 ;;;; Purpose:       Pipes based on ideas from Norvig's PAIP book
7 ;;;; Programmers:   Kevin M. Rosenberg and Peter Norvig
8 ;;;; Date Started:  Apr 2000
9 ;;;;
10 ;;;; $Id: src.lisp,v 1.1 2003/03/15 00:48:56 kevin Exp $
11 ;;;;
12 ;;;; This file is Copyright (c) 2000-2002 by Kevin M. Rosenberg and
13 ;;;; Copyright (c) 1998-2002 by Peter Norvig. 
14 ;;;; *************************************************************************
15
16 (in-package :pipes)
17
18
19 (defconstant +empty-pipe+ nil)
20
21 (defmacro make-pipe (head tail)
22   "Create a pipe by evaluating head and delaying tail."
23   `(cons ,head #'(lambda () ,tail)))
24
25 (defun pipe-tail (pipe)
26   "Return tail of pipe or list, and destructively update 
27    the tail if it is a function."
28   (if (functionp (rest pipe))
29     (setf (rest pipe) (funcall (rest pipe)))
30     (rest pipe)))
31
32 (defun pipe-head (pipe) (first pipe))
33
34 (defun pipe-elt (pipe i)
35   "The i-th element of pipe, 0-based."
36   (if (= i 0)
37       (pipe-head pipe)
38     (pipe-elt (pipe-tail pipe) (- i 1))))
39
40
41 (defun pipe-enumerate (pipe &key count key (result pipe))
42   "Go through all (or count) elements of pipe,
43    possibly applying the KEY function. (Try PRINT.)"
44   ;; Returns RESULT, which defaults to the pipe itself. 
45   (if (or (eq pipe +empty-pipe+) (eql count 0))
46     result
47     (progn
48       (unless (null key) (funcall key (pipe-head pipe)))
49       (pipe-enumerate (pipe-tail pipe)
50                  :count (if count (1- count))
51                  :key key :result result))))
52
53 (defun pipe-values (pipe &optional count)
54   "Simple wrapper to return values of a pipe"
55   (pipe-enumerate pipe :count count))
56
57 (defun pipe-force (pipe)
58   "Force the enumeration of all of the pipe. Never returns
59 if the pipe is infinite in length."
60   (pipe-enumerate pipe))
61
62 (defun pipe-filter (predicate pipe)
63   "Keep only items in pipe satisfying predicate."
64   (if (eq pipe +empty-pipe+)
65       +empty-pipe+
66     (let ((head (pipe-head pipe))
67           (tail (pipe-tail pipe)))
68       (if (funcall predicate head)
69           (make-pipe head (pipe-filter predicate tail))
70         (pipe-filter predicate tail)))))
71                
72
73 (defun pipe-map (fn pipe)
74   "Map fn over pipe, delaying all but the first fn call."
75   (if (eq pipe +empty-pipe+)
76       +empty-pipe+
77     (make-pipe (funcall fn (pipe-head pipe))
78                (pipe-map fn (pipe-tail pipe)))))
79
80
81 (defun pipe-map-filtering (fn pipe &optional filter-pred)
82   "Map fn over pipe, delaying all but the first fn call,
83    while filtering results."
84   (if (eq pipe +empty-pipe+)
85       +empty-pipe+
86     (let* ((head (pipe-head pipe))
87            (tail (pipe-tail pipe))
88            (result (funcall fn head)))
89       (if (or (and filter-pred (funcall filter-pred result))
90               result)
91           (make-pipe result (pipe-map-filtering fn tail filter-pred))
92         (pipe-map-filtering fn tail filter-pred)))))
93
94       
95 (defun pipe-append (x y)
96   "Return a pipe that appends the elements of x and y."
97   (if (eq x +empty-pipe+)
98       y
99     (make-pipe (pipe-head x)
100                (pipe-append (pipe-tail x) y))))
101
102
103 (defun pipe-mappend (fn pipe)
104   "Lazily map fn over pipe, appending results."
105   (if (eq pipe +empty-pipe+)
106       +empty-pipe+
107     (let ((x (funcall fn (pipe-head pipe))))
108       (make-pipe (pipe-head x)
109                  (pipe-append (pipe-tail x)
110                               (pipe-mappend fn (pipe-tail pipe)))))))
111
112 (defun pipe-mappend-filtering (fn pipe &optional filter-pred)
113   "Map fn over pipe, delaying all but the first fn call,
114    appending results while filtering."
115   (if (eq pipe +empty-pipe+)
116       +empty-pipe+
117     (let* ((head (pipe-head pipe))
118            (tail (pipe-tail pipe))
119            (result (funcall fn head)))
120       (if (or (and filter-pred (funcall filter-pred result))
121               result)
122           (make-pipe (pipe-head result)
123                      (pipe-append (pipe-tail result)
124                                   (pipe-mappend-filtering fn tail filter-pred)))
125         (pipe-mappend-filtering fn tail filter-pred)))))