/
docume.89
executable file
·2194 lines (2075 loc) · 71.3 KB
/
docume.89
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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
.xgp
.vsp 11
.squish
.ltrspc 0
.c << text font >>
.font 0 25fr1
.c << SCHEME font >>
.font 1 22fg
.c << heading fonts >>
.font 2 30vrb
.font 3 66vr
.font 4 gls;foo1
.quote
.dummy _
.twinch 6.25
.tlinch 9
.sidm 53
.c << want to flush losing multiple cr's >>
.crcomp
.c << want to make leading spaces on line small >>
.c << want to have at least 3 lines of a section on a page >>
.sblock 5
.spw 16
.adjust
.center
2MASSACHUSETTS INSTITUTE OF TECHNOLOGY
.CENTER
ARTIFICIAL INTELLIGENCE LABORATORY
.sp
.spread
/0AI Memo No. 452//January 1978
.sp
.center
2THE__REVISED__REPORT__ON
.sp 2
.center
3SCHEME
.sp
.center
2A__DIALECT__OF__LISP
.sp
.center
0by
.sp
.center
Guy Lewis Steele Jr.* and Gerald Jay Sussman
.sp 2
0Abstract:
.br
SCHEME is a dialect of LISP.
It is an expression-oriented, applicative order,
interpreter-based language which allows one to
manipulate programs as data.
It differs from most current dialects of LISP
in that it closes all lambda-expressions
in the environment of their definition or declaration,
rather than in the execution environment.
This has the consequence that variables are normally lexically scoped,
as in ALGOL. However, in contrast with ALGOL,
SCHEME treats procedures as a first-class data type.
They can be the values of variables, the returned values of
procedures, and components of data structures.
Another difference from LISP is that SCHEME is
implemented in such a way that tail-recursions execute
without net growth of the interpreter stack.
The effect of this is that a procedure call behaves like a GOTO,
and thus procedure calls can be used to implement iterations,
as in PLASMA.
Here we give a complete "user manual" for the SCHEME
language. Some features described here were not documented in the original
report on SCHEME (for instance particular macros).
Other features have been added, changed, or deleted
as our understanding of certain language issues evolved.
Annotations to the manual describe the motivations
for these changes.
.sp
.in 4
.un 4
Keywords:__LISP, SCHEME, LISP-like languages,
lambda-calculus, environments, lexical scoping, dynamic scoping,
fluid variables, control structures, macros, extensible syntax,
extensible languages
.in 0
.sp
This report describes research done at the Artificial Intelligence
Laboratory of the Massachusetts Institute of Technology.
Support for the laboratory's artificial intelligence research
is provided in part by the Advanced Research Projects Agency
of the Department of Defense under Office of Naval Research contract N00014-75-C-0643.
.sp
*__NSF Fellow
.page
.spage
.php1
.he1
1Steele and Sussman
.he2
1The Revised Report on SCHEME0
.section
A.__The Representation of SCHEME Procedures as S-expressions
.sp
SCHEME programs are represented as LISP s-expressions.
The evaluator interprets these s-expressions in a specified way.
This specification constitutes the definition of the language.
The definition of SCHEME is a little fuzzy around the edges.
This is because of the inherent extensibility of LISP-like languages
{Note LISP Is a Ball of Mud}.
We can define a few essential features
which constitute the "kernel" of the language,
and also enumerate several syntactic and semantic extensions
which are convenient and normally included in a given implementation.
The existence of a mechanism for such extensions
is a part of the kernel of SCHEME; however, any particular
such extension is not necessarily part of the kernel.
.sp
For those who like this sort of thing,
here is the BNF for SCHEME programs {Note LISP BNF}:
.sp
.nofill
.spw 13
.in 4
1<form> ::= <non-symbol atom> | <identifier> | <magic> | <combination>
<identifier> ::= <atomic symbol>
<magic> ::= <lambda-expression>
| (QUOTE <s-expression> )
| (IF <form> <form> <form> )
| (IF <form> <form> )
| (LABELS ( <labels list> ) <body> )
| (DEFINE <identifier> <lambda-expression> )
| (DEFINE <identifier> ( <identifier list> ) <form> )
| (DEFINE ( <identifier> <identifier list> ) <form> )
| (ASET' <identifier> <form> )
| (FLUIDBIND ( <fluidbind list> ) <form> )
| (FLUID <identifier> )
| (FLUIDSET' <identifier> <form> )
| (CATCH <identifier> <form> )
| <syntactic extension>
<lambda-expression> ::= (LAMBDA ( <identifier list> ) <body>)
<identifier list> ::= <empty> | <identifier> <identifier list>
<body> ::= <form>
<labels list> ::= <empty>
| ( <identifier> <lambda-expression> ) <labels list>
<fluidbind list> ::= <empty> | ( <identifier> <form> ) <fluidbind list>
<combination> ::= ( <form list> )
<form list> ::= <form> | <form> <form list>
<syntactic extension> ::= ( <magic word> . <s-expression> )
<magic word> ::= <atomic symbol>
<non-symbol atom> ::= <number> | <array> | <string> | ...0
.in 0
.spw 16
.adjust
.sp
Atoms which are not atomic symbols (identifiers) evaluate to themselves.
Typical examples of such atoms are numbers, arrays, and strings (character arrays).
Symbols are treated as identifiers or variables.
They may be lexically bound by lambda-expressions.
There is a global environment containing values for (some) free variables.
Many of the variables in this global environment initially
have as their values primitive operations such as, for example,
1CAR0, 1CONS0, and 1PLUS0.
SCHEME differs from most LISP systems in that the atom 1CAR0
is not itself an operation (in the sense of being an invocable object,
e.g. a valid first argument to 1APPLY*),
but only has one as a value when considered as an identifier.
Non-atomic forms are divided by the evaluator into two classes:
combinations and "magic (special) forms".
The BNF given above is ambiguous; any magic form can also be parsed
as a combination. The evaluator always treats an ambiguous case as
a magic form. Magic forms are recognized by the presence of a
"magic (reserved) word" in the car position of the form.
All non-atomic forms which are not magic forms
are considered to be combinations.
The system has a small initial set of magic words;
there is also a mechanism for creating new ones
{Note FUNCALL is a Pain}.
A combination is considered to be a list
of subforms. These subforms are all evaluated.
The first value must be a procedure; it is applied
to the other values to get the value of the combination.
There are four important points here:
.sp
.in 3
(1)__The procedure position is always evaluated
just like any other position. (This is why the primitive
operators are the values of global identifiers.)
.sp
(2)__The procedure is never "re-evaluated"; if the first subform fails to
evaluate to an applicable procedure, it is an error.
Thus, unlike most LISP systems, SCHEME always evaluates the
first subform of a combination exactly once.
.sp
(3)__The arguments are all completely evaluated
before the procedure is applied; that is, SCHEME,
like most LISP systems, is an applicative-order language.
Many SCHEME programs exploit this fact.
.sp
(4)__The argument forms (and procedure form) may in principle
be evaluated in any order. This is unlike the usual LISP left-to-right order.
(All SCHEME interpreters implemented so far have in fact
performed left-to-right evaluation, but we do not wish programs
to depend on this fact. Indeed, there are some reasons why
a clever interpreter might want to evaluate them right-to-left,
e.g. to get things on a stack in the correct order.)
.in 0
.page
.section
B.__Catalogue of Magic Forms
.sp
.section
B.1.__Kernel Magic Forms
.sp
The magic forms in this section are all part of the kernel of SCHEME,
and so must exist in any SCHEME implementation.
.in 3
.sp 2
.block 4
.un 3
1(LAMBDA ( <identifier list> ) <body> )0
.sp
Lambda-expressions evaluate to procedures.
Unlike most LISP systems, SCHEME does not consider a lambda-expression
(an s-expression whose car is the atom 1LAMBDA0)
to be a procedure. A lambda-expression only evaluates to a procedure.
A lambda-expression should be thought of as a partial
description of a procedure; a procedure and a description
of it are conceptually distinct objects.
A lambda-expression must be "closed" (associated with an
environment) to produce a procedure object.
Evaluation of a lambda-expression performs such a closure operation.
The resulting procedure takes as many arguments as there are
identifiers in the identifier list of the lambda-expression.
When the procedure is eventually invoked,
the intuitive effect is that the evaluation of the procedure call is
equivalent to the evaluation
of the 1<body>0 in an environment consisting of
(a)_the environment in which the lambda-expression had been evaluated to
produce the procedure, plus (b)_the pairing of the
identifiers of the 1<identifier list>0 with the arguments supplied
to the procedure. The pairings (b) take precedence over the environment (a),
and to prevent confusion no identifier may appear twice
in the 1<identifier list>*.
The net effect is to implement ALGOL-style lexical
scoping [Naur], and to "solve the funarg problem" [Moses].
.sp 2
.block 4
.un 3
1(IF <predicate> <consequent> <alternative>)0
.sp
This is a primitive conditional operator.
The predicate form is evaluated. If the result
is non-1NIL0 {Note 1IF* Is Data-Dependent},
then the consequent is evaluated, and otherwise the alternative is evaluated.
The resulting value (if there is one) is the value of the 1IF0 form.
.sp 2
.block 4
.un 3
1(IF <predicate> <consequent>)0
.sp
As above, but if the predicate evaluates to 1NIL0,
then 1NIL0 is the value of the 1IF0 form. (As a matter of style,
this is usually used
only when the value of the 1IF0 form doesn't matter,
for example, when the consequent is intended to cause a side effect.)
.sp 2
.block 4
.un 3
1(QUOTE <s-expression>)0
.sp
As in LISP, this quotes the argument form so
that it will be passed verbatim as data; the value of
this form is 1<s-expression>0.
If a SCHEME implementation has the MacLISP read-macro-character feature, then
the abbreviation 1'FOO0 may be used instead of 1(QUOTE FOO)0.
.sp 2
.block 4
.un 3
1(LABELS ( <labels list> ) <body> )0
where 1<labels list> ::=0
1 <empty> | ( <identifier> <lambda-expression> ) <labels list>0
.sp
This has the effect of evaluating the 1<body>0 in an environment
where all the identifiers (which, as for 1LAMBDA*, must all be distinct)
in the labels list evaluate to
the values of the respective lambda-expressions.
Furthermore, the procedures which are the values of
the lambda-expressions are themselves closed in that environment,
and not in the outer environment; this allows the procedures to call
themselves and each other recursively.
For example, consider a procedure which counts all the atoms in a list
structure recursively to all levels, but which doesn't count the 1NIL0s
which terminate lists (but 1NIL0s in the car of a list count).
In order to perform this we define two mutually recursive procedures,
one to count the car and one to count the cdr, as follows:
.sp
.block 14
.nofill
.spw 13
1(DEFINE COUNT
(LAMBDA (L)
(LABELS ((COUNTCAR
(LAMBDA (L)
(IF (ATOM L) 1
(+ (COUNTCAR (CAR L))
(COUNTCDR (CDR L))))))
(COUNTCDR
(LAMBDA (L)
(IF (ATOM L)
(IF (NULL L) 0 1)
(+ (COUNTCAR (CAR L))
(COUNTCDR (CDR L)))))))
(COUNTCDR L))))0
.spw 16
.adjust
.sp
(We have decided not to use the traditional LISP
1LABEL0 primitive in SCHEME because it is difficult to define several mutually
recursive procedures using only 1LABEL0.
Although 1LABELS* is a little more complicated than 1LABEL*,
it is considerably more convenient.
Contrast this design decision with the choice of
1IF* over the more traditional 1COND*, where the definitional
simplicity of 1IF* outweighed the somewhat greater convenience of 1COND*.)
.in 0
.sp 2
.section
B.2.__Side Effects
.sp
These magic forms produce side effects in the environment.
.in 3
.sp 2
.block 4
.un 3
1(DEFINE <identifier> <lambda-expression> )0
.sp
This is used for defining a procedure in the "global environment" permanently,
as opposed to 1LABELS0, which is used for temporary procedure definitions
in a local environment. 1DEFINE0 takes a name and a lambda-expression;
it evaluates the lambda-expression in the global environment and
causes the result to be the global value of the identifier.
(1DEFINE0 may perform other implementation-dependent operations as well,
such as keeping track of defined procedures for an editor.
For this reason it is the preferred way to define a globally
available procedure.)
.sp 2
.block 4
.un 3
1(DEFINE <identifier> ( <identifier list> ) <form list> )0
.br
.un 3
1(DEFINE ( <identifier> <identifier list> ) <form list> )0
.sp
These alternative syntaxes permitted by 1DEFINE0
are equivalent to:
.sp
.block 3
.nofill
.spw 13
1(DEFINE <identifier>
(LAMBDA ( <identifier list> )
(BLOCK <form list> )))0
.spw 16
.adjust
.sp
where 1BLOCK* is a syntactic extension defined below.
For example, these three definitions are equivalent:
.sp
.nofill
.spw 13
.block 3
1(DEFINE CIRCULATE (LAMBDA (X) (RPLACD X X)))
(DEFINE CIRCULATE (X) (RPLACD X X))
(DEFINE (CIRCULATE X) (RPLACD X X))0
.spw 16
.adjust
.sp
These forms are provided to support stylistic diversity.
.sp 2
.block 4
.un 3
1(ASET' <identifier> <form>)0
.sp
This is analogous to the LISP primitive 1SETQ0.
For example, to define a cell [Smith and Hewitt],
we may use 1ASET'0 as follows:
.sp
.block 12
.nofill
.spw 13
1(DEFINE CONS-CELL
(LAMBDA (CONTENTS)
(LABELS ((THE-CELL
(LAMBDA (MSG)
(IF (EQ MSG 'CONTENTS?) CONTENTS
(IF (EQ MSG 'CELL?) 'YES
(IF (EQ (CAR MSG) '<-)
(BLOCK (ASET' CONTENTS (CADR MSG))
THE-CELL)
(ERROR '|UNRECOGNIZED MESSAGE - CELL|
MSG
'WRNG-TYPE-ARG)))))))
THE-CELL)))0
.spw 16
.adjust
.sp
Note that 1ASET'0 may be used on global identifiers as well as locally bound identifiers
{Note 1ASET0 Has Disappeared}.
.in 0
.sp 2
.section
B.3.__Dynamic Magic
.sp
These magic forms implement escape objects and fluid (dynamic)
variables. They are not a part of the essential kernel.
For a further explication of their semantics in terms of kernel primitives,
see [Imperative].
.in 3
.sp 2
.block 4
.un 3
1(FLUIDBIND ( <fluidbind list> ) <form> )0
where 1<fluidbind list> ::=0
1 <empty> | ( <identifier> <form> ) <fluidbind list>0
.sp
This evaluates the 1<form>0 in the environment of the
1FLUIDBIND0 form, with a dynamic environment to which
dynamic bindings of the identifiers in the 1<fluidbind list>0
have been added. Any procedure dynamically called by
the form, even if not lexically apparent to the 1FLUIDBIND0 form,
will see this dynamic environment (unless modified by further
1FLUIDBIND0s, of course). The dynamic environment is restored
on return from the form.
Most LISP systems use a dynamic environment for
all variables. A SCHEME which implements 1FLUIDBIND0 provides two
distinct environments. The fluid variable named 1FOO0 is completely
unrelated to a normal lexical variable named 1FOO0
{Note Global Fluid Environment},
and the access mechanisms for the two are distinct.
.sp 2
.block 4
.un 3
1(FLUID <identifier> )0
.sp
The value of this form is the value of the 1<identifier>* in
the current dynamic environment. In SCHEME implementations
which have the MacLISP read-macro-character feature,
1(FLUID FOO)0 may be abbreviated to 1FOO0.
.sp 2
.block 4
.un 3
1(FLUIDSET' <identifier> <form> )0
.sp
The value of the 1<form>0 is assigned to the 1<identifier>0 in the
current dynamic environment.
.sp 2
.block 4
.un 3
1(STATIC <identifier>)0
.sp
The value of this is the value of the lexical identifier;
writing this is the same as writing just 1<identifier>0
{Note What Good Is It?}.
.sp 2
.block 4
.un 3
1(CATCH <identifier> <form> )0
.sp
This evaluates the form in an environment where the identifier is bound to
an "escape object" [Landin] [Reynolds].
This is a strange object which can be invoked as if
it were a procedure of one argument. When the escape object is so invoked,
then control proceeds as if the 1CATCH0 expression had returned with the
supplied argument as its value
{Note Multiple Throw}.
If both 1CATCH0 and 1FLUIDBIND0 are implemented, then their
semantics are intertwined. When the escape object is called,
then the dynamic environment is restored to
the one which was current at the time the 1CATCH0 form was evaluated
{Note Environment Symmetry}.
For a contorted example,
consider the following obscure definition of 1SQRT0
(Sussman's least favorite style/Steele's favorite; but see [SCHEME]):
.sp
.block 13
.nofill
.spw 13
1(DEFINE SQRT
(LAMBDA (X EPSILON)
((LAMBDA (ANS TAG GO)
(CATCH RETURN
(BLOCK
(CATCH M (ASET' TAG M)) ;CREATE PROG TAG
(IF (< (ABS (-$ (*$ ANS ANS) X)) EPSILON) ;CAMGE
(RETURN ANS)) ;POPJ
(ASET' ANS (//$ (+$ (//$ X ANS) ANS) 2.0)) ;MOVEM
(GO TAG)))) ;JRST
1.0
NIL
(LAMBDA (F) (F NIL)))))0
.spw 16
.adjust
.sp
This example differs slightly from the version given in [SCHEME];
notice the forms 1(RETURN ANS)0 and 1(GO TAG)0.
As another example, we can define a 1THROW0 function,
which may then be used with 1CATCH0 much as it is in MacLISP [Moon]
(except that in MacLISP the tag is written after the body of the 1CATCH0,
not before):
.sp
.nofill
.spw 13
1(DEFINE THROW (LAMBDA (TAG RESULT) (TAG RESULT)))*
.spw 16
.adjust
.sp
An example of its use:
.sp
.nofill
.spw 13
.block 3
1(CATCH LOSE
(MAPCAR (LAMBDA (X) (IF (MINUSP X) (THROW LOSE NIL) (SQRT X)))
NUMLIST))0
.spw 16
.adjust
.sp
Indeed, note the similarity between 1THROW0 and the definition of
1GO0 in the first example.
.in 0
.page
.section
C.__Syntactic Extensions
.sp
SCHEME has a syntactic extension mechanism which provides
a way to define an identifier to be a magic word,
and to associate a function with that word.
The function accepts the magic form as an argument,
and produces a new form; this new form is then evaluated
in place of the original (magic) form.
This is precisely the same as the MacLISP macro facility.
.sp 2
.section
C.1.__System-Provided Extensions
.sp
Some standard syntactic extensions are provided by the system
for convenience in ordinary programming. They are distinguished
from other magic words in that they are semantically defined in terms of others
rather than being primitive {Note FEXPRs Are Okay by Us}.
For expository purposes they are described here in a
pattern-matching/production-rule kind of language.
The matching is on s-expression structure, not on character string syntax,
and takes advantage of the definition of list notation:
1(A_B_C) = (A_._(B_._(C_._NIL)))0.
Thus the pattern 1(x_._r)0 matches 1(A_B_C)0, with 1x_=_A0
and 1r_=_(B C)0.
The ordering of the "productions" is significant;
the first one which matches is to be used.
.in 3
.sp 2
.block 4
.nofill
.spw 13
.un 3
1(BLOCK x1 x2 ... xn)0
.sp
1 (BLOCK x) 41 x0
1 (BLOCK x . r) 41 ((LAMBDA (A B) (B)) x (LAMBDA () (BLOCK . r)))0
.spw 16
.adjust
.sp
1BLOCK0 sequentially evaluates the subforms 1xi0 from left to right.
For example:
.sp
.nofill
.spw 13
1 (BLOCK (ASET' X 43) (PRINT X) (+ X 1))0
.spw 16
.adjust
.sp
returns 1440 after setting 1X0 to 143* and then printing it
{Note 1BLOCK0 Exploits Applicative Order}.
.sp 2
.block 4
.nofill
.spw 13
.un 3
1(LET ((v1 x1) (v2 x2) ... (vn xn)) . body)0
.sp
41 ((LAMBDA (v1 v2 ... vn) (BLOCK . body)) x1 x2 ... xn)0
.spw 16
.adjust
.sp
1LET0 provides a convenient syntax for binding several
variables to corresponding quantities. It allows the
forms for the quantities to appear textually adjacent
to their corresponding variables. Notice that the
variables are all bound simultaneously, not sequentially,
and that the initialization forms 1xi* may be evaluated
in any order.
For convenience, 1LET0 also supplies a 1BLOCK0 around the forms constituting its body.
.sp 2
.nofill
.spw 13
.block 10
.un 3
1(DO ((v1 x1 s1) ... (vn xn sn)) (test . done) . body)0
.sp
.block 8
.nofill
.spw 13
41 (LET ((A1 (LAMBDA () x1))
(B1 (LAMBDA (v1 ... vn) s1))
...
(An (LAMBDA () xn))
(Bn (LAMBDA (v1 ... vn) sn))
(TS (LAMBDA (v1 ... vn) test))
(DN (LAMBDA (v1 ... vn) (BLOCK . done)))
(BD (LAMBDA (v1 ... vn) (BLOCK . body))))
.block 9
(LABELS ((LOOP
(LAMBDA (Z1 ... Zn)
(IF (TS Z1 ... Zn)
(DN Z1 ... Zn)
(BLOCK (BD Z1 ... Zn)
(LOOP (B1 Z1 ... Zn)
...
(Bn Z1 ... Zn)))))))
(LOOP (A1) ... (An))))0
.spw 16
.adjust
.sp
This is essentially the MacLISP "new-style" 1DO0 loop [Moon].
The variables 1vi0 are bound to the values of the corresponding 1xi0,
and stepped in parallel after every execution of the body by the 1si0
(by "step" we mean "set to the value of", not "increment by").
If an 1si0 is omitted, 1vi0 is assumed; this results in the variable
not being stepped. If in addition 1xi0 is omitted, 1NIL0 is assumed.
The loop terminates when the test evaluates non-1NIL0; it is
evaluated before each execution of the body. When this occurs,
the 1done0 part is evaluated as a 1BLOCK0.
The complexity of the definition shown is due to an effort to
avoid conflict of variable names, as for 1BLOCK0.
The auxiliary variables 1Ai0, 1Bi0, and 1Zi0 must be generated
to produce as many as are needed, but they need not be chosen
different from all variables appearing in 1xi0, 1si0, 1body0, etc.
The iteration is effected entirely by procedure calls.
In this manner the definition of 1DO* exploits the tail-recursive
properties of SCHEME [SCHEME] [Imperative].
As an example, here is a definition of a function
to find the length of a list:
.sp
.nofill
.spw 13
1(DEFINE (LENGTH X)
(DO ((Z X (CDR Z))
(N 0 (+ N 1)))
((NULL Z) N)))0
.spw 16
.adjust
.sp
The initializations forms 1xi* may be evaluated in any order,
and on each iteration the stepping form 1si* may be evaluated in any order.
This differs from the MacLISP definition of 1DO*.
For example, this definition of 1NREVERSE* (destructively reverse a list)
would work in MacLISP but not necessarily in SCHEME:
.sp
.nofill
.spw 13
.block 4
1 (DEFINE NREVERSE (X)
(DO ((A X (CDR A))
(B NIL (RPLACD A B)))
((NULL A) B)))0
.spw 16
.adjust
.sp
This definition depends on the 1CDR* occurring before the 1RPLACD*.
In SCHEME we must instead write:
.sp
.nofill
.spw 13
.block 4
1(DEFINE NREVERSE (X)
(DO ((A X (PROG1 (CDR A) (RPLACD A B)))
(B NIL A))
((NULL A) B)))*
.spw 16
.adjust
.sp
where by 1(PROG1 x y)* we mean 1((LAMBDA (P_Q) (BLOCK_(Q)_P)) x_(LAMBDA_()_y))*
(but 1PROG1* is not really a defined SCHEME primitive).
Note also that the effect of an 1ASET'0 on a 1DO0 variable does not
survive to the next iteration; this differs from using 1SETQ0
on a 1DO0 variable in MacLISP.
.sp 2
.block 6
.un 3
1(ITERATE name ((v1 e1) ... (vn en)) . body)0
.sp
.nofill
.spw 13
41 (LABELS ((name (LAMBDA (v1 ... vn) (BLOCK . body))))
(name e1 ... en))0
.spw 16
.adjust
.sp
This defines a looping construct more general than 1DO0.
For example, consider a function to sort out a list of
s-expressions into atoms and lists:
.sp
.nofill
.spw 13
.block 9
1(DEFINE COLLATE
(LAMBDA (X)
(ITERATE COL
((Z X) (ATOMS NIL) (LISTS NIL))
(IF (NULL Z)
(LIST ATOMS LISTS)
(IF (ATOM (CAR Z))
(COL (CDR Z) (CONS (CAR Z) ATOMS) LISTS)
(COL (CDR Z) ATOMS (CONS (CAR Z) LISTS)))))))0
.spw 16
.adjust
.sp
We have found many situations involving loops where there may be more
than one condition on which to exit and/or more than one condition
to iterate, where 1DO0 is too restrictive but 1ITERATE0 suffices.
Notice that because each loop has a name, one can
specify from an inner loop that the next iteration of any outer
loop is to occur. Here is a function very similar to the one used
in one SCHEME implementation for variable lookup:
there are two lists of lists, one containing names and the other values.
.sp
.nofill
.spw 13
.block 4
1(DEFINE (LOOKUP NAME VARS VALUES)
(ITERATE MAJOR-LOOP
((VARS-BACKBONE VARS)
(VALUES-BACKBONE VALUES))
.block 5
(IF (NULL VARS-BACKBONE)
NIL
(ITERATE MINOR-LOOP
((VARS-RIB (CAR VARS-BACKBONE))
(VALUES-RIB (CAR VALUES-BACKBONE)))
.block 3
(IF (NULL VARS-RIB)
(MAJOR-LOOP (CDR VARS-BACKBONE)
(CDR VALUES-BACKBONE))
.block 4
(IF (EQ (CAR VARS-RIB) NAME)
VALUES-RIB
(MINOR-LOOP (CDR VARS-RIB)
(CDR VALUES-RIB))))))))0
.spw 16
.adjust
.sp
(We had originally wanted to call this construct 1LOOP0,
but see {Note 1FUNCALL0 is a Pain}. Compare this with looping constructs
appearing in [Hewitt].)
It happens that 1ITERATE* is a misleading name; the construct can actually
be used for recursion ("true" recursion, as opposed to tail-recursion) as well.
If the 1name* is invoked from a non-tail-recursive situation,
the argument evaluation in which the call is embedded is not
aborted. It just so happens that we have found 1ITERATE0 useful
primarily to implement complicated iterations.
One can draw the rough analogy 1ITERATE* : 1LABELS* ::
1LET* : 1LAMBDA*.
.sp 2
.block 6
.un 3
1(TEST pred fn alt)0
.sp
.nofill
.spw 13
41 ((LAMBDA (P F A) (IF P ((F) P) (A)))
pred
(LAMBDA () fn)
(LAMBDA () alt))0
.spw 16
.adjust
.sp
The predicate is evaluated; if its value is non-1NIL0
then the form 1fn0 should evaluate to a procedure of one argument,
which is then invoked on the value of the predicate. Otherwise
the alternative 1alt0 is evaluated.
This construct is of occasional use with LISP "predicates" which
return a "useful" non-1NIL0 value. For the consequent of an 1IF0
to get at the non-1NIL0 value of the predicate, one might first
bind a variable to the value of the predicate, and this variable
would then be visible to the alternative as well. With 1TEST0, the use
of the variable is restricted to the consequent.
An example:
.sp
.nofill
.spw 13
1(TEST (ASSQ VARIABLE ENVIRONMENT)
CDR
(GLOBALVALUE VARIABLE))0
.spw 16
.adjust
.sp
Using an a-list to represent an environment, one wants
to use the cdr of the result of 1ASSQ0 if it is non-1NIL0;
but if it is 1NIL0, then the variable was not in the environment,
and one must look elsewhere.
.sp 2
.block 8
.nofill
.spw 13
.un 3
1(COND (p1 . e1) ... (pn . en))0
.sp
.nofill
.spw 13
.block 6
1(COND) 41 'NIL
(COND (p) . r) 41 ((LAMBDA (V R) (IF V V (R)))
p
(LAMBDA () (COND . r)))
(COND (p => f) . r) 41 (TEST p f (COND . r))
(COND (p . e) . r) 41 (IF p (BLOCK . e) (COND . r))0
.spw 16
.adjust
.sp
This 1COND0 is a superset of the MacLISP 1COND0.
As in MacLISP, singleton clauses return the value of the predicate if it is non-1NIL0, and
clauses with two or more forms treat the first as the predicate
and the rest as constituents of a 1BLOCK0, thus evaluating them
in order.
The extension to the MacLISP 1COND0 made in SCHEME is flagged
by the atom 1=>0.
(It cannot be confused with the more general case of two 1BLOCK0
constituents because having the atom 1=>0 as the first element of a 1BLOCK0
is not useful.) In this situation the form 1f0 following the 1=>0
should have as its value a function of one argument;
if the predicate 1p0 is non-1NIL0, this function is determined
and invoked on the value returned by the predicate.
This is useful for the common situation encountered in LISP:
.sp
.nofill
.spw 13
.block 2
1(COND ((SETQ IT (GET X 'PROPERTY)) (HACK IT))
...)0
.spw 16
.adjust
.sp
which in SCHEME can be rendered without using a variable global to the 1COND0:
.sp
.nofill
.spw 13
.block 3
1(COND ((GET X 'PROPERTY)
=> (LAMBDA (IT) (HACK IT)))
...)0
.spw 16
.adjust
.sp
or, in this specific instance, simply as:
.sp
.nofill
.spw 13
.block 2
1(COND ((GET X 'PROPERTY) => HACK)
...)0
.spw 16
.adjust
.sp
.sp 2
.block 5
.un 3
1(OR x1 x2 ... xn)0
.sp
.nofill
.spw 13
1 (OR) 41 'NIL
(OR x) 41 x
(OR x . r) 41 (COND (x) (T (OR . r)))0
.spw 16
.adjust
.sp
This standard LISP primitive evaluates the forms 1xi0 in order,
returning the first non-1NIL0 value (and ignoring all following forms).
If all forms produce 1NIL0, then 1NIL0 is returned
{Note Tail-Recursive 1OR0}.
.sp 2
.block 5
.un 3
1(AND x1 x2 ... xn)0
.sp
.nofill
.spw 13
1 (AND) 41 'T
(AND x) 41 x
(AND x . r) 41 (COND (x (AND . r)))0
.spw 16
.adjust
.sp
This standard LISP primitive evaluates the forms 1xi0 in order.
If any form produces 1NIL0, then 1NIL0 is returned, and succeeding forms 1xi0
are ignored. If all forms produce non-1NIL0 values, the value of the
last is returned
{Note Tail-Recursive 1AND0}.
.sp 2
.block 10
.un 3
1(AMAPCAR f x1 x2 ... xn)0
.sp
.nofill
.spw 13
.block 8
41 (DO ((FN f)
(V1 x1 (CDR V1))
(V2 x2 (CDR V2))
...
(Vn xn (CDR Vn))
(Q 'NIL (CONS (FN (CAR V1) (CAR V2) ... (CAR Vn)) Q)))
((OR (NULL V1) (NULL V2) ... (NULL Vn))
(NREVERSE Q)))0
.spw 16
.adjust
.sp
1AMAPCAR0 is analogous to the MacLISP 1MAPCAR0 function. The function 1f0,
a function of 1n0 arguments,
is mapped simultaneously down the lists 1x10, 1x20, ..., 1xn0;
that is, 1f0 is applied to tuples of successive elements of the lists.
The values returned by 1f0 are collected and returned as a list.
Note that 1AMAPCAR0 of a fixed number of arguments could easily
be written as a function in SCHEME. It is a syntactic extension
only so that it may accommodate any number of arguments,
which saves the trouble of defining an entire set of primitive
functions 1AMAPCAR10, 1AMAPCAR20, ... where 1AMAPCARn0 takes 1n+10 arguments.
.sp 2
.block 10
.un 3
1(AMAPLIST f x1 x2 ... xn)0
.sp
.nofill
.spw 13
.block 8
41 (DO ((FN f)
(V1 x1 (CDR V1))
(V2 x2 (CDR V2))
...
(Vn xn (CDR Vn))
(Q 'NIL (CONS (FN V1 V2 ... Vn) Q)))