This repository has been archived by the owner on Nov 8, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathiterate.lisp
172 lines (156 loc) · 5.79 KB
/
iterate.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -*- Mode: Lisp -*- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; File - iterate.lisp
;; Description - Applicative iteration
;; Author - Tim Bradshaw (tfb at lostwithiel)
;; Created On - Sat Oct 7 00:23:24 2000
;; Last Modified On - Mon Jun 3 17:34:15 2002
;; Last Modified By - Tim Bradshaw (tfb at lostwithiel)
;; Update Count - 12
;; Status - Unknown
;;
;; $Id: //depot/www-tfeb-org/before-2013-prune/www-tfeb-org/html/programs/lisp/iterate.lisp#1 $
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; * Applicative iteration (don't need this in CMUCL)
;;;
;;; Note all these forms do sequential bindings, like LET*.
;;;
;;; iterate.lisp is copyright 1997-2000 by me, Tim Bradshaw, and may
;;; be used for any purpose whatsoever by anyone. It has no warranty
;;; whatsoever. I would appreciate acknowledgement if you use it in
;;; anger, and I would also very much appreciate any feedback or bug
;;; fixes.
(provide :org.tfeb.hax.iterate)
(eval-when (:compile-toplevel :load-toplevel :execute)
(when (not (find-package ':org.tfeb.hax))
(make-package ':org.tfeb.hax)))
(eval-when (:compile-toplevel :load-toplevel :execute)
(export '(org.tfeb.hax::iterate)
(find-package ':org.tfeb.hax)))
(in-package :org.tfeb.hax)
(defconstant +tr-implementation-p+
#+Genera nil ;genera does no TRO
;; Assume true: even though this may not be safe you'll
;; soon realise at runtime
#-Genera t
"Can this implementation perform tail-call optimisation?
Specifically can it optimise (LABELS ((X (...) ... (X ...) ...))), where the
call to X is in tail position, given reasonable constraints (no special
bindings, in the case of CMU for instance")
(defmacro iterate (name bindings &body body)
"Scheme-style named-LET, with hacks.
For implementations which can do TRO, this compiles into LABELS
and recursive calls, which is fully general.
For implementations which can not, *if* the name contains the word `LOOP'
(any case), then this compiles into something dreadful using BLOCK and
RETURN-FROM, which is fast for looping but can't be used recursively.
If the name does *not* contain `LOOP' it compiles into LABELS as above.
Notes: bindings are sequential not parallel (because LABELS is), so this is
like LET* not LET. The local function defined should be considered to have
dynamic extent."
(cond (+tr-implementation-p+
;; labels is free
`(iterate/labels ,name ,bindings ,@body))
((search "LOOP" (string-upcase (symbol-name name)))
;; not TR, and we asked for a loop
`(iterate/tag ,name ,bindings ,@body))
(t
;; not TR, not loop
`(iterate/labels ,name ,bindings ,@body))))
(defmacro iterate/labels (name bindings &body body)
;; this one is the fully-fledged variant: note that this is like LET*
;; not LET
(let ((argnames ())
(argvals ()))
(labels ((grind-bindings (tail)
(if (not (null tail))
(etypecase (car tail)
(symbol
(grind-bindings (cdr tail))
(push (car tail) argnames)
(push nil argvals))
(list
(grind-bindings (cdr tail))
(push (car (car tail)) argnames)
(push (cadr (car tail)) argvals))))))
(grind-bindings bindings)
`(labels ((,name ,argnames
#+Genera(declare (sys:downward-function))
,@body))
(,name ,@argvals)))))
(defmacro iterate/tag (tag bindings &body body)
;; this is the hacky one! It really, really is a hack, believe me.
;; Note that the bindings are in sequence, not pll -- because ITERATE
;; is (accidentally), and I want this to be the same. I presume that
;; compilers get code that is as good for PROG* as PROG if there are
;; no dependencies.
(let ((argnames (mapcar #'(lambda (binding)
(etypecase binding
(symbol binding)
(cons (car binding))))
bindings)))
;; this used to use PROG*, but it's not clear if that really
;; portably allows an initial symbol as a name for the block.
;; This does (and gets the same code).
`(block ,tag
(let* ,bindings
(tagbody
,tag
(macrolet ((,tag (&rest args)
`(progn
(setf ,@(mapcan #'(lambda (name val)
(list name val))
',argnames args))
(go ,',tag))))
;; it's a pain that all the GO-containing forms need to be
;; RETURNed from explicitly rather than returning the last
;; value. On the SB this means the body of the loop is
;; one instruction bigger than DOTIMES, because the RETURN
;; compiles into a conditional branch (see below for
;; samples). Anyway that's why this slightly obscure
;; thing is done.
(return-from ,tag (progn ,@body))))))))
#||
;;; (ITERATE/TAG was formerly called TAGLET)
;;; trivial tests of TAGLET.
;;;
;;; I wondered why it was a bit slower than DOTIMES...
;;;
(defun ts (n)
;; trying to be a loop
(taglet loop ((i 0))
(if (< i n)
(loop (1+ i))
i)))
;;; TS compiles on a 36xx as
;;;
; 0 ENTRY: 1 REQUIRED, 0 OPTIONAL
; 1 PUSH-IMMED 0 ;creating I(FP|1)
; 2 PUSH-LOCAL FP|1 ;I
; 3 PUSH-LOCAL FP|0 ;N
; 4 BUILTIN INTERNAL-< STACK
; 5 BRANCH-FALSE 10
; 6 BUILTIN +-INTERNAL STACK 1
; 7 BRANCH 2
; 10 RETURN-STACK
;;;
;;; note the branch at 5 which somes from the RETURN
(defun tsloop (n)
;; actually a loop
(dotimes (i n i)))
;;; TSLOOP compiles on a 36xx as
;;;
; 0 ENTRY: 1 REQUIRED, 0 OPTIONAL
; 1 PUSH-IMMED 0 ;creating I(FP|1)
; 2 PUSH-LOCAL FP|0 ;N
; 3 BUILTIN PLUSP STACK
; 4 BRANCH-FALSE 12
; 5 BUILTIN +-INTERNAL STACK 1
; 6 PUSH-LOCAL FP|1 ;I
; 7 PUSH-LOCAL FP|0 ;N
; 10 BUILTIN INTERNAL-< STACK
; 11 BRANCH-TRUE 5
; 12 RETURN-STACK
;;;
;;; there is no branch in the loop here so this is a bit faster. Of
;;; course a decent compiler would probably deal with this anyway.
||#