# embeddings_queue

created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6801792
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "         0))   This won't work in an applicative-order language because both the usual value and the exceptional value will be evaluated before unless  is called (compare exercise 1.6). An advantage of lazy evaluation is that some procedures, such as unless , can do useful computation even if evaluation of some of their ar guments would produce errors or would not terminate. If the body of a procedure is entered before an ar gument has been evaluated we say that the procedure is non-strict  in that ar gument. If the ar gument is evaluated before the body of the procedure is entered we say that the procedure is strict  in that ar gument.33 In a purely applicative-order language, all procedures are strict in each ar gument. In a purely normal- order language, all compound procedures are non-strict in each ar gument, and primitive procedures may be either strict or non-strict. There are also languages (see exercise 4.31) that give programmers detailed control over the strictness of the procedures they define. A "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6802816s
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "striking example of a procedure that can usefully be made non-strict is cons  (or, in general, almost any constructor for data structures). One can do useful computation, combining elements to form data structures and operating on the resulting data structures, even if the values of the elements are not known. It makes perfect sense, for instance, to compute the length of a list without knowing the values of the individual elements in the list. We will exploit this idea in section 4.2.3  to implement the streams of chapter 3 as lists formed of non-strict cons  pairs.Exer cise 4.25.   Suppose that (in ordinary applicative-order Scheme) we define unless  as shown above and then define factorial  in terms of unless  as (define (factorial n)     (unless (= n 1)             (* n (factorial (- n 1)))             1))   What happens if we attempt to evaluate (factorial 5) ? Will our definitions work in a normal-order language? Exer cise 4.26.   Ben Bitdiddle and Alyssa P . Hacker disagree over the importance of lazy "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6803840e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "evaluation for implementing things such as unless . Ben points out that it's possible to implement unless  in applicative order as a special form. Alyssa counters that, if one did that, unless  would be merely syntax, not a procedure that could be used in conjunction with higher -order procedures. Fill in the details on both sides of the ar gument. Show how to implement unless  as a derived expression (like cond  or let), and give an example of a situation where it might be useful to have unless  available as a procedure, rather than as a special form. 4.2.2  An Interpr eter with Lazy Evaluation In this section we will implement a normal-order language that is the same as Scheme except that compound procedures are non-strict in each argument. Primitive procedures will still be strict. It is not dif ficult to modify the evaluator of section 4.1.1  so that the language it interprets behaves this way . Almost all the required changes center around procedure application. The basic idea is that, when applying a pr"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6804864o
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ocedure, the interpreter must determine which ar guments are to be evaluated and which are to be delayed. The delayed ar guments are not evaluated; instead, they are transformed into objects called thunk s.34 The thunk must contain the information required to produce the value of the ar gument when it is needed, as if it had been evaluated at the time of the application. Thus, thethunk must contain the ar gument expression and the environment in which the procedure application is being evaluated. The process of evaluating the expression in a thunk is called forcing.35 In general, a thunk will be forced only when its value is needed: when it is passed to a primitive procedure that will use the value of the thunk; when it is the value of a predicate of a conditional; and when it is the value of an operator that is about to be applied as a procedure. One design choice we have available is whether or not to memoize  thunks, as we did with delayed objects in section 3.5.1 . With memoization, the first time a thunk"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6805888
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " is forced, it stores the value that is computed. Subsequent forcings simply return the stored value without repeating the computation. We'll make our interpreter memoize, because this is more ef ficient for many applications. There are tricky considerations here, however .36 Modifying the evaluator The main dif ference between the lazy evaluator and the one in section 4.1 is in the handling of procedure applications in eval  and apply . The application?  clause of eval  becomes ((application? exp)    (apply (actual-value (operator exp) env)           (operands exp)           env))   This is almost the same as the application?  clause of eval  in section 4.1.1 . For lazy evaluation, however , we call apply  with the operand expressions, rather than the ar guments produced by evaluating them. Since we will need the environment to construct thunks if the ar guments are to be delayed, we must pass this as well. We still evaluate the operator , because apply  needs the actual procedure to be applied in order to d"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6806912i
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ispatch on its type (primitive versus compound) and apply it. Whenever we need the actual value of an expression, we use(define (actual-value exp env)     (force-it (eval exp env)))   instead of just eval , so that if the expression's value is a thunk, it will be forced. Our new version of apply  is also almost the same as the version in section 4.1.1 . The dif ference is that eval  has passed in unevaluated operand expressions: For primitive procedures (which are strict), we evaluate all the arguments before applying the primitive; for compound procedures (which are non-strict) we delay all the ar guments before applying the procedure. (define (apply procedure arguments env)     (cond ((primitive-procedure? procedure)            (apply-primitive-procedure             procedure             (list-of-arg-values arguments env)))  ; changed           ((compound-procedure? procedure)            (eval-sequence             (procedure-body procedure)             (extend-environment              (procedure-parameters "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6807936p
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "procedure)              (list-of-delayed-args arguments env) ; changed              (procedure-environment procedure))))           (else            (error             \"Unknown procedure type -- APPLY\" procedure))))   The procedures that process the ar guments are just like list-of-values from section 4.1.1 , except that list-of-delayed-args  delays the arguments instead of evaluating them, and list-of-arg-values  uses actual-value  instead of eval : (define (list-of-arg-values exps env)     (if (no-operands? exps)         '()         (cons (actual-value (first-operand exps) env)               (list-of-arg-values (rest-operands exps)                                  env))))   (define (list-of-delayed-args exps env)     (if (no-operands? exps)         '()         (cons (delay-it (first-operand exps) env)               (list-of-delayed-args (rest-operands exps)                                     env))))   The other place we must change the evaluator is in the handling of if, where we must use actual-value  inst"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6808960e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ead of eval  to get the value of the predicate expression before testing whether it is true or false: (define (eval-if exp env)     (if (true? (actual-value (if-predicate exp) env))         (eval (if-consequent exp) env)         (eval (if-alternative exp) env)))   Finally , we must change the driver-loop  procedure (section 4.1.4 ) to use actual-value  instead of eval , so that if a delayed value is propagated back to the read-eval-print loop, it will be forced before being printed. We also change the prompts to indicate that this is the lazy evaluator: (define input-prompt \";;; L-Eval input:\")   (define output-prompt \";;; L-Eval value:\")   (define (driver-loop)     (prompt-for-input input-prompt)     (let ((input (read)))       (let ((output              (actual-value input the-global-environment)))         (announce-output output-prompt)         (user-print output)))     (driver-loop))   With these changes made, we can start the evaluator and test it. The successful evaluation of the try expression discusse"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6809984d
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "d in section 4.2.1 indicates that the interpreter is performing lazy evaluation:(define the-global-environment (setup-environment))   (driver-loop)   ;;; L-Eval input:   (define (try a b)     (if (= a 0) 1 b))   ;;; L-Eval value:   ok  ;;; L-Eval input:   (try 0 (/ 1 0))   ;;; L-Eval value:   1  Repr esenting thunks Our evaluator must arrange to create thunks when procedures are applied to arguments and to force these thunks later . A thunk must package an expression together with the environment, so that the ar gument can be produced later . To force the thunk, we simply extract the expression and environment from the thunk and evaluate the expression in the environment. We use actual-value  rather than eval  so that in case the value of the expression is itself a thunk, we will force that, and so on, until we reach something that is not a thunk: (define (force-it obj)     (if (thunk? obj)         (actual-value (thunk-exp obj) (thunk-env obj))         obj))   One easy way to package an expression with an env"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6811008i
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ironment is to make a list containing the expression and the environment. Thus, we create a thunk as follows: (define (delay-it exp env)     (list 'thunk exp env))     (define (thunk? obj)     (tagged-list? obj 'thunk))    (define (thunk-exp thunk) (cadr thunk))     (define (thunk-env thunk) (caddr thunk))   Actually , what we want for our interpreter is not quite this, but rather thunks that have been memoized. When a thunk is forced, we will turn it into an evaluated thunk by replacing the stored expression with its value and changing the thunk  tag so that it can be recognized as already evaluated.37 (define (evaluated-thunk? obj)     (tagged-list? obj 'evaluated-thunk))     (define (thunk-value evaluated-thunk) (cadr evaluated-thunk))   (define (force-it obj)     (cond ((thunk? obj)            (let ((result (actual-value                           (thunk-exp obj)                           (thunk-env obj))))              (set-car! obj 'evaluated-thunk)              (set- car! (cdr obj) result)  ; replace ex"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6812032p
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "p with its value              (set-cdr! (cdr obj) '())     ; forget unneeded env             result))           ((evaluated-thunk? obj)            (thunk-value obj))           (else obj)))   Notice that the same delay-it  procedure works both with and without memoization. Exer cise 4.27.   Suppose we type in the following definitions to the lazy evaluator: (define count 0)   (define (id x)     (set! count (+ count 1))     x) Give the missing values in the following sequence of interactions, and explain your answers.38 (define w (id (id 10)))   ;;; L-Eval input:   count  ;;; L-Eval value:   <response >  ;;; L-Eval input:   w  ;;; L-Eval value:   <response >  ;;; L-Eval input:   count  ;;; L-Eval value:   <response >  Exer cise 4.28.   Eval  uses actual-value  rather than eval  to evaluate the operator before passing it to apply , in order to force the value of the operator . Give an example that demonstrates the need for this forcing. Exer cise 4.29.   Exhibit a program that you would expect to run much more s"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6813056l
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "lowly without memoization than with memoization. Also, consider the following interaction, where the id procedure is defined as in exercise 4.27 and count  starts at 0: (define (square x)     (* x x))   ;;; L-Eval input:   (square (id 10))   ;;; L-Eval value:   <response >  ;;; L-Eval input:   count  ;;; L-Eval value:   <response >  Give the responses both when the evaluator memoizes and when it does not.Exer cise 4.30.   Cy D. Fect, a reformed C programmer , is worried that some side ef fects may never take place, because the lazy evaluator doesn't force the expressions in a sequence. Since the value of an expression in a sequence other than the last one is not used (the expression is there only for its ef fect, such as assigning to a variable or printing), there can be no subsequent use of this value (e.g., as an ar gument to a primitive procedure) that will cause it to be forced. Cy thus thinks that when evaluating sequences, we must force all expressions in the sequence except the final one. He proposes t"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6814080o
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "o modify eval-sequence  from section 4.1.1  to use actual-value  rather than eval : (define (eval-sequence exps env)     (cond ((last-exp? exps) (eval (first-exp exps) env))           (else (actual-value (first-exp exps) env)                 (eval-sequence (rest-exps exps) env))))   a. Ben Bitdiddle thinks Cy is wrong. He shows Cy the for-each  procedure described in exercise 2.23, which gives an important example of a sequence with side ef fects: (define (for-each proc items)     (if (null? items)         'done         (begin (proc (car items))                (for-each proc (cdr items)))))   He claims that the evaluator in the text (with the original eval-sequence ) handles this correctly: ;;; L-Eval input:   (for-each (lambda (x) (newline) (display x))             (list 57 321 88))   57  321  88  ;;; L-Eval value:   done Explain why Ben is right about the behavior of for-each . b. Cy agrees that Ben is right about the for-each  example, but says that that's not the kind of program he was thinking about when"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6815104
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " he proposed his change to eval-sequence . He defines the following two procedures in the lazy evaluator: (define (p1 x)     (set! x (cons x '(2)))     x)    (define (p2 x)     (define (p e)       e      x)    (p (set! x (cons x '(2)))))   What are the values of (p1 1)  and (p2 1)  with the original eval- sequence ? What would the values be with Cy's proposed change to eval- sequence ? c. Cy also points out that changing eval-sequence  as he proposes does not affect the behavior of the example in part a. Explain why this is true. d. How do you think sequences ought to be treated in the lazy evaluator? Do you like Cy's approach, the approach in the text, or some other approach? Exer cise 4.31.   The approach taken in this section is somewhat unpleasant, because it makes an incompatible change to Scheme. It might be nicer to implement lazy evaluation as an upwar d-compatible extension , that is, so that ordinary Scheme programs will work as before. We can do this by extending the syntax of procedure declaration"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6816128s
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "s to let the user control whether or not ar guments are to be delayed. While we're at it, we may as well also give the user the choice between delaying with and without memoization. For example, the definition(define (f a (b lazy) c (d lazy-memo))     ...)  would define f to be a procedure of four ar guments, where the first and third ar guments are evaluated when the procedure is called, the second argument is delayed, and the fourth ar gument is both delayed and memoized. Thus, ordinary procedure definitions will produce the same behavior as ordinary Scheme, while adding the lazy-memo  declaration to each parameter of every compound procedure will produce the behavior of the lazy evaluator defined in this section. Design and implement the changes required to produce such an extension to Scheme. You will have to implement new syntax procedures to handle the new syntax for define . You must also arrange for eval  or apply  to determine when ar guments are to be delayed, and to force or delay ar guments accord"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6817152i
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ingly , and you must arrange for forcing to memoize or not, as appropriate. 4.2.3  Str eams as Lazy Lists In section 3.5.1 , we showed how to implement streams as delayed lists. We introduced special forms delay  and cons-stream , which allowed us to construct a \u201cpromise\u201d to compute the cdr of a stream, without actually fulfilling that promise until later . We could use this general technique of introducing special forms whenever we need more control over the evaluation process, but this is awkward. For one thing, a special form is not a first-class object like a procedure, so we cannot use it together with higher -order procedures.39 Additionally , we were forced to create streams as a new kind of data object similar but not identical to lists, and this required us to reimplement many ordinary list operations ( map, append , and so on) for use with streams. With lazy evaluation, streams and lists can be identical, so there is no need for special forms or for separate list and stream operations. All we need t"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6818176o
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "o do is to arrange matters so that cons  is non-strict. One way to accomplish this is to extend the lazy evaluator to allow for non-strict primitives, and to implement cons  as one of these. An easier way is to recall (section 2.1.3 ) that there is no fundamental need to implement cons  as a primitive at all. Instead, we can represent pairs as procedures:40(define (cons x y)     (lambda (m) (m x y)))   (define (car z)     (z (lambda (p q) p)))   (define (cdr z)     (z (lambda (p q) q)))   In terms of these basic operations, the standard definitions of the list operations will work with infinite lists (streams) as well as finite ones, and the stream operations can be implemented as list operations. Here are some examples: (define (list-ref items n)     (if (= n 0)         (car items)         (list-ref (cdr items) (- n 1))))   (define (map proc items)     (if (null? items)         '()         (cons (proc (car items))               (map proc (cdr items)))))   (define (scale-list items factor)     (map (lambda (x"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6819200)
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": ") (* x factor))          items))   (define (add-lists list1 list2)     (cond ((null? list1) list2)           ((null? list2) list1)           (else (cons (+ (car list1) (car list2))                       (add-lists (cdr list1) (cdr list2))))))   (define ones (cons 1 ones))   (define integers (cons 1 (add-lists ones integers)))   ;;; L-Eval input:   (list-ref integers 17)   ;;; L-Eval value:   18 Note that these lazy lists are even lazier than the streams of chapter 3: The car of the list, as well as the cdr, is delayed.41 In fact, even accessing the car or cdr of a lazy pair need not force the value of a list element. The value will be forced only when it is really needed \u2013 e.g., for use as the argument of a primitive, or to be printed as an answer . Lazy pairs also help with the problem that arose with streams in section 3.5.4 , where we found that formulating stream models of systems with loops may require us to sprinkle our programs with explicit delay operations, beyond the ones supplied by cons-stream . W"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6820224i
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ith lazy evaluation, all ar guments to procedures are delayed uniformly . For instance, we can implement procedures to integrate lists and solve dif ferential equations as we originally intended in section 3.5.4 : (define (integral integrand initial-value dt)     (define int       (cons initial-value             (add-lists (scale-list integrand dt)                       int)))     int)  (define (solve f y0 dt)     (define y (integral dy y0 dt))     (define dy (map f y))     y)  ;;; L-Eval input:   (list-ref (solve (lambda (x) x) 1 0.001) 1000)   ;;; L-Eval value:   2.716924 Exer cise 4.32.   Give some examples that illustrate the dif ference between the streams of chapter 3 and the \u201clazier\u201d lazy lists described in this section. How can you take advantage of this extra laziness? Exer cise 4.33.   Ben Bitdiddle tests the lazy list implementation given above by evaluating the expression (car '(a b c))  To his surprise, this produces an error . After some thought, he realizes that the \u201clists\u201d obtained by reading "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6821248i
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "in quoted expressions are dif ferent from the lists manipulated by the new definitions of cons , car, and cdr. Modify the evaluator's treatment of quoted expressions so that quoted lists typed at the driver loop will produce true lazy lists. Exer cise 4.34.   Modify the driver loop for the evaluator so that lazy pairs and lists will print in some reasonable way . (What are you going to do about infinite lists?) You may also need to modify the representation of lazy pairs so that the evaluator can identify them in order to print them. 31 Snarf: \u201cT o grab, especially a lar ge document or file for the purpose of using it either with or without the owner's permission.\u201d Snarf down: \u201cT o snarf, sometimes with the connotation of absorbing, processing, or understanding.\u201d (These definitions were snarfed from Steele et al. 1983. See also Raymond 1993.) 32 The dif ference between the \u201clazy\u201d terminology and the \u201cnormal-order\u201d terminology is somewhat fuzzy . Generally , \u201clazy\u201d refers to the mechanisms of particular evalua"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6822272t
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "tors, while \u201cnormal-order\u201d refers to the semantics of languages, independent of any particular evaluation strategy . But this is not a hard-and-fast distinction, and the two terminologies are often used interchangeably . 33 The \u201cstrict\u201d versus \u201cnon-strict\u201d terminology means essentially the same thing as \u201capplicative- order\u201d versus \u201cnormal-order ,\u201d except that it refers to individual procedures and ar guments rather than to the language as a whole. At a conference on programming languages you might hear someone say , \u201cThe normal-order language Hassle has certain strict primitives. Other procedures take their arguments by lazy evaluation.\u201d 34 The word thunk  was invented by an informal working group that was discussing the implementation of call-by-name in Algol 60. They observed that most of the analysis of (\u201cthinking about\u201d) the expression could be done at compile time; thus, at run time, the expression would already have been \u201cthunk\u201d about (Ingerman et al. 1960). 35 This is analogous to the use of force  on "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6823296t
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "the delayed objects that were introduced in chapter 3 to represent streams. The critical dif ference between what we are doing here and what we did in chapter 3 is that we are building delaying and forcing into the evaluator , and thus making this uniform and automatic throughout the language. 36 Lazy evaluation combined with memoization is sometimes referred to as call-by-need  argument passing, in contrast to call-by-name  argument passing. (Call-by-name, introduced in Algol 60, is similar to non-memoized lazy evaluation.) As language designers, we can build our evaluator to memoize, not to memoize, or leave this an option for programmers (exercise 4.31). As you might expect from chapter 3, these choices raise issues that become both subtle and confusing in the presence of assignments. (See exercises 4.27 and 4.29.) An excellent article by Clinger (1982) attempts to clarify the multiple dimensions of confusion that arise here.37 Notice that we also erase the env from the thunk once the expression's value ha"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6824320s
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "s been computed. This makes no dif ference in the values returned by the interpreter . It does help save space, however , because removing the reference from the thunk to the env once it is no longer needed allows this structure to be garbage-collected  and its space recycled, as we will discuss in section 5.3. Similarly , we could have allowed unneeded environments in the memoized delayed objects of section 3.5.1  to be garbage-collected, by having memo-proc  do something like (set! proc '())  to discard the procedure proc  (which includes the environment in which the delay  was evaluated) after storing its value. 38 This exercise demonstrates that the interaction between lazy evaluation and side ef fects can be very confusing. This is just what you might expect from the discussion in chapter 3. 39 This is precisely the issue with the unless  procedure, as in exercise 4.26. 40 This is the procedural representation described in exercise 2.4. Essentially any procedural representation (e.g., a message-passing i"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6825344m
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "mplementation) would do as well. Notice that we can install these definitions in the lazy evaluator simply by typing them at the driver loop. If we had originally included cons , car, and cdr as primitives in the global environment, they will be redefined. (Also see exercises 4.33 and 4.34.) 41 This permits us to create delayed versions of more general kinds of list structures, not just sequences. Hughes 1990 discusses some applications of \u201clazy trees.\u201d4.3  Variations on a Scheme \u2013 Nondeterministic Computing In this section, we extend the Scheme evaluator to support a programming paradigm called nondeterministic computing  by building into the evaluator a facility to support automatic search. This is a much more profound change to the language than the introduction of lazy evaluation in section 4.2. Nondeterministic computing, like stream processing, is useful for \u201cgenerate and test\u201d applications. Consider the task of starting with two lists of positive integers and finding a pair of integers \u2013 one from the f"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6826368i
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "irst list and one from the second list \u2013 whose sum is prime. We saw how to handle this with finite sequence operations in section 2.2.3  and with infinite streams in section 3.5.3 . Our approach was to generate the sequence of all possible pairs and filter these to select the pairs whose sum is prime. Whether we actually generate the entire sequence of pairs first as in chapter 2, or interleave the generating and filtering as in chapter 3, is immaterial to the essential image of how the computation is or ganized. The nondeterministic approach evokes a dif ferent image. Imagine simply that we choose (in some way) a number from the first list and a number from the second list and require (using some mechanism) that their sum be prime. This is expressed by following procedure: (define (prime-sum-pair list1 list2)     (let ((a (an-element-of list1))           (b (an-element-of list2)))       (require (prime? (+ a b)))       (list a b)))   It might seem as if this procedure merely restates the problem, rather than"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6827392
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " specifying a way to solve it. Nevertheless, this is a legitimate nondeterministic program.42 The key idea here is that expressions in a nondeterministic language can have more than one possible value. For instance, an-element-of  mightreturn any element of the given list. Our nondeterministic program evaluator will work by automatically choosing a possible value and keeping track of the choice. If a subsequent requirement is not met, the evaluator will try a dif ferent choice, and it will keep trying new choices until the evaluation succeeds, or until we run out of choices. Just as the lazy evaluator freed the programmer from the details of how values are delayed and forced, the nondeterministic program evaluator will free the programmer from the details of how choices are made. It is instructive to contrast the dif ferent images of time evoked by nondeterministic evaluation and stream processing. Stream processing uses lazy evaluation to decouple the time when the stream of possible answers is assembled fro"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6828416m
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "m the time when the actual stream elements are produced. The evaluator supports the illusion that all the possible answers are laid out before us in a timeless sequence. With nondeterministic evaluation, an expression represents the exploration of a set of possible worlds, each determined by a set of choices. Some of the possible worlds lead to dead ends, while others have useful values. The nondeterministic program evaluator supports the illusion that time branches, and that our programs have dif ferent possible execution histories. When we reach a dead end, we can revisit a previous choice point and proceed along a dif ferent branch. The nondeterministic program evaluator implemented below is called the amb evaluator because it is based on a new special form called amb. We can type the above definition of prime-sum-pair  at the amb evaluator driver loop (along with definitions of prime? , an-element-of , and require ) and run the procedure as follows: ;;; Amb-Eval input:   (prime-sum-pair '(1 3 5 8) '(20 35"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6829440
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " 110))   ;;; Starting a new problem   ;;; Amb-Eval value:   (3 20)  The value returned was obtained after the evaluator repeatedly chose elements from each of the lists, until a successful choice was made.Section 4.3.1  introduces amb and explains how it supports nondeterminism through the evaluator's automatic search mechanism. Section 4.3.2  presents examples of nondeterministic programs, and section 4.3.3  gives the details of how to implement the amb evaluator by modifying the ordinary Scheme evaluator . 4.3.1  Amb and Sear ch To extend Scheme to support nondeterminism, we introduce a new special form called amb.43 The expression (amb <e1> <e2> ... <en>) returns the value of one of the n expressions < ei> \u201cambiguously .\u201d For example, the expression (list (amb 1 2 3) (amb 'a 'b))   can have six possible values: (1 a)(1 b)(2 a)(2 b)(3 a)(3 b) Amb with a single choice produces an ordinary (single) value. Amb with no choices \u2013 the expression (amb)  \u2013 is an expression with no acceptable values. Operationally ,"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6830464
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " we can think of (amb)  as an expression that when evaluated causes the computation to \u201cfail\u201d: The computation aborts and no value is produced. Using this idea, we can express the requirement that a particular predicate expression p must be true as follows: (define (require p)     (if (not p) (amb)))   With amb and require , we can implement the an-element-of  procedure used above: (define (an-element-of items)     (require (not (null? items)))     (amb (car items) (an-element-of (cdr items))))  An-element-of  fails if the list is empty . Otherwise it ambiguously returns either the first element of the list or an element chosen from the rest of the list. We can also express infinite ranges of choices. The following procedure potentially returns any integer greater than or equal to some given n: (define (an-integer-starting-from n)     (amb n (an-integer-starting-from (+ n 1))))   This is like the stream procedure integers-starting-from  described in section 3.5.2 , but with an important dif ference: The strea"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6831488m
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "m procedure returns an object that represents the sequence of all integers beginning with n, whereas the amb procedure returns a single integer .44 Abstractly , we can imagine that evaluating an amb expression causes time to split into branches, where the computation continues on each branch with one of the possible values of the expression. We say that amb represents a nondeterministic choice point . If we had a machine with a suf ficient number of processors that could be dynamically allocated, we could implement the search in a straightforward way . Execution would proceed as in a sequential machine, until an amb expression is encountered. At this point, more processors would be allocated and initialized to continue all of the parallel executions implied by the choice. Each processor would proceed sequentially as if it were the only choice, until it either terminates by encountering a failure, or it further subdivides, or it finishes.45 On the other hand, if we have a machine that can execute only one proc"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6832512e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ess (or a few concurrent processes), we must consider the alternatives sequentially . One could imagine modifying an evaluator to pick at random a branch to follow whenever it encounters a choice point. Random choice, however , can easily lead to failing values. We might try running the evaluator over and over , making random choices and hoping to find a non- failing value, but it is better to systematically sear ch all possible execution paths. The amb evaluator that we will develop and work with in this section implements a systematic search as follows: When the evaluator encounters an application of amb, it initially selects the first alternative. This selection may itself lead to a further choice. The evaluator will always initiallychoose the first alternative at each choice point. If a choice results in a failure, then the evaluator automagically46 backtracks  to the most recent choice point and tries the next alternative. If it runs out of alternatives at any choice point, the evaluator will back up to "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6833536t
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "the previous choice point and resume from there. This process leads to a search strategy known as depth-first sear ch or chronological backtracking .47 Driver loop The driver loop for the amb evaluator has some unusual properties. It reads an expression and prints the value of the first non-failing execution, as in the prime-sum-pair  example shown above. If we want to see the value of the next successful execution, we can ask the interpreter to backtrack and attempt to generate a second non-failing execution. This is signaled by typing the symbol try-again . If any expression except try-again  is given, the interpreter will start a new problem, discarding the unexplored alternatives in the previous problem. Here is a sample interaction: ;;; Amb-Eval input:   (prime-sum-pair '(1 3 5 8) '(20 35 110))   ;;; Starting a new problem   ;;; Amb-Eval value:   (3 20)  ;;; Amb-Eval input:   try-again   ;;; Amb-Eval value:   (3 110)  ;;; Amb-Eval input:   try-again   ;;; Amb-Eval value:   (8 35)  ;;; Amb-Eval input:   t"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6834560r
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ry-again   ;;; There are no more values of   (prime-sum-pair (quote (1 3 5 8)) (quote (20 35 110)))   ;;; Amb-Eval input:   (prime-sum-pair '(19 27 30) '(11 36 58))  ;;; Starting a new problem   ;;; Amb-Eval value:   (30 11)  Exer cise 4.35.   Write a procedure an-integer-between  that returns an integer between two given bounds. This can be used to implement a procedure that finds Pythagorean triples, i.e., triples of integers ( i,j,k) between the given bounds such that i < j and i2 + j2 = k2, as follows: (define (a-pythagorean-triple-between low high)     (let ((i (an-integer-between low high)))       (let ((j (an-integer-between i high)))         (let ((k (an-integer-between j high)))           (require (= (+ (* i i) (* j j)) (* k k)))           (list i j k)))))   Exer cise 4.36.   Exercise 3.69 discussed how to generate the stream of all Pythagorean triples, with no upper bound on the size of the integers to be searched. Explain why simply replacing an-integer-between  by an- integer-starting-from  in the"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6835584
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " procedure in exercise 4.35 is not an adequate way to generate arbitrary Pythagorean triples. Write a procedure that actually will accomplish this. (That is, write a procedure for which repeatedly typing try-again  would in principle eventually generate all Pythagorean triples.) Exer cise 4.37.   Ben Bitdiddle claims that the following method for generating Pythagorean triples is more ef ficient than the one in exercise 4.35. Is he correct? (Hint: Consider the number of possibilities that must be explored.) (define (a-pythagorean-triple-between low high)     (let ((i (an-integer-between low high))           (hsq (* high high)))       (let ((j (an-integer-between i high)))         (let ((ksq (+ (* i i) (* j j))))           (require (>= hsq ksq))           (let ((k (sqrt ksq)))            (require (integer? k))             (list i j k))))))   4.3.2  Examples of Nondeterministic Pr ograms Section 4.3.3  describes the implementation of the amb evaluator . First, however , we give some examples of how it can be us"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6836608e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ed. The advantage of nondeterministic programming is that we can suppress the details of how search is carried out, thereby expressing our programs at a higher level of abstraction. Logic Puzzles The following puzzle (taken from Dinesman 1968) is typical of a lar ge class of simple logic puzzles: Baker , Cooper , Fletcher , Miller , and Smith live on dif ferent floors of an apartment house that contains only five floors. Baker does not live on the top floor . Cooper does not live on the bottom floor . Fletcher does not live on either the top or the bottom floor . Miller lives on a higher floor than does Cooper . Smith does not live on a floor adjacent to Fletcher's. Fletcher does not live on a floor adjacent to Cooper's. Where does everyone live? We can determine who lives on each floor in a straightforward way by enumerating all the possibilities and imposing the given restrictions:48 (define (multiple-dwelling)     (let ((baker (amb 1 2 3 4 5))           (cooper (amb 1 2 3 4 5))           (fletcher (amb 1 2"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6837632
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " 3 4 5))           (miller (amb 1 2 3 4 5))           (smith (amb 1 2 3 4 5)))       (require        (distinct? (list baker cooper fletcher miller smith)))       (require (not (= baker 5)))       (require (not (= cooper 1)))      (require (not (= fletcher 5)))       (require (not (= fletcher 1)))       (require (> miller cooper))       (require (not (= (abs (- smith fletcher)) 1)))       (require (not (= (abs (- fletcher cooper)) 1)))       (list (list 'baker baker)             (list 'cooper cooper)             (list 'fletcher fletcher)             (list 'miller miller)             (list 'smith smith))))   Evaluating the expression (multiple-dwelling)  produces the result ((baker 3) (cooper 2) (fletcher 4) (miller 5) (smith 1))   Although this simple procedure works, it is very slow . Exercises 4.39 and 4.40 discuss some possible improvements. Exer cise 4.38.   Modify the multiple-dwelling procedure to omit the requirement that Smith and Fletcher do not live on adjacent floors. How many solutions are there to"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6838656
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " this modified puzzle? Exer cise 4.39.   Does the order of the restrictions in the multiple-dwelling procedure af fect the answer? Does it af fect the time to find an answer? If you think it matters, demonstrate a faster program obtained from the given one by reordering the restrictions. If you think it does not matter , argue your case. Exer cise 4.40.   In the multiple dwelling problem, how many sets of assignments are there of people to floors, both before and after the requirement that floor assignments be distinct? It is very inef ficient to generate all possible assignments of people to floors and then leave it to backtracking to eliminate them. For example, most of the restrictions depend on only one or two of the person-floor variables, and can thus be imposed before floors have been selected for all the people. Write and demonstrate a much more ef ficient nondeterministic procedure that solves this problem based upon generating only those possibilities that are notalready ruled out by previous restri"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6839680c
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ctions. (Hint: This will require a nest of let expressions.) Exer cise 4.41.   Write an ordinary Scheme program to solve the multiple dwelling puzzle. Exer cise 4.42.   Solve the following \u201cLiars\u201d puzzle (from Phillips 1934): Five schoolgirls sat for an examination. Their parents \u2013 so they thought \u2013 showed an undue degree of interest in the result. They therefore agreed that, in writing home about the examination, each girl should make one true statement and one untrue one. The following are the relevant passages from their letters: Betty: \u201cKitty was second in the examination. I was only third.\u201d Ethel: \u201cY ou'll be glad to hear that I was on top. Joan was second.\u201d Joan: \u201cI was third, and poor old Ethel was bottom.\u201d Kitty: \u201cI came out second. Mary was only fourth.\u201d Mary: \u201cI was fourth. Top place was taken by Betty .\u201d What in fact was the order in which the five girls were placed? Exer cise 4.43.   Use the amb evaluator to solve the following puzzle:49 Mary Ann Moore's father has a yacht and so has each of his f"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6840704o
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "our friends: Colonel Downing, Mr . Hall, Sir Barnacle Hood, and Dr . Parker . Each of the five also has one daughter and each has named his yacht after a daughter of one of the others. Sir Barnacle's yacht is the Gabrielle, Mr . Moore owns the Lorna; Mr . Hall the Rosalind. The Melissa, owned by Colonel Downing, is named after Sir Barnacle's daughter . Gabrielle's father owns the yacht that is named after Dr . Parker's daughter . Who is Lorna's father? Try to write the program so that it runs ef ficiently (see exercise 4.40). Also determine how many solutions there are if we are not told that Mary Ann's last name is Moore.Exer cise 4.44.   Exercise 2.42 described the \u201ceight-queens puzzle\u201d of placing queens on a chessboard so that no two attack each other . Write a nondeterministic program to solve this puzzle. Parsing natural language Programs designed to accept natural language as input usually start by attempting to parse  the input, that is, to match the input against some grammatical structure. For exampl"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6841728e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "e, we might try to recognize simple sentences consisting of an article followed by a noun followed by a verb, such as \u201cThe cat eats.\u201d To accomplish such an analysis, we must be able to identify the parts of speech of individual words. We could start with some lists that classify various words:50 (define nouns '(noun student professor cat class))   (define verbs '(verb studies lectures eats sleeps))   (define articles '(article the a))   We also need a grammar , that is, a set of rules describing how grammatical elements are composed from simpler elements. A very simple grammar might stipulate that a sentence always consists of two pieces \u2013 a noun phrase followed by a verb \u2013 and that a noun phrase consists of an article followed by a noun. With this grammar , the sentence \u201cThe cat eats\u201d is parsed as follows: (sentence (noun-phrase (article the) (noun cat))             (verb eats))   We can generate such a parse with a simple program that has separate procedures for each of the grammatical rules. To parse a sen"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6842752t
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "tence, we identify its two constituent pieces and return a list of these two elements, tagged with the symbol sentence : (define (parse-sentence)     (list 'sentence            (parse-noun-phrase)            (parse-word verbs)))  A noun phrase, similarly , is parsed by finding an article followed by a noun: (define (parse-noun-phrase)     (list 'noun-phrase           (parse-word articles)           (parse-word nouns)))   At the lowest level, parsing boils down to repeatedly checking that the next unparsed word is a member of the list of words for the required part of speech. To implement this, we maintain a global variable *unparsed* , which is the input that has not yet been parsed. Each time we check a word, we require that *unparsed*  must be non-empty and that it should begin with a word from the designated list. If so, we remove that word from *unparsed*  and return the word together with its part of speech (which is found at the head of the list):51 (define (parse-word word-list)     (require (not (null"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6843776?
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "? *unparsed*)))     (require (memq (car *unparsed*) (cdr word-list)))     (let ((found-word (car *unparsed*)))       (set! *unparsed* (cdr *unparsed*))       (list (car word-list) found-word)))   To start the parsing, all we need to do is set *unparsed*  to be the entire input, try to parse a sentence, and check that nothing is left over: (define *unparsed* '())   (define (parse input)     (set! *unparsed* input)     (let ((sent (parse-sentence)))       (require (null? *unparsed*))       sent))   We can now try the parser and verify that it works for our simple test sentence: ;;; Amb-Eval input:   (parse '(the cat eats))  ;;; Starting a new problem   ;;; Amb-Eval value:   (sentence (noun-phrase (article the) (noun cat)) (verb eats))   The amb evaluator is useful here because it is convenient to express the parsing constraints with the aid of require . Automatic search and backtracking really pay of f, however , when we consider more complex grammars where there are choices for how the units can be decomposed."}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6844800
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " Let's add to our grammar a list of prepositions: (define prepositions '(prep for to in by with))   and define a prepositional phrase (e.g., \u201cfor the cat\u201d) to be a preposition followed by a noun phrase: (define (parse-prepositional-phrase)     (list 'prep-phrase           (parse-word prepositions)           (parse-noun-phrase)))   Now we can define a sentence to be a noun phrase followed by a verb phrase, where a verb phrase can be either a verb or a verb phrase extended by a prepositional phrase:52 (define (parse-sentence)     (list 'sentence            (parse-noun-phrase)            (parse-verb-phrase)))   (define (parse-verb-phrase)     (define (maybe-extend verb-phrase)       (amb verb-phrase            (maybe-extend (list 'verb-phrase                                verb-phrase                                (parse-prepositional-phrase)))))     (maybe-extend (parse-word verbs)))  While we're at it, we can also elaborate the definition of noun phrases to permit such things as \u201ca cat in the class.\u201d What we "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6845824u
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "used to call a noun phrase, we'll now call a simple noun phrase, and a noun phrase will now be either a simple noun phrase or a noun phrase extended by a prepositional phrase: (define (parse-simple-noun-phrase)     (list 'simple-noun-phrase           (parse-word articles)           (parse-word nouns)))   (define (parse-noun-phrase)     (define (maybe-extend noun-phrase)       (amb noun-phrase            (maybe-extend (list 'noun-phrase                                noun-phrase                                (parse-prepositional-phrase)))))     (maybe-extend (parse-simple-noun-phrase)))   Our new grammar lets us parse more complex sentences. For example (parse '(the student with the cat sleeps in the class))   produces (sentence    (noun-phrase     (simple-noun-phrase (article the) (noun student))     (prep-phrase (prep with)                  (simple-noun-phrase                   (article the) (noun cat))))    (verb-phrase     (verb sleeps)     (prep-phrase (prep in)                  (simple-noun-phrase      "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6846848
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "             (article the) (noun class)))))   Observe that a given input may have more than one legal parse. In the sentence \u201cThe professor lectures to the student with the cat,\u201d it may be thatthe professor is lecturing with the cat, or that the student has the cat. Our nondeterministic program finds both possibilities: (parse '(the professor lectures to the student with the cat))   produces (sentence    (simple-noun-phrase (article the) (noun professor))    (verb-phrase     (verb-phrase      (verb lectures)      (prep-phrase (prep to)                   (simple-noun-phrase                    (article the) (noun student))))     (prep-phrase (prep with)                  (simple-noun-phrase                   (article the) (noun cat)))))   Asking the evaluator to try again yields (sentence    (simple-noun-phrase (article the) (noun professor))    (verb-phrase     (verb lectures)     (prep-phrase (prep to)                  (noun-phrase                   (simple-noun-phrase                    (article the) (noun st"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6847872u
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "udent))                   (prep-phrase (prep with)                                (simple-noun-phrase                                 (article the) (noun cat)))))))   Exer cise 4.45.   With the grammar given above, the following sentence can be parsed in five dif ferent ways: \u201cThe professor lectures to the student in the class with the cat.\u201d Give the five parses and explain the dif ferences in shades of meaning among them.Exer cise 4.46.   The evaluators in sections 4.1 and 4.2 do not determine what order operands are evaluated in. We will see that the amb evaluator evaluates them from left to right. Explain why our parsing program wouldn't work if the operands were evaluated in some other order . Exer cise 4.47.   Louis Reasoner suggests that, since a verb phrase is either a verb or a verb phrase followed by a prepositional phrase, it would be much more straightforward to define the procedure parse-verb-phrase  as follows (and similarly for noun phrases): (define (parse-verb-phrase)     (amb (parse-word verb"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6848896s
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "s)          (list 'verb-phrase                (parse-verb-phrase)                (parse-prepositional-phrase))))   Does this work? Does the program's behavior change if we interchange the order of expressions in the amb? Exer cise 4.48.   Extend the grammar given above to handle more complex sentences. For example, you could extend noun phrases and verb phrases to include adjectives and adverbs, or you could handle compound sentences.53 Exer cise 4.49.   Alyssa P . Hacker is more interested in generating interesting sentences than in parsing them. She reasons that by simply changing the procedure parse-word  so that it ignores the \u201cinput sentence\u201d and instead always succeeds and generates an appropriate word, we can use the programs we had built for parsing to do generation instead. Implement Alyssa's idea, and show the first half-dozen or so sentences generated.54 4.3.3  Implementing the Amb Evaluator The evaluation of an ordinary Scheme expression may return a value, may never terminate, or may signal an er"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6849920r
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ror . In nondeterministic Scheme the evaluation of an expression may in addition result in the discovery of a dead end, in which case evaluation must backtrack to a previous choice point.The interpretation of nondeterministic Scheme is complicated by this extra case. We will construct the amb evaluator for nondeterministic Scheme by modifying the analyzing evaluator of section 4.1.7 .55 As in the analyzing evaluator , evaluation of an expression is accomplished by calling an execution procedure produced by analysis of that expression. The dif ference between the interpretation of ordinary Scheme and the interpretation of nondeterministic Scheme will be entirely in the execution procedures. Execution pr ocedur es and continuations Recall that the execution procedures for the ordinary evaluator take one argument: the environment of execution. In contrast, the execution procedures in the amb evaluator take three ar guments: the environment, and two procedures called continuation pr ocedur es. The evaluation of a"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6850944n
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "n expression will finish by calling one of these two continuations: If the evaluation results in a value, the success continuation  is called with that value; if the evaluation results in the discovery of a dead end, the failur e continuation  is called. Constructing and calling appropriate continuations is the mechanism by which the nondeterministic evaluator implements backtracking. It is the job of the success continuation to receive a value and proceed with the computation. Along with that value, the success continuation is passed another failure continuation, which is to be called subsequently if the use of that value leads to a dead end. It is the job of the failure continuation to try another branch of the nondeterministic process. The essence of the nondeterministic language is in the fact that expressions may represent choices among alternatives. The evaluation of such an expression must proceed with one of the indicated alternative choices, even though it is not known in advance which choices will l"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6851968e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ead to acceptable results. To deal with this, the evaluator picks one of the alternatives and passes this value to the success continuation. Together with this value, the evaluator constructs and passes along a failure continuation that can be called later to choose a dif ferent alternative.A failure is triggered during evaluation (that is, a failure continuation is called) when a user program explicitly rejects the current line of attack (for example, a call to require  may result in execution of (amb) , an expression that always fails \u2013 see section 4.3.1 ). The failure continuation in hand at that point will cause the most recent choice point to choose another alternative. If there are no more alternatives to be considered at that choice point, a failure at an earlier choice point is triggered, and so on. Failure continuations are also invoked by the driver loop in response to a try- again  request, to find another value of the expression. In addition, if a side-ef fect operation (such as assignment to a va"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6852992r
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "riable) occurs on a branch of the process resulting from a choice, it may be necessary , when the process finds a dead end, to undo the side ef fect before making a new choice. This is accomplished by having the side-ef fect operation produce a failure continuation that undoes the side ef fect and propagates the failure. In summary , failure continuations are constructed by amb expressions \u2013 to provide a mechanism to make alternative choices if the current choice made by the amb expression leads to a dead end; the top-level driver \u2013 to provide a mechanism to report failure when the choices are exhausted; assignments \u2013 to intercept failures and undo assignments during backtracking. Failures are initiated only when a dead end is encountered. This occurs if the user program executes (amb) ; if the user types try-again  at the top-level driver . Failure continuations are also called during processing of a failure: When the failure continuation created by an assignment finishes undoing a side ef fect, it calls the"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6854016
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " failure continuation it intercepted, inorder to propagate the failure back to the choice point that led to this assignment or to the top level. When the failure continuation for an amb runs out of choices, it calls the failure continuation that was originally given to the amb, in order to propagate the failure back to the previous choice point or to the top level. Structur e of the evaluator The syntax- and data-representation procedures for the amb evaluator , and also the basic analyze  procedure, are identical to those in the evaluator of section 4.1.7 , except for the fact that we need additional syntax procedures to recognize the amb special form:56 (define (amb? exp) (tagged-list? exp 'amb))   (define (amb-choices exp) (cdr exp))   We must also add to the dispatch in analyze  a clause that will recognize this special form and generate an appropriate execution procedure: ((amb? exp) (analyze-amb exp))   The top-level procedure ambeval  (similar to the version of eval  given in section 4.1.7 ) analyzes t"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6855040h
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "he given expression and applies the resulting execution procedure to the given environment, together with two given continuations: (define (ambeval exp env succeed fail)     ((analyze exp) env succeed fail))   A success continuation is a procedure of two ar guments: the value just obtained and another failure continuation to be used if that value leads to a subsequent failure. A failure continuation is a procedure of no ar guments. So the general form of an execution procedure is (lambda (env succeed fail)     ;; succeed is (lambda (value fail) ...)   ;; fail is (lambda () ...)    ...)  For example, executing (ambeval < exp>           the-global-environment            (lambda (value fail) value)            (lambda () 'failed))   will attempt to evaluate the given expression and will return either the expression's value (if the evaluation succeeds) or the symbol failed  (if the evaluation fails). The call to ambeval  in the driver loop shown below uses much more complicated continuation procedures, which conti"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6856064n
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "nue the loop and support the try-again  request. Most of the complexity of the amb evaluator results from the mechanics of passing the continuations around as the execution procedures call each other . In going through the following code, you should compare each of the execution procedures with the corresponding procedure for the ordinary evaluator given in section 4.1.7 . Simple expr essions The execution procedures for the simplest kinds of expressions are essentially the same as those for the ordinary evaluator , except for the need to manage the continuations. The execution procedures simply succeed with the value of the expression, passing along the failure continuation that was passed to them. (define (analyze-self-evaluating exp)     (lambda (env succeed fail)       (succeed exp fail)))   (define (analyze-quoted exp)     (let ((qval (text-of-quotation exp)))       (lambda (env succeed fail)         (succeed qval fail))))   (define (analyze-variable exp)    (lambda (env succeed fail)       (succeed (loo"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6857088k
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "kup-variable-value exp env)                fail)))   (define (analyze-lambda exp)     (let ((vars (lambda-parameters exp))           (bproc (analyze-sequence (lambda-body exp))))       (lambda (env succeed fail)         (succeed (make-procedure vars bproc env)                  fail))))   Notice that looking up a variable always \u201csucceeds.\u201d If lookup-variable- value  fails to find the variable, it signals an error , as usual. Such a \u201cfailure\u201d indicates a program bug \u2013 a reference to an unbound variable; it is not an indication that we should try another nondeterministic choice instead of the one that is currently being tried. Conditionals and sequences Conditionals are also handled in a similar way as in the ordinary evaluator . The execution procedure generated by analyze-if  invokes the predicate execution procedure pproc  with a success continuation that checks whether the predicate value is true and goes on to execute either the consequent or the alternative. If the execution of pproc  fails, the original "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6858112f
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "failure continuation for the if expression is called. (define (analyze-if exp)     (let ((pproc (analyze (if-predicate exp)))           (cproc (analyze (if-consequent exp)))           (aproc (analyze (if-alternative exp))))       (lambda (env succeed fail)         (pproc env                ;; success continuation for evaluating the predica te               ;; to obtain pred-value                (lambda (pred-value fail2)                  (if (true? pred-value)                      (cproc env succeed fail2)                     (aproc env succeed fail2)))                ;; failure continuation for evaluating the predica te               fail))))   Sequences are also handled in the same way as in the previous evaluator , except for the machinations in the subprocedure sequentially  that are required for passing the continuations. Namely , to sequentially execute a and then b, we call a with a success continuation that calls b. (define (analyze-sequence exps)     (define (sequentially a b)       (lambda (env succ"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6859136e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "eed fail)         (a env            ;; success continuation for calling a           (lambda (a-value fail2)              (b env succeed fail2))            ;; failure continuation for calling a           fail)))     (define (loop first-proc rest-procs)       (if (null? rest-procs)           first-proc           (loop (sequentially first-proc (car rest-procs))                 (cdr rest-procs))))     (let ((procs (map analyze exps)))       (if (null? procs)           (error \"Empty sequence -- ANALYZE\"))       (loop (car procs) (cdr procs))))   Definitions and assignments Definitions are another case where we must go to some trouble to manage the continuations, because it is necessary to evaluate the definition-value expression before actually defining the new variable. To accomplish this, the definition-value execution procedure vproc  is called with the environment, a success continuation, and the failure continuation. If theexecution of vproc  succeeds, obtaining a value val for the defined variable, the varia"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6860160b
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ble is defined and the success is propagated: (define (analyze-definition exp)     (let ((var (definition-variable exp))           (vproc (analyze (definition-value exp))))       (lambda (env succeed fail)         (vproc env                                       (lambda (val fail2)                  (define-variable! var val env)                  (succeed 'ok fail2))                fail))))   Assignments are more interesting. This is the first place where we really use the continuations, rather than just passing them around. The execution procedure for assignments starts out like the one for definitions. It first attempts to obtain the new value to be assigned to the variable. If this evaluation of vproc  fails, the assignment fails. If vproc  succeeds, however , and we go on to make the assignment, we must consider the possibility that this branch of the computation might later fail, which will require us to backtrack out of the assignment. Thus, we must arrange to undo the assignment as part of the backtrack"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6861184i
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ing process.57 This is accomplished by giving vproc  a success continuation (marked with the comment \u201c*1*\u201d below) that saves the old value of the variable before assigning the new value to the variable and proceeding from the assignment. The failure continuation that is passed along with the value of the assignment (marked with the comment \u201c*2*\u201d below) restores the old value of the variable before continuing the failure. That is, a successful assignment provides a failure continuation that will intercept a subsequent failure; whatever failure would otherwise have called fail2  calls this procedure instead, to undo the assignment before actually calling fail2 . (define (analyze-assignment exp)     (let ((var (assignment-variable exp))           (vproc (analyze (assignment-value exp))))       (lambda (env succeed fail)        (vproc env                (lambda (val fail2)        ; *1*                 (let ((old-value                         (lookup-variable-value var env)))                    (set-variable-value"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6862208!
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "! var val env)                    (succeed 'ok                             (lambda ()    ; *2*                              (set-variable-value! var                                                    old-value                                                    env)                               (fail2)))))                fail))))   Procedur e applications The execution procedure for applications contains no new ideas except for the technical complexity of managing the continuations. This complexity arises in analyze-application , due to the need to keep track of the success and failure continuations as we evaluate the operands. We use a procedure get-args  to evaluate the list of operands, rather than a simple map as in the ordinary evaluator . (define (analyze-application exp)     (let ((fproc (analyze (operator exp)))           (aprocs (map analyze (operands exp))))       (lambda (env succeed fail)         (fproc env                (lambda (proc fail2)                  (get-args aprocs                      "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6863232
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "      env                            (lambda (args fail3)                              (execute-application                               proc args succeed fail3))                            fail2))                fail))))  In get-args , notice how cdring down the list of aproc  execution procedures and cons ing up the resulting list of args  is accomplished by calling each aproc  in the list with a success continuation that recursively calls get-args . Each of these recursive calls to get-args  has a success continuation whose value is the cons  of the newly obtained ar gument onto the list of accumulated ar guments: (define (get-args aprocs env succeed fail)     (if (null? aprocs)         (succeed '() fail)         ((car aprocs) env                       ;; success continuation for this aproc                      (lambda (arg fail2)                         (get-args (cdr aprocs)                                   env                                   ;; success continuation for rec ursive                    "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6864256
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "              ;; call to get-args                                   (lambda (args fail3)                                     (succeed (cons arg args)                                              fail3))                                   fail2))                       fail)))   The actual procedure application, which is performed by execute- application , is accomplished in the same way as for the ordinary evaluator , except for the need to manage the continuations. (define (execute-application proc args succeed fail)     (cond ((primitive-procedure? proc)            (succeed (apply-primitive-procedure proc args)                     fail))           ((compound-procedure? proc)            ((procedure-body proc)             (extend-environment (procedure-parameters proc)                                 args                                 (procedure-environment proc))            succeed             fail))           (else            (error             \"Unknown procedure type -- EXECUTE-APPLICATION\"             pro"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6865280c
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "c))))   Evaluating amb expr essions The amb special form is the key element in the nondeterministic language. Here we see the essence of the interpretation process and the reason for keeping track of the continuations. The execution procedure for amb defines a loop try-next  that cycles through the execution procedures for all the possible values of the amb expression. Each execution procedure is called with a failure continuation that will try the next one. When there are no more alternatives to try , the entire amb expression fails. (define (analyze-amb exp)     (let ((cprocs (map analyze (amb-choices exp))))       (lambda (env succeed fail)         (define (try-next choices)           (if (null? choices)               (fail)               ((car choices) env                              succeed                              (lambda ()                                (try-next (cdr choices))))))         (try-next cprocs))))   Driver loop The driver loop for the amb evaluator is complex, due to the mechanism th"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6866304a
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "at permits the user to try again in evaluating an expression. The driver uses a procedure called internal-loop , which takes as ar gument a procedure try-again . The intent is that calling try-again  should go on to the next untried alternative in the nondeterministic evaluation. Internal-loop  eithercalls try-again  in response to the user typing try-again  at the driver loop, or else starts a new evaluation by calling ambeval . The failure continuation for this call to ambeval  informs the user that there are no more values and re-invokes the driver loop. The success continuation for the call to ambeval  is more subtle. We print the obtained value and then invoke the internal loop again with a try-again procedure that will be able to try the next alternative. This next- alternative  procedure is the second ar gument that was passed to the success continuation. Ordinarily , we think of this second ar gument as a failure continuation to be used if the current evaluation branch later fails. In this case, howev"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6867328e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "er , we have completed a successful evaluation, so we can invoke the \u201cfailure\u201d alternative branch in order to search for additional successful evaluations. (define input-prompt \";;; Amb-Eval input:\")   (define output-prompt \";;; Amb-Eval value:\")   (define (driver-loop)     (define (internal-loop try-again)       (prompt-for-input input-prompt)       (let ((input (read)))         (if (eq? input 'try-again)             (try-again)             (begin               (newline)               (display \";;; Starting a new problem \")               (ambeval input                        the-global-environment                        ;; ambeval success                        (lambda (val next-alternative)                          (announce-output output-prompt)                          (user-print val)                          (internal-loop next-alternative))                        ;; ambeval failure                        (lambda ()                          (announce-output                          \";;; There are no mor"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6868352e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "e values of\")                          (user-print input)                          (driver-loop)))))))     (internal-loop      (lambda ()        (newline)        (display \";;; There is no current problem\")        (driver-loop))))   The initial call to internal-loop  uses a try-again  procedure that complains that there is no current problem and restarts the driver loop. This is the behavior that will happen if the user types try-again  when there is no evaluation in progress. Exer cise 4.50.   Implement a new special form ramb  that is like amb except that it searches alternatives in a random order , rather than from left to right. Show how this can help with Alyssa's problem in exercise 4.49. Exer cise 4.51.   Implement a new kind of assignment called permanent- set!  that is not undone upon failure. For example, we can choose two distinct elements from a list and count the number of trials required to make a successful choice as follows: (define count 0)   (let ((x (an-element-of '(a b c)))         (y (an-e"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6869376l
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "lement-of '(a b c))))     (permanent-set! count (+ count 1))     (require (not (eq? x y)))     (list x y count))   ;;; Starting a new problem   ;;; Amb-Eval value:   (a b 2)  ;;; Amb-Eval input:   try-again   ;;; Amb-Eval value:   (a c 3) What values would have been displayed if we had used set!  here rather than permanent-set!  ? Exer cise 4.52.   Implement a new construct called if-fail  that permits the user to catch the failure of an expression. If-fail  takes two expressions. It evaluates the first expression as usual and returns as usual if the evaluation succeeds. If the evaluation fails, however , the value of the second expression is returned, as in the following example: ;;; Amb-Eval input:   (if-fail (let ((x (an-element-of '(1 3 5))))              (require (even? x))              x)            'all-odd)   ;;; Starting a new problem   ;;; Amb-Eval value:   all-odd  ;;; Amb-Eval input:   (if-fail (let ((x (an-element-of '(1 3 5 8))))              (require (even? x))              x)            'all-o"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6870400d
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "dd)   ;;; Starting a new problem   ;;; Amb-Eval value:   8  Exer cise 4.53.   With permanent-set!  as described in exercise 4.51 and if- fail  as in exercise 4.52, what will be the result of evaluating (let ((pairs '()))     (if-fail (let ((p (prime-sum-pair '(1 3 5 8) '(20 35 110))))                (permanent-set! pairs (cons p pairs))                (amb))              pairs))   Exer cise 4.54.   If we had not realized that require  could be implemented as an ordinary procedure that uses amb, to be defined by the user as part of anondeterministic program, we would have had to implement it as a special form. This would require syntax procedures (define (require? exp) (tagged-list? exp 'require))     (define (require-predicate exp) (cadr exp))   and a new clause in the dispatch in analyze ((require? exp) (analyze-require exp))   as well the procedure analyze-require  that handles require  expressions. Complete the following definition of analyze-require . (define (analyze-require exp)     (let ((pproc (analyz"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6871424e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "e (require-predicate exp))))       (lambda (env succeed fail)         (pproc env                (lambda (pred-value fail2)                  (if < ??>                     < ??>                     (succeed 'ok fail2)))                fail))))   42 We assume that we have previously defined a procedure prime?  that tests whether numbers are prime. Even with prime?  defined, the prime-sum-pair  procedure may look suspiciously like the unhelpful \u201cpseudo-Lisp\u201d attempt to define the square-root function, which we described at the beginning of section 1.1.7 . In fact, a square-root procedure along those lines can actually be formulated as a nondeterministic program. By incorporating a search mechanism into the evaluator , we are eroding the distinction between purely declarative descriptions and imperative specifications of how to compute answers. We'll go even farther in this direction in section 4.4. 43 The idea of amb for nondeterministic programming was first described in 1961 by John McCarthy (see McCarthy 1967)"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6872448.
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": ". 44 In actuality , the distinction between nondeterministically returning a single choice and returning all choices depends somewhat on our point of view . From the perspective of the code that uses the value, the nondeterministic choice returns a single value. From the perspective of the programmer designing the code, the nondeterministic choice potentially returns all possible values, and the computation branches so that each value is investigated separately .45 One might object that this is a hopelessly inef ficient mechanism. It might require millions of processors to solve some easily stated problem this way , and most of the time most of those processors would be idle. This objection should be taken in the context of history . Memory used to be considered just such an expensive commodity . In 1964 a megabyte of RAM cost about $400,000. Now every personal computer has many megabytes of RAM, and most of the time most of that RAM is unused. It is hard to underestimate the cost of mass-produced electronics"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6873472.
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": ". 46 Automagically: \u201cAutomatically , but in a way which, for some reason (typically because it is too complicated, or too ugly , or perhaps even too trivial), the speaker doesn't feel like explaining.\u201d (Steele 1983, Raymond 1993) 47 The integration of automatic search strategies into programming languages has had a long and checkered history . The first suggestions that nondeterministic algorithms might be elegantly encoded in a programming language with search and automatic backtracking came from Robert Floyd (1967). Carl Hewitt (1969) invented a programming language called Planner that explicitly supported automatic chronological backtracking, providing for a built-in depth-first search strategy . Sussman, Winograd, and Charniak (1971) implemented a subset of this language, called MicroPlanner , which was used to support work in problem solving and robot planning. Similar ideas, arising from logic and theorem proving, led to the genesis in Edinbur gh and Marseille of the elegant language Prolog (which we wi"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6874496l
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ll discuss in section 4.4). After suf ficient frustration with automatic search, McDermott and Sussman (1972) developed a language called Conniver , which included mechanisms for placing the search strategy under programmer control. This proved unwieldy , however , and Sussman and Stallman (1975) found a more tractable approach while investigating methods of symbolic analysis for electrical circuits. They developed a non-chronological backtracking scheme that was based on tracing out the logical dependencies connecting facts, a technique that has come to be known as dependency-dir ected backtracking . Although their method was complex, it produced reasonably efficient programs because it did little redundant search. Doyle (1979) and McAllester (1978, 1980) generalized and clarified the methods of Stallman and Sussman, developing a new paradigm for formulating search that is now called truth maintenance . Modern problem-solving systems all use some form of truth-maintenance system as a substrate. See Forbus an"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6875520d
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "d deKleer 1993 for a discussion of elegant ways to build truth-maintenance systems and applications using truth maintenance. Zabih, McAllester , and Chapman 1987 describes a nondeterministic extension to Scheme that is based on amb; it is similar to the interpreter described in this section, but more sophisticated, because it uses dependency-directed backtracking rather than chronological backtracking. Winston 1992 gives an introduction to both kinds of backtracking. 48 Our program uses the following procedure to determine if the elements of a list are distinct: (define (distinct? items)     (cond ((null? items) true)           ((null? (cdr items)) true)           ((member (car items) (cdr items)) false)           (else (distinct? (cdr items)))))   Member  is like memq  except that it uses equal?  instead of eq? to test for equality . 49 This is taken from a booklet called \u201cProblematical Recreations,\u201d published in the 1960s by Litton Industries, where it is attributed to the Kansas State Engineer .50 Here we "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6876544u
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "use the convention that the first element of each list designates the part of speech for the rest of the words in the list. 51 Notice that parse-word  uses set!  to modify the unparsed input list. For this to work, our amb evaluator must undo the ef fects of set!  operations when it backtracks. 52 Observe that this definition is recursive \u2013 a verb may be followed by any number of prepositional phrases. 53 This kind of grammar can become arbitrarily complex, but it is only a toy as far as real language understanding is concerned. Real natural-language understanding by computer requires an elaborate mixture of syntactic analysis and interpretation of meaning. On the other hand, even toy parsers can be useful in supporting flexible command languages for programs such as information-retrieval systems. Winston 1992 discusses computational approaches to real language understanding and also the applications of simple grammars to command languages. 54 Although Alyssa's idea works just fine (and is surprisingly simple"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6877568)
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "), the sentences that it generates are a bit boring \u2013 they don't sample the possible sentences of this language in a very interesting way . In fact, the grammar is highly recursive in many places, and Alyssa's technique \u201cfalls into\u201d one of these recursions and gets stuck. See exercise 4.50 for a way to deal with this. 55 We chose to implement the lazy evaluator in section 4.2 as a modification of the ordinary metacircular evaluator of section 4.1.1 . In contrast, we will base the amb evaluator on the analyzing evaluator of section 4.1.7 , because the execution procedures in that evaluator provide a convenient framework for implementing backtracking. 56 We assume that the evaluator supports let (see exercise 4.22), which we have used in our nondeterministic programs. 57 We didn't worry about undoing definitions, since we can assume that internal definitions are scanned out (section 4.1.6 ).4.4  Logic Programming In chapter 1 we stressed that computer science deals with imperative (how to) knowledge, whereas ma"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6878592t
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "thematics deals with declarative (what is) knowledge. Indeed, programming languages require that the programmer express knowledge in a form that indicates the step-by-step methods for solving particular problems. On the other hand, high-level languages provide, as part of the language implementation, a substantial amount of methodological knowledge that frees the user from concern with numerous details of how a specified computation will progress. Most programming languages, including Lisp, are or ganized around computing the values of mathematical functions. Expression-oriented languages (such as Lisp, Fortran, and Algol) capitalize on the \u201cpun\u201d that an expression that describes the value of a function may also be interpreted as a means of computing that value. Because of this, most programming languages are strongly biased toward unidirectional computations (computations with well-defined inputs and outputs). There are, however , radically dif ferent programming languages that relax this bias. We saw one su"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6879616c
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ch example in section 3.3.5 , where the objects of computation were arithmetic constraints. In a constraint system the direction and the order of computation are not so well specified; in carrying out a computation the system must therefore provide more detailed \u201chow to\u201d knowledge than would be the case with an ordinary arithmetic computation. This does not mean, however , that the user is released altogether from the responsibility of providing imperative knowledge. There are many constraint networks that implement the same set of constraints, and the user must choose from the set of mathematically equivalent networks a suitable network to specify a particular computation. The nondeterministic program evaluator of section 4.3 also moves away from the view that programming is about constructing algorithms for computing unidirectional functions. In a nondeterministic language, expressions can have more than one value, and, as a result, the computation is dealing with relations rather than with single-valued fu"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6880640n
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "nctions. Logic programming extends this idea by combining a relational vision ofprogramming with a powerful kind of symbolic pattern matching called unification .58 This approach, when it works, can be a very powerful way to write programs. Part of the power comes from the fact that a single \u201cwhat is\u201d fact can be used to solve a number of dif ferent problems that would have different \u201chow to\u201d components. As an example, consider the append operation, which takes two lists as ar guments and combines their elements to form a single list. In a procedural language such as Lisp, we could define append  in terms of the basic list constructor cons , as we did in section 2.2.1 : (define (append x y)     (if (null? x)         y        (cons (car x) (append (cdr x) y))))   This procedure can be regarded as a translation into Lisp of the following two rules, the first of which covers the case where the first list is empty and the second of which handles the case of a nonempty list, which is a cons  of two parts: For any "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6881664l
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "list y, the empty list and y append  to form y. For any u, v, y, and z, (cons u v)  and y append  to form (cons u z)  if v and y append  to form z.59 Using the append  procedure, we can answer questions such as Find the append  of (a b)  and (c d) . But the same two rules are also suf ficient for answering the following sorts of questions, which the procedure can't answer: Find a list y that append s with (a b)  to produce (a b c d) . Find all x and y that append  to form (a b c d) .In a logic programming language, the programmer writes an append \u201cprocedure\u201d by stating the two rules about append  given above. \u201cHow to\u201d knowledge is provided automatically by the interpreter to allow this single pair of rules to be used to answer all three types of questions about append .60 Contemporary logic programming languages (including the one we implement here) have substantial deficiencies, in that their general \u201chow to\u201d methods can lead them into spurious infinite loops or other undesirable behavior . Logic programming"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6882688
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " is an active field of research in computer science.61 Earlier in this chapter we explored the technology of implementing interpreters and described the elements that are essential to an interpreter for a Lisp-like language (indeed, to an interpreter for any conventional language). Now we will apply these ideas to discuss an interpreter for a logic programming language. We call this language the query language , because it is very useful for retrieving information from data bases by formulating queries , or questions, expressed in the language. Even though the query language is very dif ferent from Lisp, we will find it convenient to describe the language in terms of the same general framework we have been using all along: as a collection of primitive elements, together with means of combination that enable us to combine simple elements to create more complex elements and means of abstraction that enable us to regard complex elements as single conceptual units. An interpreter for a logic programming language "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6883712i
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "is considerably more complex than an interpreter for a language like Lisp. Nevertheless, we will see that our query-language interpreter contains many of the same elements found in the interpreter of section 4.1. In particular , there will be an \u201ceval\u201d part that classifies expressions according to type and an \u201capply\u201d part that implements the language's abstraction mechanism (procedures in the case of Lisp, and rules in the case of logic programming). Also, a central role is played in the implementation by a frame data structure, which determines the correspondence between symbols and their associated values. One additional interesting aspect of our query-language implementation is that we make substantial use of streams, which were introduced in chapter 3.4.4.1  Deductive Information Retrieval Logic programming excels in providing interfaces to data bases for information retrieval. The query language we shall implement in this chapter is designed to be used in this way . In order to illustrate what the query "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6884736s
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "system does, we will show how it can be used to manage the data base of personnel records for Microshaft, a thriving high-technology company in the Boston area. The language provides pattern-directed access to personnel information and can also take advantage of general rules in order to make logical deductions. A sample data base The personnel data base for Microshaft contains assertions  about company personnel. Here is the information about Ben Bitdiddle, the resident computer wizard: (address (Bitdiddle Ben) (Slumerville (Ridge Road) 10))   (job (Bitdiddle Ben) (computer wizard))   (salary (Bitdiddle Ben) 60000)   Each assertion is a list (in this case a triple) whose elements can themselves be lists. As resident wizard, Ben is in char ge of the company's computer division, and he supervises two programmers and one technician. Here is the information about them: (address (Hacker Alyssa P) (Cambridge (Mass Ave) 78))   (job (Hacker Alyssa P) (computer programmer))   (salary (Hacker Alyssa P) 40000)   (super"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6885760v
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "visor (Hacker Alyssa P) (Bitdiddle Ben))   (address (Fect Cy D) (Cambridge (Ames Street) 3))   (job (Fect Cy D) (computer programmer))   (salary (Fect Cy D) 35000)   (supervisor (Fect Cy D) (Bitdiddle Ben))   (address (Tweakit Lem E) (Boston (Bay State Road) 22))  (job (Tweakit Lem E) (computer technician))   (salary (Tweakit Lem E) 25000)   (supervisor (Tweakit Lem E) (Bitdiddle Ben))   There is also a programmer trainee, who is supervised by Alyssa: (address (Reasoner Louis) (Slumerville (Pine Tree Road) 80))   (job (Reasoner Louis) (computer programmer trainee))   (salary (Reasoner Louis) 30000)   (supervisor (Reasoner Louis) (Hacker Alyssa P))   All of these people are in the computer division, as indicated by the word computer  as the first item in their job descriptions. Ben is a high-level employee. His supervisor is the company's big wheel himself: (supervisor (Bitdiddle Ben) (Warbucks Oliver))   (address (Warbucks Oliver) (Swellesley (Top Heap Road)))   (job (Warbucks Oliver) (administration big whee"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6886784l
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "l))   (salary (Warbucks Oliver) 150000)   Besides the computer division supervised by Ben, the company has an accounting division, consisting of a chief accountant and his assistant: (address (Scrooge Eben) (Weston (Shady Lane) 10))   (job (Scrooge Eben) (accounting chief accountant))   (salary (Scrooge Eben) 75000)   (supervisor (Scrooge Eben) (Warbucks Oliver))   (address (Cratchet Robert) (Allston (N Harvard Street) 16))   (job (Cratchet Robert) (accounting scrivener))   (salary (Cratchet Robert) 18000)   (supervisor (Cratchet Robert) (Scrooge Eben))   There is also a secretary for the big wheel: (address (Aull DeWitt) (Slumerville (Onion Square) 5))   (job (Aull DeWitt) (administration secretary))  (salary (Aull DeWitt) 25000)   (supervisor (Aull DeWitt) (Warbucks Oliver))   The data base also contains assertions about which kinds of jobs can be done by people holding other kinds of jobs. For instance, a computer wizard can do the jobs of both a computer programmer and a computer technician: (can-do-job ("}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6887808c
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "computer wizard) (computer programmer))   (can-do-job (computer wizard) (computer technician))   A computer programmer could fill in for a trainee: (can-do-job (computer programmer)               (computer programmer trainee))   Also, as is well known, (can-do-job (administration secretary)               (administration big wheel))   Simple queries The query language allows users to retrieve information from the data base by posing queries in response to the system's prompt. For example, to find all computer programmers one can say ;;; Query input:   (job ?x (computer programmer))   The system will respond with the following items: ;;; Query results:   (job (Hacker Alyssa P) (computer programmer))   (job (Fect Cy D) (computer programmer))   The input query specifies that we are looking for entries in the data base that match a certain pattern . In this example, the pattern specifies entries consisting of three items, of which the first is the literal symbol job, thesecond can be anything, and the third is the"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6888832
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " literal list (computer programmer) . The \u201canything\u201d that can be the second item in the matching list is specified by a pattern variable , ?x. The general form of a pattern variable is a symbol, taken to be the name of the variable, preceded by a question mark. We will see below why it is useful to specify names for pattern variables rather than just putting ? into patterns to represent \u201canything.\u201d The system responds to a simple query by showing all entries in the data base that match the specified pattern. A pattern can have more than one variable. For example, the query (address ?x ?y)   will list all the employees' addresses. A pattern can have no variables, in which case the query simply determines whether that pattern is an entry in the data base. If so, there will be one match; if not, there will be no matches. The same pattern variable can appear more than once in a query , specifying that the same \u201canything\u201d must appear in each position. This is why variables have names. For example, (supervisor ?x ?"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6889856x
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "x)   finds all people who supervise themselves (though there are no such assertions in our sample data base). The query (job ?x (computer ?type))   matches all job entries whose third item is a two-element list whose first item is computer : (job (Bitdiddle Ben) (computer wizard))   (job (Hacker Alyssa P) (computer programmer))   (job (Fect Cy D) (computer programmer))   (job (Tweakit Lem E) (computer technician))  This same pattern does not match (job (Reasoner Louis) (computer programmer trainee))   because the third item in the entry is a list of three elements, and the pattern's third item specifies that there should be two elements. If we wanted to change the pattern so that the third item could be any list beginning with computer , we could specify62 (job ?x (computer . ?type))   For example, (computer . ?type)   matches the data (computer programmer trainee)   with ?type  as the list (programmer trainee) . It also matches the data (computer programmer)   with ?type  as the list (programmer) , and match"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6890880e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "es the data (computer)   with ?type  as the empty list (). We can describe the query language's processing of simple queries as follows: The system finds all assignments to variables in the query pattern that satisfy  the pattern \u2013 that is, all sets of values for the variables such that if the pattern variables are instantiated with  (replaced by) the values, the result is in the data base. The system responds to the query by listing all instantiations of the query pattern with the variable assignments that satisfy it.Note that if the pattern has no variables, the query reduces to a determination of whether that pattern is in the data base. If so, the empty assignment, which assigns no values to variables, satisfies that pattern for that data base. Exer cise 4.55.   Give simple queries that retrieve the following information from the data base: a. all people supervised by Ben Bitdiddle; b. the names and jobs of all people in the accounting division; c. the names and addresses of all people who live in Slumerv"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6891904i
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ille. Compound queries Simple queries form the primitive operations of the query language. In order to form compound operations, the query language provides means of combination. One thing that makes the query language a logic programming language is that the means of combination mirror the means of combination used in forming logical expressions: and, or, and not. (Here and, or, and not are not the Lisp primitives, but rather operations built into the query language.) We can use and as follows to find the addresses of all the computer programmers: (and (job ?person (computer programmer))        (address ?person ?where))   The resulting output is (and (job (Hacker Alyssa P) (computer programmer))        (address (Hacker Alyssa P) (Cambridge (Mass Ave) 78)))   (and (job (Fect Cy D) (computer programmer))        (address (Fect Cy D) (Cambridge (Ames Street) 3)))   In general,(and <query1> <query2> ... <queryn>)  is satisfied by all sets of values for the pattern variables that simultaneously satisfy < query1> ."}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6892928.
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": ".. <queryn>. As for simple queries, the system processes a compound query by finding all assignments to the pattern variables that satisfy the query , then displaying instantiations of the query with those values. Another means of constructing compound queries is through or. For example, (or (supervisor ?x (Bitdiddle Ben))       (supervisor ?x (Hacker Alyssa P)))   will find all employees supervised by Ben Bitdiddle or Alyssa P . Hacker: (or (supervisor (Hacker Alyssa P) (Bitdiddle Ben))       (supervisor (Hacker Alyssa P) (Hacker Alyssa P)))   (or (supervisor (Fect Cy D) (Bitdiddle Ben))       (supervisor (Fect Cy D) (Hacker Alyssa P)))   (or (supervisor (Tweakit Lem E) (Bitdiddle Ben))       (supervisor (Tweakit Lem E) (Hacker Alyssa P)))   (or (supervisor (Reasoner Louis) (Bitdiddle Ben))       (supervisor (Reasoner Louis) (Hacker Alyssa P)))   In general, (or <query1> <query2> ... <queryn>)  is satisfied by all sets of values for the pattern variables that satisfy at least one of < query1> ... <queryn>. C"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6893952o
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ompound queries can also be formed with not. For example, (and (supervisor ?x (Bitdiddle Ben))        (not (job ?x (computer programmer))))  finds all people supervised by Ben Bitdiddle who are not computer programmers. In general, (not <query1>)  is satisfied by all assignments to the pattern variables that do not satisfy <query1>.63 The final combining form is called lisp-value . When lisp-value  is the first element of a pattern, it specifies that the next element is a Lisp predicate to be applied to the rest of the (instantiated) elements as arguments. In general, (lisp-value < predicate > <arg1> ... <argn>)  will be satisfied by assignments to the pattern variables for which the <predicate > applied to the instantiated < arg1> ... <argn> is true. For example, to find all people whose salary is greater than $30,000 we could write64 (and (salary ?person ?amount)        (lisp-value > ?amount 30000))   Exer cise 4.56.   Formulate compound queries that retrieve the following information: a. the names of all p"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6894976e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "eople who are supervised by Ben Bitdiddle, together with their addresses; b. all people whose salary is less than Ben Bitdiddle's, together with their salary and Ben Bitdiddle's salary; c. all people who are supervised by someone who is not in the computer division, together with the supervisor's name and job. RulesIn addition to primitive queries and compound queries, the query language provides means for abstracting queries. These are given by rules . The rule (rule (lives-near ?person-1 ?person-2)         (and (address ?person-1 (?town . ?rest-1))              (address ?person-2 (?town . ?rest-2))              (not (same ?person-1 ?person-2))))   specifies that two people live near each other if they live in the same town. The final not clause prevents the rule from saying that all people live near themselves. The same  relation is defined by a very simple rule:65 (rule (same ?x ?x))   The following rule declares that a person is a \u201cwheel\u201d in an or ganization if he supervises someone who is in turn a super"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6896000v
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "visor: (rule (wheel ?person)         (and (supervisor ?middle-manager ?person)              (supervisor ?x ?middle-manager)))   The general form of a rule is (rule <conclusion > <body>)  where < conclusion > is a pattern and < body > is any query .66 We can think of a rule as representing a lar ge (even infinite) set of assertions, namely all instantiations of the rule conclusion with variable assignments that satisfy the rule body . When we described simple queries (patterns), we said that an assignment to variables satisfies a pattern if the instantiated pattern is in the data base. But the pattern needn't be explicitly in the data base as an assertion. It can be an implicit assertion implied by a rule. For example, the query (lives-near ?x (Bitdiddle Ben))   results in(lives-near (Reasoner Louis) (Bitdiddle Ben))   (lives-near (Aull DeWitt) (Bitdiddle Ben))   To find all computer programmers who live near Ben Bitdiddle, we can ask (and (job ?x (computer programmer))        (lives-near ?x (Bitdiddle Ben))) "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6897024
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "  As in the case of compound procedures, rules can be used as parts of other rules (as we saw with the lives-near  rule above) or even be defined recursively . For instance, the rule (rule (outranked-by ?staff-person ?boss)         (or (supervisor ?staff-person ?boss)             (and (supervisor ?staff-person ?middle-manager)                  (outranked-by ?middle-manager ?boss))))   says that a staf f person is outranked by a boss in the or ganization if the boss is the person's supervisor or (recursively) if the person's supervisor is outranked by the boss. Exer cise 4.57.   Define a rule that says that person 1 can replace person 2 if either person 1 does the same job as person 2 or someone who does person 1's job can also do person 2's job, and if person 1 and person 2 are not the same person. Using your rule, give queries that find the following: a.  all people who can replace Cy D. Fect; b.  all people who can replace someone who is being paid more than they are, together with the two salaries. Exer ci"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6898048s
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "se 4.58.   Define a rule that says that a person is a \u201cbig shot\u201d in a division if the person works in the division but does not have a supervisor who works in the division. Exer cise 4.59.   Ben Bitdiddle has missed one meeting too many . Fearing that his habit of for getting meetings could cost him his job, Ben decides todo something about it. He adds all the weekly meetings of the firm to the Microshaft data base by asserting the following: (meeting accounting (Monday 9am))   (meeting administration (Monday 10am))   (meeting computer (Wednesday 3pm))   (meeting administration (Friday 1pm))   Each of the above assertions is for a meeting of an entire division. Ben also adds an entry for the company-wide meeting that spans all the divisions. All of the company's employees attend this meeting. (meeting whole-company (Wednesday 4pm))   a. On Friday morning, Ben wants to query the data base for all the meetings that occur that day . What query should he use? b. Alyssa P . Hacker is unimpressed. She thinks it wou"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6899072l
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ld be much more useful to be able to ask for her meetings by specifying her name. So she designs a rule that says that a person's meetings include all whole-company meetings plus all meetings of that person's division. Fill in the body of Alyssa's rule. (rule (meeting-time ?person ?day-and-time)         <rule-body >)  c. Alyssa arrives at work on W ednesday morning and wonders what meetings she has to attend that day . Having defined the above rule, what query should she make to find this out? Exer cise 4.60.   By giving the query (lives-near ?person (Hacker Alyssa P))   Alyssa P . Hacker is able to find people who live near her , with whom she can ride to work. On the other hand, when she tries to find all pairs of people who live near each other by querying (lives-near ?person-1 ?person-2)  she notices that each pair of people who live near each other is listed twice; for example, (lives-near (Hacker Alyssa P) (Fect Cy D))   (lives-near (Fect Cy D) (Hacker Alyssa P))   Why does this happen? Is there a way t"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6900096o
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "o find a list of people who live near each other , in which each pair appears only once? Explain. Logic as pr ograms We can regard a rule as a kind of logical implication: If an assignment of values to pattern variables satisfies the body , then it satisfies the conclusion. Consequently , we can regard the query language as having the ability to perform logical deductions  based upon the rules. As an example, consider the append  operation described at the beginning of section 4.4. As we said, append  can be characterized by the following two rules: For any list y, the empty list and y append  to form y. For any u, v, y, and z, (cons u v)  and y append  to form (cons u z)  if v and y append  to form z. To express this in our query language, we define two rules for a relation (append-to-form x y z)   which we can interpret to mean \u201c x and y append  to form z\u201d: (rule (append-to-form () ?y ?y))   (rule (append-to-form (?u . ?v) ?y (?u . ?z))         (append-to-form ?v ?y ?z))   The first rule has no body , which"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6901120
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " means that the conclusion holds for any value of ?y. Note how the second rule makes use of dotted-tail notation to name the car and cdr of a list.Given these two rules, we can formulate queries that compute the append  of two lists: ;;; Query input:   (append-to-form (a b) (c d) ?z)   ;;; Query results:   (append-to-form (a b) (c d) (a b c d))   What is more striking, we can use the same rules to ask the question \u201cWhich list, when append ed to (a b) , yields (a b c d) ?\u201d This is done as follows: ;;; Query input:   (append-to-form (a b) ?y (a b c d))   ;;; Query results:   (append-to-form (a b) (c d) (a b c d))   We can also ask for all pairs of lists that append  to form (a b c d) : ;;; Query input:   (append-to-form ?x ?y (a b c d))   ;;; Query results:   (append-to-form () (a b c d) (a b c d))   (append-to-form (a) (b c d) (a b c d))   (append-to-form (a b) (c d) (a b c d))   (append-to-form (a b c) (d) (a b c d))   (append-to-form (a b c d) () (a b c d))   The query system may seem to exhibit quite a bit "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6902144o
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "of intelligence in using the rules to deduce the answers to the queries above. Actually , as we will see in the next section, the system is following a well-determined algorithm in unraveling the rules. Unfortunately , although the system works impressively in the append  case, the general methods may break down in more complex cases, as we will see in section 4.4.3 . Exer cise 4.61.   The following rules implement a next-to  relation that finds adjacent elements of a list:(rule (?x next-to ?y in (?x ?y . ?u)))     (rule (?x next-to ?y in (?v . ?z))         (?x next-to ?y in ?z))   What will the response be to the following queries? (?x next-to ?y in (1 (2 3) 4))     (?x next-to 1 in (2 1 3 1))   Exer cise 4.62.   Define rules to implement the last-pair  operation of exercise 2.17, which returns a list containing the last element of a nonempty list. Check your rules on queries such as (last-pair (3) ?x) , (last-pair (1 2 3) ?x) , and (last-pair (2 ?x) (3)) . Do your rules work correctly on queries such as (la"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6903168s
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "st-pair ?x (3))  ? Exer cise 4.63.   The following data base (see Genesis 4) traces the genealogy of the descendants of Ada back to Adam, by way of Cain: (son Adam Cain)   (son Cain Enoch)   (son Enoch Irad)   (son Irad Mehujael)   (son Mehujael Methushael)   (son Methushael Lamech)   (wife Lamech Ada)   (son Ada Jabal)   (son Ada Jubal)   Formulate rules such as \u201cIf S is the son of F, and F is the son of G, then S is the grandson of G\u201d and \u201cIf W is the wife of M, and S is the son of W, then S is the son of M\u201d (which was supposedly more true in biblical times than today) that will enable the query system to find the grandson of Cain; the sons of Lamech; the grandsons of Methushael. (See exercise 4.69 for some rules to deduce more complicated relationships.) 4.4.2  How the Query System W orksIn section 4.4.4  we will present an implementation of the query interpreter as a collection of procedures. In this section we give an overview that explains the general structure of the system independent of low-level imp"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6904192l
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "lementation details. After describing the implementation of the interpreter , we will be in a position to understand some of its limitations and some of the subtle ways in which the query language's logical operations differ from the operations of mathematical logic. It should be apparent that the query evaluator must perform some kind of search in order to match queries against facts and rules in the data base. One way to do this would be to implement the query system as a nondeterministic program, using the amb evaluator of section 4.3 (see exercise 4.78). Another possibility is to manage the search with the aid of streams. Our implementation follows this second approach. The query system is or ganized around two central operations called pattern matching  and unification . We first describe pattern matching and explain how this operation, together with the or ganization of information in terms of streams of frames, enables us to implement both simple and compound queries. We next discuss unification, a gen"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6905216e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "eralization of pattern matching needed to implement rules. Finally , we show how the entire query interpreter fits together through a procedure that classifies expressions in a manner analogous to the way eval  classifies expressions for the interpreter described in section 4.1. Pattern matching A pattern matcher  is a program that tests whether some datum fits a specified pattern. For example, the data list ((a b) c (a b))  matches the pattern (?x c ?x)  with the pattern variable ?x bound to (a b) . The same data list matches the pattern (?x ?y ?z)  with ?x and ?z both bound to (a b) and ?y bound to c. It also matches the pattern ((?x ?y) c (?x ?y)) with ?x bound to a and ?y bound to b. However , it does not match the pattern (?x a ?y) , since that pattern specifies a list whose second element is the symbol a.The pattern matcher used by the query system takes as inputs a pattern, a datum, and a frame  that specifies bindings for various pattern variables. It checks whether the datum matches the pattern in a "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6906240w
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "way that is consistent with the bindings already in the frame. If so, it returns the given frame augmented by any bindings that may have been determined by the match. Otherwise, it indicates that the match has failed. For example, using the pattern (?x ?y ?x)  to match (a b a)  given an empty frame will return a frame specifying that ?x is bound to a and ?y is bound to b. Trying the match with the same pattern, the same datum, and a frame specifying that ?y is bound to a will fail. Trying the match with the same pattern, the same datum, and a frame in which ?y is bound to b and ?x is unbound will return the given frame augmented by a binding of ?x to a. The pattern matcher is all the mechanism that is needed to process simple queries that don't involve rules. For instance, to process the query (job ?x (computer programmer))   we scan through all assertions in the data base and select those that match the pattern with respect to an initially empty frame. For each match we find, we use the frame returned by the"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6907264
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " match to instantiate the pattern with a value for ?x. Streams of frames The testing of patterns against frames is or ganized through the use of streams. Given a single frame, the matching process runs through the data- base entries one by one. For each data-base entry , the matcher generates either a special symbol indicating that the match has failed or an extension to the frame. The results for all the data-base entries are collected into a stream, which is passed through a filter to weed out the failures. The result is a stream of all the frames that extend the given frame via a match to some assertion in the data base.67 In our system, a query takes an input stream of frames and performs the above matching operation for every frame in the stream, as indicated in figure 4.4. That is, for each frame in the input stream, the query generates anew stream consisting of all extensions to that frame by matches to assertions in the data base. All these streams are then combined to form one huge stream, which cont"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6908288a
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ains all possible extensions of every frame in the input stream. This stream is the output of the query . Figur e 4.4:   A query processes a stream of frames. To answer a simple query , we use the query with an input stream consisting of a single empty frame. The resulting output stream contains all extensions to the empty frame (that is, all answers to our query). This stream of frames is then used to generate a stream of copies of the original query pattern with the variables instantiated by the values in each frame, and this is the stream that is finally printed. Compound queries The real elegance of the stream-of-frames implementation is evident when we deal with compound queries. The processing of compound queries makes use of the ability of our matcher to demand that a match be consistent with a specified frame. For example, to handle the and of two queries, such as (and (can-do-job ?x (computer programmer trainee))        (job ?person ?x))   (informally , \u201cFind all people who can do the job of a comput"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6909312e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "er programmer trainee\u201d), we first find all entries that match the pattern (can-do-job ?x (computer programmer trainee))  This produces a stream of frames, each of which contains a binding for ?x. Then for each frame in the stream we find all entries that match (job ?person ?x)   in a way that is consistent with the given binding for ?x. Each such match will produce a frame containing bindings for ?x and ?person . The and of two queries can be viewed as a series combination of the two component queries, as shown in figure 4.5. The frames that pass through the first query filter are filtered and further extended by the second query . Figur e 4.5:   The and combination of two queries is produced by operating on the stream of frames in series. Figure 4.6 shows the analogous method for computing the or of two queries as a parallel combination of the two component queries. The input stream of frames is extended separately by each query . The two resulting streams are then mer ged to produce the final output stream."}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6910336
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " Figur e 4.6:   The or combination of two queries is produced by operating on the stream of frames in parallel and mer ging the results.Even from this high-level description, it is apparent that the processing of compound queries can be slow . For example, since a query may produce more than one output frame for each input frame, and each query in an and gets its input frames from the previous query , an and query could, in the worst case, have to perform a number of matches that is exponential in the number of queries (see exercise 4.76).68 Though systems for handling only simple queries are quite practical, dealing with complex queries is extremely dif ficult.69 From the stream-of-frames viewpoint, the not of some query acts as a filter that removes all frames for which the query can be satisfied. For instance, given the pattern (not (job ?x (computer programmer)))   we attempt, for each frame in the input stream, to produce extension frames that satisfy (job ?x (computer programmer)) . We remove from the i"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6911360n
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "nput stream all frames for which such extensions exist. The result is a stream consisting of only those frames in which the binding for ?x does not satisfy (job ?x (computer programmer)) . For example, in processing the query (and (supervisor ?x ?y)        (not (job ?x (computer programmer))))   the first clause will generate frames with bindings for ?x and ?y. The not clause will then filter these by removing all frames in which the binding for ?x satisfies the restriction that ?x is a computer programmer .70 The lisp-value  special form is implemented as a similar filter on frame streams. We use each frame in the stream to instantiate any variables in the pattern, then apply the Lisp predicate. We remove from the input stream all frames for which the predicate fails. Unification In order to handle rules in the query language, we must be able to find the rules whose conclusions match a given query pattern. Rule conclusions are like assertions except that they can contain variables, so we will need ageneraliz"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6912384a
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ation of pattern matching \u2013 called unification  \u2013 in which both the \u201cpattern\u201d and the \u201cdatum\u201d may contain variables. A unifier takes two patterns, each containing constants and variables, and determines whether it is possible to assign values to the variables that will make the two patterns equal. If so, it returns a frame containing these bindings. For example, unifying (?x a ?y)  and (?y ?z a)  will specify a frame in which ?x, ?y, and ?z must all be bound to a. On the other hand, unifying (?x ?y a)  and (?x b ?y)  will fail, because there is no value for ? y that can make the two patterns equal. (For the second elements of the patterns to be equal, ?y would have to be b; however , for the third elements to be equal, ?y would have to be a.) The unifier used in the query system, like the pattern matcher , takes a frame as input and performs unifications that are consistent with this frame. The unification algorithm is the most technically dif ficult part of the query system. With complex patterns, performing"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6913408
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " unification may seem to require deduction. To unify (?x ?x)  and ((a ?y c) (a b ?z)) , for example, the algorithm must infer that ?x should be (a b c) , ?y should be b, and ?z should be c. We may think of this process as solving a set of equations among the pattern components. In general, these are simultaneous equations, which may require substantial manipulation to solve.71 For example, unifying (?x ?x)  and ((a ?y c) (a b ?z))  may be thought of as specifying the simultaneous equations ?x  =  (a ?y c)   ?x  =  (a b ?z)   These equations imply that (a ?y c)  =  (a b ?z)   which in turn implies that a  =  a, ?y  =  b, c  =  ?z,   and hence that?x  =  (a b c)   In a successful pattern match, all pattern variables become bound, and the values to which they are bound contain only constants. This is also true of all the examples of unification we have seen so far . In general, however , a successful unification may not completely determine the variable values; some variables may remain unbound and others may be"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6914432
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " bound to values that contain variables. Consider the unification of (?x a)  and ((b ?y) ?z) . We can deduce that ? x = (b ?y)  and a = ?z, but we cannot further solve for ?x or ?y. The unification doesn't fail, since it is certainly possible to make the two patterns equal by assigning values to ?x and ?y. Since this match in no way restricts the values ?y can take on, no binding for ?y is put into the result frame. The match does, however , restrict the value of ?x. Whatever value ?y has, ?x must be (b ?y) . A binding of ?x to the pattern (b ?y)  is thus put into the frame. If a value for ?y is later determined and added to the frame (by a pattern match or unification that is required to be consistent with this frame), the previously bound ?x will refer to this value.72 Applying rules Unification is the key to the component of the query system that makes inferences from rules. T o see how this is accomplished, consider processing a query that involves applying a rule, such as (lives-near ?x (Hacker Alyssa P)"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6915456)
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": ")   To process this query , we first use the ordinary pattern-match procedure described above to see if there are any assertions in the data base that match this pattern. (There will not be any in this case, since our data base includes no direct assertions about who lives near whom.) The next step is to attempt to unify the query pattern with the conclusion of each rule. We find that the pattern unifies with the conclusion of the rule (rule (lives-near ?person-1 ?person-2)         (and (address ?person-1 (?town . ?rest-1))             (address ?person-2 (?town . ?rest-2))              (not (same ?person-1 ?person-2))))   resulting in a frame specifying that ?person-2  is bound to (Hacker Alyssa P) and that ?x should be bound to (have the same value as) ?person-1 . Now , relative to this frame, we evaluate the compound query given by the body of the rule. Successful matches will extend this frame by providing a binding for ?person-1 , and consequently a value for ?x, which we can use to instantiate the origin"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6916480a
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "al query pattern. In general, the query evaluator uses the following method to apply a rule when trying to establish a query pattern in a frame that specifies bindings for some of the pattern variables: Unify the query with the conclusion of the rule to form, if successful, an extension of the original frame. Relative to the extended frame, evaluate the query formed by the body of the rule. Notice how similar this is to the method for applying a procedure in the eval /apply  evaluator for Lisp: Bind the procedure's parameters to its ar guments to form a frame that extends the original procedure environment. Relative to the extended environment, evaluate the expression formed by the body of the procedure. The similarity between the two evaluators should come as no surprise. Just as procedure definitions are the means of abstraction in Lisp, rule definitions are the means of abstraction in the query language. In each case, we unwind the abstraction by creating appropriate bindings and evaluating the rule or pro"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6917504c
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "cedure body relative to these. Simple queriesWe saw earlier in this section how to evaluate simple queries in the absence of rules. Now that we have seen how to apply rules, we can describe how to evaluate simple queries by using both rules and assertions. Given the query pattern and a stream of frames, we produce, for each frame in the input stream, two streams: a stream of extended frames obtained by matching the pattern against all assertions in the data base (using the pattern matcher), and a stream of extended frames obtained by applying all possible rules (using the unifier).73 Appending these two streams produces a stream that consists of all the ways that the given pattern can be satisfied consistent with the original frame. These streams (one for each frame in the input stream) are now all combined to form one lar ge stream, which therefore consists of all the ways that any of the frames in the original input stream can be extended to produce a match with the given pattern. The query evaluator and th"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6918528e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "e driver loop Despite the complexity of the underlying matching operations, the system is organized much like an evaluator for any language. The procedure that coordinates the matching operations is called qeval , and it plays a role analogous to that of the eval  procedure for Lisp. Qeval  takes as inputs a query and a stream of frames. Its output is a stream of frames, corresponding to successful matches to the query pattern, that extend some frame in the input stream, as indicated in figure 4.4. Like eval , qeval classifies the dif ferent types of expressions (queries) and dispatches to an appropriate procedure for each. There is a procedure for each special form (and, or, not, and lisp-value ) and one for simple queries. The driver loop, which is analogous to the driver-loop  procedure for the other evaluators in this chapter , reads queries from the terminal. For each query , it calls qeval  with the query and a stream that consists of a single empty frame. This will produce the stream of all possible ma"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6919552t
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "tches (all possible extensions to the empty frame). For each frame in the resultingstream, it instantiates the original query using the values of the variables found in the frame. This stream of instantiated queries is then printed.74 The driver also checks for the special command assert! , which signals that the input is not a query but rather an assertion or rule to be added to the data base. For instance, (assert! (job (Bitdiddle Ben) (computer wizard)))   (assert! (rule (wheel ?person)                  (and (supervisor ?middle-manager ?person)                       (supervisor ?x ?middle-manager))))   4.4.3  Is Logic Pr ogramming Mathematical Logic? The means of combination used in the query language may at first seem identical to the operations and, or, and not of mathematical logic, and the application of query-language rules is in fact accomplished through a legitimate method of inference.75 This identification of the query language with mathematical logic is not really valid, though, because the query"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6920576
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " language provides a contr ol structur e that interprets the logical statements procedurally . We can often take advantage of this control structure. For example, to find all of the supervisors of programmers we could formulate a query in either of two logically equivalent forms: (and (job ?x (computer programmer))        (supervisor ?x ?y))   or (and (supervisor ?x ?y)        (job ?x (computer programmer)))   If a company has many more supervisors than programmers (the usual case), it is better to use the first form rather than the second because the data base must be scanned for each intermediate result (frame) produced by the first clause of the and.The aim of logic programming is to provide the programmer with techniques for decomposing a computational problem into two separate problems: \u201cwhat\u201d is to be computed, and \u201chow\u201d this should be computed. This is accomplished by selecting a subset of the statements of mathematical logic that is powerful enough to be able to describe anything one might want to com"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6921600p
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "pute, yet weak enough to have a controllable procedural interpretation. The intention here is that, on the one hand, a program specified in a logic programming language should be an ef fective program that can be carried out by a computer . Control (\u201chow\u201d to compute) is effected by using the order of evaluation of the language. We should be able to arrange the order of clauses and the order of subgoals within each clause so that the computation is done in an order deemed to be ef fective and ef ficient. At the same time, we should be able to view the result of the computation (\u201cwhat\u201d to compute) as a simple consequence of the laws of logic. Our query language can be regarded as just such a procedurally interpretable subset of mathematical logic. An assertion represents a simple fact (an atomic proposition). A rule represents the implication that the rule conclusion holds for those cases where the rule body holds. A rule has a natural procedural interpretation: T o establish the conclusion of the rule, establi"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6922624s
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "sh the body of the rule. Rules, therefore, specify computations. However , because rules can also be regarded as statements of mathematical logic, we can justify any \u201cinference\u201d accomplished by a logic program by asserting that the same result could be obtained by working entirely within mathematical logic.76 Infinite loops A consequence of the procedural interpretation of logic programs is that it is possible to construct hopelessly inef ficient programs for solving certain problems. An extreme case of inef ficiency occurs when the system falls into infinite loops in making deductions. As a simple example, suppose we are setting up a data base of famous marriages, including (assert! (married Minnie Mickey))  If we now ask (married Mickey ?who)   we will get no response, because the system doesn't know that if A is married to B, then B is married to A. So we assert the rule (assert! (rule (married ?x ?y)                  (married ?y ?x)))   and again query (married Mickey ?who)   Unfortunately , this will dri"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6923648v
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ve the system into an infinite loop, as follows: The system finds that the married  rule is applicable; that is, the rule conclusion (married ?x ?y)  successfully unifies with the query pattern (married Mickey ?who)  to produce a frame in which ?x is bound to Mickey  and ?y is bound to ?who . So the interpreter proceeds to evaluate the rule body (married ?y ?x)  in this frame \u2013 in ef fect, to process the query (married ?who Mickey) . One answer appears directly as an assertion in the data base: (married Minnie Mickey) . The married  rule is also applicable, so the interpreter again evaluates the rule body , which this time is equivalent to (married Mickey ? who) . The system is now in an infinite loop. Indeed, whether the system will find the simple answer (married Minnie Mickey)  before it goes into the loop depends on implementation details concerning the order in which the system checks the items in the data base. This is a very simple example of the kinds of loops that can occur . Collections of interrela"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6924672t
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ted rules can lead to loops that are much harder to anticipate, and the appearance of a loop can depend on the order of clauses in an and (see exercise 4.64) or on low-level details concerning the order in which the system processes queries.77Problems with not Another quirk in the query system concerns not. Given the data base of section 4.4.1 , consider the following two queries: (and (supervisor ?x ?y)        (not (job ?x (computer programmer))))   (and (not (job ?x (computer programmer)))        (supervisor ?x ?y))   These two queries do not produce the same result. The first query begins by finding all entries in the data base that match (supervisor ?x ?y) , and then filters the resulting frames by removing the ones in which the value of ?x satisfies (job ?x (computer programmer)) . The second query begins by filtering the incoming frames to remove those that can satisfy (job ?x (computer programmer)) . Since the only incoming frame is empty , it checks the data base to see if there are any patterns that "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6925696s
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "satisfy (job ?x (computer programmer)) . Since there generally are entries of this form, the not clause filters out the empty frame and returns an empty stream of frames. Consequently , the entire compound query returns an empty stream. The trouble is that our implementation of not really is meant to serve as a filter on values for the variables. If a not clause is processed with a frame in which some of the variables remain unbound (as does ?x in the example above), the system will produce unexpected results. Similar problems occur with the use of lisp-value  \u2013 the Lisp predicate can't work if some of its arguments are unbound. See exercise 4.77. There is also a much more serious way in which the not of the query language dif fers from the not of mathematical logic. In logic, we interpret the statement \u201cnot P\u201d to mean that P is not true. In the query system, however , \u201cnot P\u201d means that P is not deducible from the knowledge in the data base. For example, given the personnel data base of section 4.4.1 , the s"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6926720y
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ystem would happily deduce all sorts of not statements, such as that Ben Bitdiddle is not a baseball fan, that it is not raining outside, and that 2 + 2 is not 4.78 In other words, the not of logic programming languages reflects theso-called closed world assumption  that all relevant information has been included in the data base.79 Exer cise 4.64.   Louis Reasoner mistakenly deletes the outranked-by  rule (section 4.4.1 ) from the data base. When he realizes this, he quickly reinstalls it. Unfortunately , he makes a slight change in the rule, and types it in as (rule (outranked-by ?staff-person ?boss)         (or (supervisor ?staff-person ?boss)             (and (outranked-by ?middle-manager ?boss)                  (supervisor ?staff-person ?middle-manager))))   Just after Louis types this information into the system, DeW itt Aull comes by to find out who outranks Ben Bitdiddle. He issues the query (outranked-by (Bitdiddle Ben) ?who)   After answering, the system goes into an infinite loop. Explain why . Exe"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6927744r
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "r cise 4.65.   Cy D. Fect, looking forward to the day when he will rise in the or ganization, gives a query to find all the wheels (using the wheel  rule of section 4.4.1 ): (wheel ?who)   To his surprise, the system responds ;;; Query results:   (wheel (Warbucks Oliver))   (wheel (Bitdiddle Ben))   (wheel (Warbucks Oliver))   (wheel (Warbucks Oliver))   (wheel (Warbucks Oliver))   Why is Oliver W arbucks listed four times?Exer cise 4.66.   Ben has been generalizing the query system to provide statistics about the company . For example, to find the total salaries of all the computer programmers one will be able to say (sum ?amount        (and (job ?x (computer programmer))             (salary ?x ?amount)))   In general, Ben's new system allows expressions of the form (accumulation-function < variable >                         < query pattern >)  where accumulation-function  can be things like sum, average , or maximum . Ben reasons that it should be a cinch to implement this. He will simply feed the query pat"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6928768t
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "tern to qeval . This will produce a stream of frames. He will then pass this stream through a mapping function that extracts the value of the designated variable from each frame in the stream and feed the resulting stream of values to the accumulation function. Just as Ben completes the implementation and is about to try it out, Cy walks by , still puzzling over the wheel  query result in exercise 4.65. When Cy shows Ben the system's response, Ben groans, \u201cOh, no, my simple accumulation scheme won't work!\u201d What has Ben just realized? Outline a method he can use to salvage the situation. Exer cise 4.67.   Devise a way to install a loop detector in the query system so as to avoid the kinds of simple loops illustrated in the text and in exercise 4.64. The general idea is that the system should maintain some sort of history of its current chain of deductions and should not begin processing a query that it is already working on. Describe what kind of information (patterns and frames) is included in this history , "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6929792a
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "and how the check should be made. (After you study the details of the query-system implementation in section 4.4.4 , you may want to modify the system to include your loop detector .)Exer cise 4.68.   Define rules to implement the reverse  operation of exercise 2.18, which returns a list containing the same elements as a given list in reverse order . (Hint: Use append-to-form .) Can your rules answer both (reverse (1 2 3) ?x)  and (reverse ?x (1 2 3))  ? Exer cise 4.69.   Beginning with the data base and the rules you formulated in exercise 4.63, devise a rule for adding \u201cgreats\u201d to a grandson relationship. This should enable the system to deduce that Irad is the great- grandson of Adam, or that Jabal and Jubal are the great-great-great-great- great-grandsons of Adam. (Hint: Represent the fact about Irad, for example, as ((great grandson) Adam Irad) . Write rules that determine if a list ends in the word grandson . Use this to express a rule that allows one to derive the relationship ((great . ?rel) ?x ?y) , "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6930816w
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "where ?rel  is a list ending in grandson .) Check your rules on queries such as ((great grandson) ?g ?ggs)  and (?relationship Adam Irad) . 4.4.4  Implementing the Query System Section 4.4.2  described how the query system works. Now we fill in the details by presenting a complete implementation of the system. 4.4.4.1  The Driver Loop and Instantiation The driver loop for the query system repeatedly reads input expressions. If the expression is a rule or assertion to be added to the data base, then the information is added. Otherwise the expression is assumed to be a query . The driver passes this query to the evaluator qeval  together with an initial frame stream consisting of a single empty frame. The result of the evaluation is a stream of frames generated by satisfying the query with variable values found in the data base. These frames are used to form a new stream consisting of copies of the original query in which the variables are instantiated with values supplied by the stream of frames, and this fina"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6931840l
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "l stream is printed at the terminal: (define input-prompt \";;; Query input:\")   (define output-prompt \";;; Query results:\")   (define (query-driver-loop)    (prompt-for-input input-prompt)     (let ((q (query-syntax-process (read))))       (cond ((assertion-to-be-added? q)              (add-rule-or-assertion! (add-assertion-body q))              (newline)              (display \"Assertion added to data base.\")              (query-driver-loop))             (else              (newline)              (display output-prompt)              (display-stream               (stream-map                (lambda (frame)                  (instantiate q                               frame                               (lambda (v f)                                 (contract-question-mark v))))                (qeval q (singleton-stream '()))))              (query-driver-loop)))))   Here, as in the other evaluators in this chapter , we use an abstract syntax for the expressions of the query language. The implementation of the expr"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6932864e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ession syntax, including the predicate assertion-to-be-added?  and the selector add-assertion-body , is given in section 4.4.4.7 . Add-rule-or- assertion!  is defined in section 4.4.4.5 . Before doing any processing on an input expression, the driver loop transforms it syntactically into a form that makes the processing more efficient. This involves changing the representation of pattern variables. When the query is instantiated, any variables that remain unbound are transformed back to the input representation before being printed. These transformations are performed by the two procedures query-syntax- process  and contract-question-mark  (section  4.4.4.7 ). To instantiate an expression, we copy it, replacing any variables in the expression by their values in a given frame. The values are themselves instantiated, since they could contain variables (for example, if ?x in exp isbound to ?y as the result of unification and ?y is in turn bound to 5). The action to take if a variable cannot be instantiated is gi"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6933888v
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ven by a procedural argument to instantiate . (define (instantiate exp frame unbound-var-handler)     (define (copy exp)       (cond ((var? exp)              (let ((binding (binding-in-frame exp frame)))                (if binding                    (copy (binding-value binding))                    (unbound-var-handler exp frame))))             ((pair? exp)              (cons (copy (car exp)) (copy (cdr exp))))             (else exp)))     (copy exp))   The procedures that manipulate bindings are defined in section 4.4.4.8 . 4.4.4.2  The Evaluator The qeval  procedure, called by the query-driver-loop , is the basic evaluator of the query system. It takes as inputs a query and a stream of frames, and it returns a stream of extended frames. It identifies special forms by a data-directed dispatch using get and put, just as we did in implementing generic operations in chapter 2. Any query that is not identified as a special form is assumed to be a simple query , to be processed by simple-query . (define (qeval qu"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6934912e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ery frame-stream)     (let ((qproc (get (type query) 'qeval)))       (if qproc           (qproc (contents query) frame-stream)           (simple-query query frame-stream))))   Type  and contents , defined in section 4.4.4.7 , implement the abstract syntax of the special forms. Simple queriesThe simple-query  procedure handles simple queries. It takes as ar guments a simple query (a pattern) together with a stream of frames, and it returns the stream formed by extending each frame by all data-base matches of the query . (define (simple-query query-pattern frame-stream)     (stream-flatmap      (lambda (frame)        (stream-append-delayed         (find-assertions query-pattern frame)         (delay (apply-rules query-pattern frame))))      frame-stream))   For each frame in the input stream, we use find-assertions (section 4.4.4.3 ) to match the pattern against all assertions in the data base, producing a stream of extended frames, and we use apply-rules (section 4.4.4.4 ) to apply all possible rules, producin"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6935936g
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "g another stream of extended frames. These two streams are combined (using stream-append- delayed , section 4.4.4.6 ) to make a stream of all the ways that the given pattern can be satisfied consistent with the original frame (see exercise 4.71). The streams for the individual input frames are combined using stream-flatmap  (section 4.4.4.6 ) to form one lar ge stream of all the ways that any of the frames in the original input stream can be extended to produce a match with the given pattern. Compound queries And queries are handled as illustrated in figure 4.5 by the conjoin procedure. Conjoin  takes as inputs the conjuncts and the frame stream and returns the stream of extended frames. First, conjoin  processes the stream of frames to find the stream of all possible frame extensions that satisfy the first query in the conjunction. Then, using this as the new frame stream, it recursively applies conjoin  to the rest of the queries. (define (conjoin conjuncts frame-stream)     (if (empty-conjunction? conjunct"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6936960s
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "s)         frame-stream        (conjoin (rest-conjuncts conjuncts)                  (qeval (first-conjunct conjuncts)                         frame-stream))))   The expression (put 'and 'qeval conjoin)   sets up qeval  to dispatch to conjoin  when an and form is encountered. Or queries are handled similarly , as shown in figure 4.6. The output streams for the various disjuncts of the or are computed separately and mer ged using the interleave-delayed  procedure from section 4.4.4.6 . (See exercises 4.71 and 4.72.) (define (disjoin disjuncts frame-stream)     (if (empty-disjunction? disjuncts)         the-empty-stream         (interleave-delayed          (qeval (first-disjunct disjuncts) frame-stream)          (delay (disjoin (rest-disjuncts disjuncts)                          frame-stream)))))   (put 'or 'qeval disjoin)   The predicates and selectors for the syntax of conjuncts and disjuncts are given in section 4.4.4.7 . Filters Not is handled by the method outlined in section 4.4.2 . We attempt to extend ea"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6937984c
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ch frame in the input stream to satisfy the query being negated, and we include a given frame in the output stream only if it cannot be extended. (define (negate operands frame-stream)     (stream-flatmap      (lambda (frame)        (if (stream-null? (qeval (negated-query operands)                                (singleton-stream frame)))            (singleton-stream frame)            the-empty-stream))      frame-stream))   (put 'not 'qeval negate)   Lisp-value  is a filter similar to not. Each frame in the stream is used to instantiate the variables in the pattern, the indicated predicate is applied, and the frames for which the predicate returns false are filtered out of the input stream. An error results if there are unbound pattern variables. (define (lisp-value call frame-stream)     (stream-flatmap      (lambda (frame)        (if (execute             (instantiate              call              frame              (lambda (v f)                (error \"Unknown pat var -- LISP-VALUE\" v))))            (singl"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6939008e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "eton-stream frame)            the-empty-stream))      frame-stream))   (put 'lisp-value 'qeval lisp-value)   Execute , which applies the predicate to the ar guments, must eval  the predicate expression to get the procedure to apply . However , it must not evaluate the ar guments, since they are already the actual ar guments, not expressions whose evaluation (in Lisp) will produce the ar guments. Note that execute  is implemented using eval  and apply  from the underlying Lisp system. (define (execute exp)     (apply (eval (predicate exp) user-initial-environment)            (args exp)))   The always-true  special form provides for a query that is always satisfied. It ignores its contents (normally empty) and simply passes through all theframes in the input stream. Always-true  is used by the rule-body  selector (section 4.4.4.7 ) to provide bodies for rules that were defined without bodies (that is, rules whose conclusions are always satisfied). (define (always-true ignore frame-stream) frame-stream)   (put '"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6940032a
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "always-true 'qeval always-true)   The selectors that define the syntax of not and lisp-value  are given in section 4.4.4.7 . 4.4.4.3  Finding Assertions by Pattern Matching Find-assertions , called by simple-query  (section 4.4.4.2 ), takes as input a pattern and a frame. It returns a stream of frames, each extending the given one by a data-base match of the given pattern. It uses fetch- assertions  (section 4.4.4.5 ) to get a stream of all the assertions in the data base that should be checked for a match against the pattern and the frame. The reason for fetch-assertions  here is that we can often apply simple tests that will eliminate many of the entries in the data base from the pool of candidates for a successful match. The system would still work if we eliminated fetch-assertions  and simply checked a stream of all assertions in the data base, but the computation would be less ef ficient because we would need to make many more calls to the matcher . (define (find-assertions pattern frame)     (stream-fla"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6941056t
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "tmap (lambda (datum)                       (check-an-assertion datum pattern frame))                     (fetch-assertions pattern frame)))   Check-an-assertion  takes as ar guments a pattern, a data object (assertion), and a frame and returns either a one-element stream containing the extended frame or the-empty-stream  if the match fails. (define (check-an-assertion assertion query-pat query-frame)     (let ((match-result            (pattern-match query-pat assertion query-frame)))       (if (eq? match-result 'failed)          the-empty-stream           (singleton-stream match-result))))   The basic pattern matcher returns either the symbol failed  or an extension of the given frame. The basic idea of the matcher is to check the pattern against the data, element by element, accumulating bindings for the pattern variables. If the pattern and the data object are the same, the match succeeds and we return the frame of bindings accumulated so far . Otherwise, if the pattern is a variable we extend the current f"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6942080r
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "rame by binding the variable to the data, so long as this is consistent with the bindings already in the frame. If the pattern and the data are both pairs, we (recursively) match the car of the pattern against the car of the data to produce a frame; in this frame we then match the cdr of the pattern against the cdr of the data. If none of these cases are applicable, the match fails and we return the symbol failed . (define (pattern-match pat dat frame)     (cond ((eq? frame 'failed) 'failed)           ((equal? pat dat) frame)           ((var? pat) (extend-if-consistent pat dat frame))           ((and (pair? pat) (pair? dat))            (pattern-match (cdr pat)                           (cdr dat)                           (pattern-match (car pat)                                          (car dat)                                          frame)))           (else 'failed)))   Here is the procedure that extends a frame by adding a new binding, if this is consistent with the bindings already in the frame: (define "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6943104(
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "(extend-if-consistent var dat frame)     (let ((binding (binding-in-frame var frame)))       (if binding           (pattern-match (binding-value binding) dat frame)           (extend var dat frame))))   If there is no binding for the variable in the frame, we simply add the binding of the variable to the data. Otherwise we match, in the frame, thedata against the value of the variable in the frame. If the stored value contains only constants, as it must if it was stored during pattern matching by extend-if-consistent , then the match simply tests whether the stored and new values are the same. If so, it returns the unmodified frame; if not, it returns a failure indication. The stored value may , however , contain pattern variables if it was stored during unification (see section 4.4.4.4 ). The recursive match of the stored pattern against the new data will add or check bindings for the variables in this pattern. For example, suppose we have a frame in which ?x is bound to (f ?y)  and ?y is unbound, and we wis"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6944128h
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "h to augment this frame by a binding of ?x to (f b) . We look up ?x and find that it is bound to (f ?y) . This leads us to match (f ?y)  against the proposed new value (f b)  in the same frame. Eventually this match extends the frame by adding a binding of ?y to b. ?X remains bound to (f ? y). We never modify a stored binding and we never store more than one binding for a given variable. The procedures used by extend-if-consistent  to manipulate bindings are defined in section 4.4.4.8 . Patterns with dotted tails If a pattern contains a dot followed by a pattern variable, the pattern variable matches the rest of the data list (rather than the next element of the data list), just as one would expect with the dotted-tail notation described in exercise 2.20. Although the pattern matcher we have just implemented doesn't look for dots, it does behave as we want. This is because the Lisp read  primitive, which is used by query-driver-loop  to read the query and represent it as a list structure, treats dots in a spe"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6945152c
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "cial way . When read  sees a dot, instead of making the next item be the next element of a list (the car of a cons  whose cdr will be the rest of the list) it makes the next item be the cdr of the list structure. For example, the list structure produced by read  for the pattern (computer ?type)  could be constructed by evaluating the expression (cons 'computer (cons '?type '())) , and that for (computer . ?type)  could be constructed by evaluating the expression (cons 'computer '?type) .Thus, as pattern-match  recursively compares cars and cdrs of a data list and a pattern that had a dot, it eventually matches the variable after the dot (which is a cdr of the pattern) against a sublist of the data list, binding the variable to that list. For example, matching the pattern (computer . ? type)  against (computer programmer trainee)  will match ?type  against the list (programmer trainee) . 4.4.4.4  Rules and Unification Apply-rules  is the rule analog of find-assertions  (section 4.4.4.3 ). It takes as input a p"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6946176a
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "attern and a frame, and it forms a stream of extension frames by applying rules from the data base. Stream-flatmap  maps apply- a-rule  down the stream of possibly applicable rules (selected by fetch- rules , section 4.4.4.5 ) and combines the resulting streams of frames. (define (apply-rules pattern frame)     (stream-flatmap (lambda (rule)                       (apply-a-rule rule pattern frame))                     (fetch-rules pattern frame)))   Apply-a-rule  applies rules using the method outlined in section 4.4.2 . It first augments its ar gument frame by unifying the rule conclusion with the pattern in the given frame. If this succeeds, it evaluates the rule body in this new frame. Before any of this happens, however , the program renames all the variables in the rule with unique new names. The reason for this is to prevent the variables for dif ferent rule applications from becoming confused with each other . For instance, if two rules both use a variable named ?x, then each one may add a binding for ?"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6947200x
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "x to the frame when it is applied. These two ?x's have nothing to do with each other , and we should not be fooled into thinking that the two bindings must be consistent. Rather than rename variables, we could devise a more clever environment structure; however , the renaming approach we have chosen here is the most straightforward, even if not the most ef ficient. (See exercise 4.79.) Here is the apply-a-rule procedure:(define (apply-a-rule rule query-pattern query-frame)     (let ((clean-rule (rename-variables-in rule)))       (let ((unify-result              (unify-match query-pattern                           (conclusion clean-rule)                           query-frame)))         (if (eq? unify-result 'failed)             the-empty-stream             (qeval (rule-body clean-rule)                    (singleton-stream unify-result))))))   The selectors rule-body  and conclusion  that extract parts of a rule are defined in section 4.4.4.7 . We generate unique variable names by associating a unique identifie"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6948224r
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "r (such as a number) with each rule application and combining this identifier with the original variable names. For example, if the rule-application identifier is 7, we might change each ?x in the rule to ?x-7  and each ?y in the rule to ? y-7. (Make-new-variable  and new-rule-application-id  are included with the syntax procedures in section 4.4.4.7 .) (define (rename-variables-in rule)     (let ((rule-application-id (new-rule-application-id)))       (define (tree-walk exp)         (cond ((var? exp)                (make-new-variable exp rule-application-id))               ((pair? exp)                (cons (tree-walk (car exp))                      (tree-walk (cdr exp))))               (else exp)))       (tree-walk rule)))   The unification algorithm is implemented as a procedure that takes as inputs two patterns and a frame and returns either the extended frame or the symbol failed . The unifier is like the pattern matcher except that it is symmetrical \u2013 variables are allowed on both sides of the match. Unif"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6949248y
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "y- match  is basically the same as pattern-match , except that there is extracode (marked \u201c ***\u201d below) to handle the case where the object on the right side of the match is a variable. (define (unify-match p1 p2 frame)     (cond ((eq? frame 'failed) 'failed)           ((equal? p1 p2) frame)           ((var? p1) (extend-if-possible p1 p2 frame))           ((var? p2) (extend-if-possible p2 p1 frame))  ; ***          ((and (pair? p1) (pair? p2))            (unify-match (cdr p1)                         (cdr p2)                         (unify-match (car p1)                                      (car p2)                                      frame)))           (else 'failed)))   In unification, as in one-sided pattern matching, we want to accept a proposed extension of the frame only if it is consistent with existing bindings. The procedure extend-if-possible  used in unification is the same as the extend-if-consistent  used in pattern matching except for two special checks, marked \u201c ***\u201d in the program below . In t"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6950272h
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "he first case, if the variable we are trying to match is not bound, but the value we are trying to match it with is itself a (dif ferent) variable, it is necessary to check to see if the value is bound, and if so, to match its value. If both parties to the match are unbound, we may bind either to the other . The second check deals with attempts to bind a variable to a pattern that includes that variable. Such a situation can occur whenever a variable is repeated in both patterns. Consider , for example, unifying the two patterns (?x ?x)  and (?y <expression involving ?y>) in a frame where both ?x and ?y are unbound. First ?x is matched against ?y, making a binding of ?x to ?y. Next, the same ?x is matched against the given expression involving ?y. Since ?x is already bound to ?y, this results in matching ?y against the expression. If we think of the unifier as finding a set of values for the pattern variables that make the patterns the same, then these patterns imply instructions to find a ?y such that ?y is "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6951296e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "equal to the expression involving ?y. There is no general method for solving such equations, so we reject suchbindings; these cases are recognized by the predicate depends-on? .80 On the other hand, we do not want to reject attempts to bind a variable to itself. For example, consider unifying (?x ?x)  and (?y ?y) . The second attempt to bind ?x to ?y matches ?y (the stored value of ?x) against ?y (the new value of ?x). This is taken care of by the equal?  clause of unify-match . (define (extend-if-possible var val frame)     (let ((binding (binding-in-frame var frame)))       (cond (binding              (unify-match               (binding-value binding) val frame))             ((var? val)                      ; ***             (let ((binding (binding-in-frame val frame)))                (if binding                    (unify-match                     var (binding-value binding) frame)                    (extend var val frame))))             ((depends-on? val var frame)     ; ***             'failed)           "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6952320
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "  (else (extend var val frame)))))   Depends-on?  is a predicate that tests whether an expression proposed to be the value of a pattern variable depends on the variable. This must be done relative to the current frame because the expression may contain occurrences of a variable that already has a value that depends on our test variable. The structure of depends-on?  is a simple recursive tree walk in which we substitute for the values of variables whenever necessary . (define (depends-on? exp var frame)     (define (tree-walk e)       (cond ((var? e)              (if (equal? var e)                  true                  (let ((b (binding-in-frame e frame)))                    (if b                        (tree-walk (binding-value b))                        false))))            ((pair? e)              (or (tree-walk (car e))                  (tree-walk (cdr e))))             (else false)))     (tree-walk exp))   4.4.4.5  Maintaining the Data Base One important problem in designing logic programming languages i"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6953344s
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "s that of arranging things so that as few irrelevant data-base entries as possible will be examined in checking a given pattern. In our system, in addition to storing all assertions in one big stream, we store all assertions whose cars are constant symbols in separate streams, in a table indexed by the symbol. To fetch an assertion that may match a pattern, we first check to see if the car of the pattern is a constant symbol. If so, we return (to be tested using the matcher) all the stored assertions that have the same car. If the pattern's car is not a constant symbol, we return all the stored assertions. Cleverer methods could also take advantage of information in the frame, or try also to optimize the case where the car of the pattern is not a constant symbol. We avoid building our criteria for indexing (using the car, handling only the case of constant symbols) into the program; instead we call on predicates and selectors that embody our criteria. (define THE-ASSERTIONS the-empty-stream)   (define (fetch-"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6954368a
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "assertions pattern frame)     (if (use-index? pattern)         (get-indexed-assertions pattern)         (get-all-assertions)))   (define (get-all-assertions) THE-ASSERTIONS)   (define (get-indexed-assertions pattern)     (get-stream (index-key-of pattern) 'assertion-stream))   Get-stream  looks up a stream in the table and returns an empty stream if nothing is stored there. (define (get-stream key1 key2)     (let ((s (get key1 key2)))      (if s s the-empty-stream)))   Rules are stored similarly , using the car of the rule conclusion. Rule conclusions are arbitrary patterns, however , so they dif fer from assertions in that they can contain variables. A pattern whose car is a constant symbol can match rules whose conclusions start with a variable as well as rules whose conclusions have the same car. Thus, when fetching rules that might match a pattern whose car is a constant symbol we fetch all rules whose conclusions start with a variable as well as those whose conclusions have the same car as the pattern. F"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6955392o
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "or this purpose we store all rules whose conclusions start with a variable in a separate stream in our table, indexed by the symbol ?. (define THE-RULES the-empty-stream)   (define (fetch-rules pattern frame)     (if (use-index? pattern)         (get-indexed-rules pattern)         (get-all-rules)))   (define (get-all-rules) THE-RULES)   (define (get-indexed-rules pattern)     (stream-append      (get-stream (index-key-of pattern) 'rule-stream)      (get-stream '? 'rule-stream)))   Add-rule-or-assertion!  is used by query-driver-loop  to add assertions and rules to the data base. Each item is stored in the index, if appropriate, and in a stream of all assertions or rules in the data base. (define (add-rule-or-assertion! assertion)     (if (rule? assertion)         (add-rule! assertion)         (add-assertion! assertion)))   (define (add-assertion! assertion)     (store-assertion-in-index assertion)     (let ((old-assertions THE-ASSERTIONS))       (set! THE-ASSERTIONS             (cons-stream assertion old-asse"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6956416r
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "rtions))       'ok))  (define (add-rule! rule)     (store-rule-in-index rule)     (let ((old-rules THE-RULES))       (set! THE-RULES (cons-stream rule old-rules))       'ok))   To actually store an assertion or a rule, we check to see if it can be indexed. If so, we store it in the appropriate stream. (define (store-assertion-in-index assertion)     (if (indexable? assertion)         (let ((key (index-key-of assertion)))           (let ((current-assertion-stream                  (get-stream key 'assertion-stream)))             (put key                  'assertion-stream                  (cons-stream assertion                               current-assertion-stream))))))   (define (store-rule-in-index rule)     (let ((pattern (conclusion rule)))       (if (indexable? pattern)           (let ((key (index-key-of pattern)))             (let ((current-rule-stream                    (get-stream key 'rule-stream)))               (put key                    'rule-stream                    (cons-stream rule            "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6957440
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "                     current-rule-stream)))))))   The following procedures define how the data-base index is used. A pattern (an assertion or a rule conclusion) will be stored in the table if it starts with a variable or a constant symbol. (define (indexable? pat)     (or (constant-symbol? (car pat))         (var? (car pat))))  The key under which a pattern is stored in the table is either ? (if it starts with a variable) or the constant symbol with which it starts. (define (index-key-of pat)     (let ((key (car pat)))       (if (var? key) '? key)))   The index will be used to retrieve items that might match a pattern if the pattern starts with a constant symbol. (define (use-index? pat)     (constant-symbol? (car pat)))   Exer cise 4.70.   What is the purpose of the let bindings in the procedures add-assertion!  and add-rule!  ? What would be wrong with the following implementation of add-assertion!  ? Hint: Recall the definition of the infinite stream of ones in section 3.5.2 : (define ones (cons-stream 1 o"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6958464n
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "nes)) . (define (add-assertion! assertion)     (store-assertion-in-index assertion)     (set! THE-ASSERTIONS           (cons-stream assertion THE-ASSERTIONS))     'ok)  4.4.4.6  Str eam Operations The query system uses a few stream operations that were not presented in chapter 3. Stream-append-delayed  and interleave-delayed  are just like stream- append  and interleave  (section 3.5.3 ), except that they take a delayed argument (like the integral  procedure in section 3.5.4 ). This postpones looping in some cases (see exercise 4.71). (define (stream-append-delayed s1 delayed-s2)     (if (stream-null? s1)         (force delayed-s2)        (cons-stream          (stream-car s1)          (stream-append-delayed (stream-cdr s1) delayed-s2))))   (define (interleave-delayed s1 delayed-s2)     (if (stream-null? s1)         (force delayed-s2)         (cons-stream          (stream-car s1)          (interleave-delayed (force delayed-s2)                              (delay (stream-cdr s1))))))   Stream-flatmap , which is"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6959488
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " used throughout the query evaluator to map a procedure over a stream of frames and combine the resulting streams of frames, is the stream analog of the flatmap  procedure introduced for ordinary lists in section 2.2.3 . Unlike ordinary flatmap , however , we accumulate the streams with an interleaving process, rather than simply appending them (see exercises 4.72 and  4.73). (define (stream-flatmap proc s)     (flatten-stream (stream-map proc s)))   (define (flatten-stream stream)     (if (stream-null? stream)         the-empty-stream         (interleave-delayed          (stream-car stream)          (delay (flatten-stream (stream-cdr stream))))))   The evaluator also uses the following simple procedure to generate a stream consisting of a single element: (define (singleton-stream x)     (cons-stream x the-empty-stream))   4.4.4.7  Query Syntax Pr ocedur es Type  and contents , used by qeval  (section 4.4.4.2 ), specify that a special form is identified by the symbol in its car. They are the same as the type-"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6960512
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " tag and contents  procedures in section 2.4.2 , except for the error message.(define (type exp)     (if (pair? exp)         (car exp)         (error \"Unknown expression TYPE\" exp)))   (define (contents exp)     (if (pair? exp)         (cdr exp)         (error \"Unknown expression CONTENTS\" exp)))   The following procedures, used by query-driver-loop  (in section 4.4.4.1 ), specify that rules and assertions are added to the data base by expressions of the form (assert! < rule-or-assertion >): (define (assertion-to-be-added? exp)     (eq? (type exp) 'assert!))   (define (add-assertion-body exp)     (car (contents exp)))   Here are the syntax definitions for the and, or, not, and lisp-value  special forms (section 4.4.4.2 ): (define (empty-conjunction? exps) (null? exps))   (define (first-conjunct exps) (car exps))   (define (rest-conjuncts exps) (cdr exps))   (define (empty-disjunction? exps) (null? exps))   (define (first-disjunct exps) (car exps))   (define (rest-disjuncts exps) (cdr exps))   (define (negated"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6961536-
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "-query exps) (car exps))   (define (predicate exps) (car exps))   (define (args exps) (cdr exps))   The following three procedures define the syntax of rules: (define (rule? statement)     (tagged-list? statement 'rule))   (define (conclusion rule) (cadr rule))   (define (rule-body rule)     (if (null? (cddr rule))        '(always-true)         (caddr rule)))   Query-driver-loop  (section 4.4.4.1 ) calls query-syntax-process  to transform pattern variables in the expression, which have the form ?symbol , into the internal format (? symbol) . That is to say , a pattern such as (job ? x ?y)  is actually represented internally by the system as (job (? x) (? y)). This increases the ef ficiency of query processing, since it means that the system can check to see if an expression is a pattern variable by checking whether the car of the expression is the symbol ?, rather than having to extract characters from the symbol. The syntax transformation is accomplished by the following procedure:81 (define (query-syntax-pr"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6962560o
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ocess exp)     (map-over-symbols expand-question-mark exp))   (define (map-over-symbols proc exp)     (cond ((pair? exp)            (cons (map-over-symbols proc (car exp))                  (map-over-symbols proc (cdr exp))))           ((symbol? exp) (proc exp))           (else exp)))   (define (expand-question-mark symbol)     (let ((chars (symbol->string symbol)))       (if (string=? (substring chars 0 1) \"?\")           (list '?                 (string->symbol                  (substring chars 1 (string-length chars))))           symbol)))   Once the variables are transformed in this way , the variables in a pattern are lists starting with ?, and the constant symbols (which need to be recognized for data-base indexing, section 4.4.4.5 ) are just the symbols. (define (var? exp)     (tagged-list? exp '?))   (define (constant-symbol? exp) (symbol? exp))  Unique variables are constructed during rule application (in section 4.4.4.4 ) by means of the following procedures. The unique identifier for a rule applicati"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6963584o
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "on is a number , which is incremented each time a rule is applied. (define rule-counter 0)   (define (new-rule-application-id)     (set! rule-counter (+ 1 rule-counter))     rule-counter)   (define (make-new-variable var rule-application-id)     (cons '? (cons rule-application-id (cdr var))))   When query-driver-loop  instantiates the query to print the answer , it converts any unbound pattern variables back to the right form for printing, using (define (contract-question-mark variable)     (string->symbol      (string-append \"?\"        (if (number? (cadr variable))            (string-append (symbol->string (caddr variable))                           \"-\"                           (number->string (cadr variable)))            (symbol->string (cadr variable))))))   4.4.4.8  Frames and Bindings Frames are represented as lists of bindings, which are variable-value pairs: (define (make-binding variable value)     (cons variable value))   (define (binding-variable binding)     (car binding))   (define (binding-value"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6964608
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " binding)     (cdr binding))   (define (binding-in-frame variable frame)     (assoc variable frame))   (define (extend variable value frame)     (cons (make-binding variable value) frame))  Exer cise 4.71.   Louis Reasoner wonders why the simple-query  and disjoin  procedures (section 4.4.4.2 ) are implemented using explicit delay operations, rather than being defined as follows: (define (simple-query query-pattern frame-stream)     (stream-flatmap      (lambda (frame)        (stream-append (find-assertions query-pattern frame)                       (apply-rules query-pattern frame)))      frame-stream))   (define (disjoin disjuncts frame-stream)     (if (empty-disjunction? disjuncts)         the-empty-stream         (interleave          (qeval (first-disjunct disjuncts) frame-stream)          (disjoin (rest-disjuncts disjuncts) frame-stream))))   Can you give examples of queries where these simpler definitions would lead to undesirable behavior? Exer cise 4.72.   Why do disjoin  and stream-flatmap  interleav"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6965632e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "e the streams rather than simply append them? Give examples that illustrate why interleaving works better . (Hint: Why did we use interleave  in section 3.5.3 ?) Exer cise 4.73.   Why does flatten-stream  use delay  explicitly? What would be wrong with defining it as follows: (define (flatten-stream stream)     (if (stream-null? stream)         the-empty-stream         (interleave          (stream-car stream)          (flatten-stream (stream-cdr stream)))))   Exer cise 4.74.   Alyssa P . Hacker proposes to use a simpler version of stream-flatmap  in negate , lisp-value , and find-assertions . She observes that the procedure that is mapped over the frame stream in thesecases always produces either the empty stream or a singleton stream, so no interleaving is needed when combining these streams. a. Fill in the missing expressions in Alyssa's program. (define (simple-stream-flatmap proc s)     (simple-flatten (stream-map proc s)))     (define (simple-flatten stream)     (stream-map < ??>                (stream-f"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6966656i
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ilter < ??> stream)))   b. Does the query system's behavior change if we change it in this way? Exer cise 4.75.   Implement for the query language a new special form called unique . Unique  should succeed if there is precisely one item in the data base satisfying a specified query . For example, (unique (job ?x (computer wizard)))   should print the one-item stream (unique (job (Bitdiddle Ben) (computer wizard)))   since Ben is the only computer wizard, and (unique (job ?x (computer programmer)))   should print the empty stream, since there is more than one computer programmer . Moreover , (and (job ?x ?j) (unique (job ?anyone ?j)))   should list all the jobs that are filled by only one person, and the people who fill them. There are two parts to implementing unique . The first is to write a procedure that handles this special form, and the second is to make qeval dispatch to that procedure. The second part is trivial, since qeval  does itsdispatching in a data-directed way . If your procedure is called uniqu"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6967680e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ely- asserted , all you need to do is (put 'unique 'qeval uniquely-asserted)   and qeval  will dispatch to this procedure for every query whose type  (car) is the symbol unique . The real problem is to write the procedure uniquely-asserted . This should take as input the contents  (cdr) of the unique  query , together with a stream of frames. For each frame in the stream, it should use qeval  to find the stream of all extensions to the frame that satisfy the given query . Any stream that does not have exactly one item in it should be eliminated. The remaining streams should be passed back to be accumulated into one big stream that is the result of the unique  query . This is similar to the implementation of the not special form. Test your implementation by forming a query that lists all people who supervise precisely one person. Exer cise 4.76.   Our implementation of and as a series combination of queries (figure 4.5) is elegant, but it is inef ficient because in processing the second query of the and we mus"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6968704t
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "t scan the data base for each frame produced by the first query . If the data base has N elements, and a typical query produces a number of output frames proportional to N (say N/k), then scanning the data base for each frame produced by the first query will require N2/k calls to the pattern matcher . Another approach would be to process the two clauses of the and separately , then look for all pairs of output frames that are compatible. If each query produces N/k output frames, then this means that we must perform N2/k2 compatibility checks \u2013 a factor of k fewer than the number of matches required in our current method. Devise an implementation of and that uses this strategy . You must implement a procedure that takes two frames as inputs, checks whether the bindings in the frames are compatible, and, if so, produces a frame that merges the two sets of bindings. This operation is similar to unification.Exer cise 4.77.   In section 4.4.3  we saw that not and lisp-value  can cause the query language to give \u201cw"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6969728r
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "rong\u201d answers if these filtering operations are applied to frames in which variables are unbound. Devise a way to fix this shortcoming. One idea is to perform the filtering in a \u201cdelayed\u201d manner by appending to the frame a \u201cpromise\u201d to filter that is fulfilled only when enough variables have been bound to make the operation possible. We could wait to perform filtering until all other operations have been performed. However , for ef ficiency's sake, we would like to perform filtering as soon as possible so as to cut down on the number of intermediate frames generated. Exer cise 4.78.   Redesign the query language as a nondeterministic program to be implemented using the evaluator of section 4.3, rather than as a stream process. In this approach, each query will produce a single answer (rather than the stream of all answers) and the user can type try-again  to see more answers. You should find that much of the mechanism we built in this section is subsumed by nondeterministic search and backtracking. You will p"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6970752r
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "robably also find, however , that your new query language has subtle differences in behavior from the one implemented here. Can you find examples that illustrate this dif ference? Exer cise 4.79.   When we implemented the Lisp evaluator in section 4.1, we saw how to use local environments to avoid name conflicts between the parameters of procedures. For example, in evaluating (define (square x)     (* x x))   (define (sum-of-squares x y)     (+ (square x) (square y)))   (sum-of-squares 3 4)   there is no confusion between the x in square  and the x in sum-of-squares , because we evaluate the body of each procedure in an environment that is specially constructed to contain bindings for the local variables. In the query system, we used a dif ferent strategy to avoid name conflicts in applying rules. Each time we apply a rule we rename the variables with new names that are guaranteed to be unique. The analogous strategy for the Lispevaluator would be to do away with local environments and simply rename the varia"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6971776b
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "bles in the body of a procedure each time we apply the procedure. Implement for the query language a rule-application method that uses environments rather than renaming. See if you can build on your environment structure to create constructs in the query language for dealing with lar ge systems, such as the rule analog of block-structured procedures. Can you relate any of this to the problem of making deductions in a context (e.g., \u201cIf I supposed that P were true, then I would be able to deduce A and B.\u201d) as a method of problem solving? (This problem is open-ended. A good answer is probably worth a Ph.D.) 58 Logic programming has grown out of a long history of research in automatic theorem proving. Early theorem-proving programs could accomplish very little, because they exhaustively searched the space of possible proofs. The major breakthrough that made such a search plausible was the discovery in the early 1960s of the unification algorithm  and the resolution principle  (Robinson 1965). Resolution was used"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6972800,
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": ", for example, by Green and Raphael (1968) (see also Green 1969) as the basis for a deductive question-answering system. During most of this period, researchers concentrated on algorithms that are guaranteed to find a proof if one exists. Such algorithms were difficult to control and to direct toward a proof. Hewitt (1969) recognized the possibility of mer ging the control structure of a programming language with the operations of a logic-manipulation system, leading to the work in automatic search mentioned in section 4.3.1  (footnote 47). At the same time that this was being done, Colmerauer , in Marseille, was developing rule-based systems for manipulating natural language (see Colmerauer et al. 1973). He invented a programming language called Prolog for representing those rules. Kowalski (1973; 1979), in Edinbur gh, recognized that execution of a Prolog program could be interpreted as proving theorems (using a proof technique called linear Horn-clause resolution). The mer ging of the last two strands led "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6973824t
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "to the logic- programming movement. Thus, in assigning credit for the development of logic programming, the French can point to Prolog's genesis at the University of Marseille, while the British can highlight the work at the University of Edinbur gh. According to people at MIT, logic programming was developed by these groups in an attempt to figure out what Hewitt was talking about in his brilliant but impenetrable Ph.D. thesis. For a history of logic programming, see Robinson 1983. 59 To see the correspondence between the rules and the procedure, let x in the procedure (where x is nonempty) correspond to (cons u v)  in the rule. Then z in the rule corresponds to the append  of (cdr x)  and y. 60 This certainly does not relieve the user of the entire problem of how to compute the answer . There are many dif ferent mathematically equivalent sets of rules for formulating the append  relation, only some of which can be turned into ef fective devices for computing in any direction. In addition, sometimes \u201cwhat is"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6974848”
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "\u201d information gives no clue \u201chow to\u201d compute an answer . For example, consider the problem of computing the y such that y2 = x. 61 Interest in logic programming peaked during the early 80s when the Japanese government began an ambitious project aimed at building superfast computers optimized to run logic programminglanguages. The speed of such computers was to be measured in LIPS (Logical Inferences Per Second) rather than the usual FLOPS (FLoating-point Operations Per Second). Although the project succeeded in developing hardware and software as originally planned, the international computer industry moved in a dif ferent direction. See Feigenbaum and Shrobe 1993 for an overview evaluation of the Japanese project. The logic programming community has also moved on to consider relational programming based on techniques other than simple pattern matching, such as the ability to deal with numerical constraints such as the ones illustrated in the constraint-propagation system of section 3.3.5 . 62 This uses the d"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6975872o
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "otted-tail notation introduced in exercise 2.20. 63 Actually , this description of not is valid only for simple cases. The real behavior of not is more complex. We will examine not's peculiarities in sections 4.4.2  and 4.4.3 . 64 Lisp-value  should be used only to perform an operation not provided in the query language. In particular , it should not be used to test equality (since that is what the matching in the query language is designed to do) or inequality (since that can be done with the same  rule shown below). 65 Notice that we do not need same  in order to make two things be the same: W e just use the same pattern variable for each \u2013 in ef fect, we have one thing instead of two things in the first place. For example, see ?town  in the lives-near  rule and ?middle-manager  in the wheel  rule below . Same  is useful when we want to force two things to be dif ferent, such as ?person-1  and ?person-2  in the lives-near  rule. Although using the same pattern variable in two parts of a query forces the sam"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6976896e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "e value to appear in both places, using dif ferent pattern variables does not force dif ferent values to appear . (The values assigned to dif ferent pattern variables may be the same or dif ferent.) 66 We will also allow rules without bodies, as in same , and we will interpret such a rule to mean that the rule conclusion is satisfied by any values of the variables. 67 Because matching is generally very expensive, we would like to avoid applying the full matcher to every element of the data base. This is usually arranged by breaking up the process into a fast, coarse match and the final match. The coarse match filters the data base to produce a small set of candidates for the final match. With care, we can arrange our data base so that some of the work of coarse matching can be done when the data base is constructed rather then when we want to select the candidates. This is called indexing  the data base. There is a vast technology built around data-base- indexing schemes. Our implementation, described in sect"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6977920i
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ion 4.4.4 , contains a simple-minded form of such an optimization. 68 But this kind of exponential explosion is not common in and queries because the added conditions tend to reduce rather than expand the number of frames produced. 69 There is a lar ge literature on data-base-management systems that is concerned with how to handle complex queries ef ficiently . 70 There is a subtle dif ference between this filter implementation of not and the usual meaning of not in mathematical logic. See section 4.4.3 . 71 In one-sided pattern matching, all the equations that contain pattern variables are explicit and already solved for the unknown (the pattern variable).72 Another way to think of unification is that it generates the most general pattern that is a specialization of the two input patterns. That is, the unification of (?x a)  and ((b ?y) ?z)  is ((b ? y) a) , and the unification of (?x a ?y)  and (?y ?z a) , discussed above, is (a a a) . For our implementation, it is more convenient to think of the result of "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6978944u
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "unification as a frame rather than a pattern. 73 Since unification is a generalization of matching, we could simplify the system by using the unifier to produce both streams. Treating the easy case with the simple matcher , however , illustrates how matching (as opposed to full-blown unification) can be useful in its own right. 74 The reason we use streams (rather than lists) of frames is that the recursive application of rules can generate infinite numbers of values that satisfy a query . The delayed evaluation embodied in streams is crucial here: The system will print responses one by one as they are generated, regardless of whether there are a finite or infinite number of responses. 75 That a particular method of inference is legitimate is not a trivial assertion. One must prove that if one starts with true premises, only true conclusions can be derived. The method of inference represented by rule applications is modus ponens , the familiar method of inference that says that if A is true and A implies B  i"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6979968s
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "s true, then we may conclude that B is true. 76 We must qualify this statement by agreeing that, in speaking of the \u201cinference\u201d accomplished by a logic program, we assume that the computation terminates. Unfortunately , even this qualified statement is false for our implementation of the query language (and also false for programs in Prolog and most other current logic programming languages) because of our use of not and lisp- value . As we will describe below , the not implemented in the query language is not always consistent with the not of mathematical logic, and lisp-value  introduces additional complications. We could implement a language consistent with mathematical logic by simply removing not and lisp-value  from the language and agreeing to write programs using only simple queries, and, and or. However , this would greatly restrict the expressive power of the language. One of the major concerns of research in logic programming is to find ways to achieve more consistency with mathematical logic witho"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6980992u
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ut unduly sacrificing expressive power . 77 This is not a problem of the logic but one of the procedural interpretation of the logic provided by our interpreter . We could write an interpreter that would not fall into a loop here. For example, we could enumerate all the proofs derivable from our assertions and our rules in a breadth-first rather than a depth-first order . However , such a system makes it more dif ficult to take advantage of the order of deductions in our programs. One attempt to build sophisticated control into such a program is described in deKleer et al. 1977. Another technique, which does not lead to such serious control problems, is to put in special knowledge, such as detectors for particular kinds of loops (exercise 4.67). However , there can be no general scheme for reliably preventing a system from going down infinite paths in performing deductions. Imagine a diabolical rule of the form \u201cT o show P(x) is true, show that P(f(x)) is true,\u201d for some suitably chosen function f. 78 Conside"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6982016r
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "r the query (not (baseball-fan (Bitdiddle Ben))) . The system finds that (baseball- fan (Bitdiddle Ben))  is not in the data base, so the empty frame does not satisfy the pattern and is not filtered out of the initial stream of frames. The result of the query is thus the empty frame, which is used to instantiate the input query to produce (not (baseball-fan (Bitdiddle Ben))) . 79 A discussion and justification of this treatment of not can be found in the article by Clark (1978).80 In general, unifying ?y with an expression involving ?y would require our being able to find a fixed point of the equation ?y = <expression involving ?y>. It is sometimes possible to syntactically form an expression that appears to be the solution. For example, ?y  =  (f ?y)  seems to have the fixed point (f (f (f ... ))) , which we can produce by beginning with the expression (f ?y)  and repeatedly substituting (f ?y)  for ?y. Unfortunately , not every such equation has a meaningful fixed point. The issues that arise here are simil"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6983040a
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ar to the issues of manipulating infinite series in mathematics. For example, we know that 2 is the solution to the equation y = 1 + y/2. Beginning with the expression 1 + y/2 and repeatedly substituting 1 + y/2 for y gives which leads to However , if we try the same manipulation beginning with the observation that - 1 is the solution to the equation y = 1 + 2y, we obtain which leads to Although the formal manipulations used in deriving these two equations are identical, the first result is a valid assertion about infinite series but the second is not. Similarly , for our unification results, reasoning with an arbitrary syntactically constructed expression may lead to errors. 81 Most Lisp systems give the user the ability to modify the ordinary read  procedure to perform such transformations by defining reader macr o characters . Quoted expressions are already handled in this way: The reader automatically translates 'expression  into (quote expression)  before the evaluator sees it. We could arrange for ?expr"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6984064e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ession  to be transformed into (? expression)  in the same way; however , for the sake of clarity we have included the transformation procedure here explicitly . Expand-question-mark  and contract-question-mark  use several procedures with string  in their names. These are Scheme primitives.Chapter 5 Computing with Register Machines My aim is to show that the heavenly machine is not a kind of divine, live being, but a kind of clockwork (and he who believes that a clock has soul attributes the maker's glory to the work), insofar as nearly all the manifold motions are caused by a most simple and material force, just as all motions of the clock are caused by a single weight. Johannes Kepler (letter to Herwart von Hohenbur g, 1605) We began this book by studying processes and by describing processes in terms of procedures written in Lisp. To explain the meanings of these procedures, we used a succession of models of evaluation: the substitution model of chapter 1, the environment model of chapter 3, and the metac"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6985088i
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ircular evaluator of chapter 4. Our examination of the metacircular evaluator , in particular , dispelled much of the mystery of how Lisp-like languages are interpreted. But even the metacircular evaluator leaves important questions unanswered, because it fails to elucidate the mechanisms of control in a Lisp system. For instance, the evaluator does not explain how the evaluation of a subexpression manages to return a value to the expression that uses this value, nor does the evaluator explain how some recursive procedures generate iterative processes (that is, are evaluated using constant space) whereas other recursive procedures generate recursive processes. These questions remain unanswered because the metacircular evaluator is itself a Lisp program and hence inherits the control structure of the underlying Lisp system. In order to provide a more complete description of the control structure of the Lisp evaluator , we must work at a more primitive level than Lisp itself. In this chapter we will describe pr"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6986112o
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ocesses in terms of the step-by-step operation of a traditional computer . Such a computer , or register machine , sequentially executes instructions  that manipulate the contents of a fixed set of storage elements called registers . A typical register -machine instruction applies a primitive operation to the contents of some registers and assignsthe result to another register . Our descriptions of processes executed by register machines will look very much like \u201cmachine-language\u201d programs for traditional computers. However , instead of focusing on the machine language of any particular computer , we will examine several Lisp procedures and design a specific register machine to execute each procedure. Thus, we will approach our task from the perspective of a hardware architect rather than that of a machine-language computer programmer . In designing register machines, we will develop mechanisms for implementing important programming constructs such as recursion. We will also present a language for describing "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6987136d
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "designs for register machines. In section 5.2 we will implement a Lisp program that uses these descriptions to simulate the machines we design. Most of the primitive operations of our register machines are very simple. For example, an operation might add the numbers fetched from two registers, producing a result to be stored into a third register . Such an operation can be performed by easily described hardware. In order to deal with list structure, however , we will also use the memory operations car, cdr, and cons , which require an elaborate storage-allocation mechanism. In section 5.3 we study their implementation in terms of more elementary operations. In section 5.4, after we have accumulated experience formulating simple procedures as register machines, we will design a machine that carries out the algorithm described by the metacircular evaluator of section 4.1. This will fill in the gap in our understanding of how Scheme expressions are interpreted, by providing an explicit model for the mechanisms o"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6988160f
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "f control in the evaluator . In section 5.5 we will study a simple compiler that translates Scheme programs into sequences of instructions that can be executed directly with the registers and operations of the evaluator register machine.5.1  Designing Register Machines To design a register machine, we must design its data paths  (registers and operations) and the contr oller  that sequences these operations. To illustrate the design of a simple register machine, let us examine Euclid's Algorithm, which is used to compute the greatest common divisor (GCD) of two integers. As we saw in section 1.2.5 , Euclid's Algorithm can be carried out by an iterative process, as specified by the following procedure: (define (gcd a b)     (if (= b 0)         a        (gcd b (remainder a b))))   A machine to carry out this algorithm must keep track of two numbers, a and b, so let us assume that these numbers are stored in two registers with those names. The basic operations required are testing whether the contents of registe"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6989184r
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "r b is zero and computing the remainder of the contents of register a divided by the contents of register b. The remainder operation is a complex process, but assume for the moment that we have a primitive device that computes remainders. On each cycle of the GCD algorithm, the contents of register a must be replaced by the contents of register b, and the contents of b must be replaced by the remainder of the old contents of a divided by the old contents of b. It would be convenient if these replacements could be done simultaneously , but in our model of register machines we will assume that only one register can be assigned a new value at each step. To accomplish the replacements, our machine will use a third \u201ctemporary\u201d register , which we call t. (First the remainder will be placed in t, then the contents of b will be placed in a, and finally the remainder stored in t will be placed in b.) We can illustrate the registers and operations required for this machine by using the data-path diagram shown in figur"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6990208e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "e 5.1. In this diagram, the registers ( a, b, and t) are represented by rectangles. Each way to assign a value to a register is indicated by an arrow with an X behind the head, pointing from the source of data to the register . We can think of the X as a button that, when pushed, allows the value at the source to \u201cflow\u201d into the designated register . The label next to each button is the name we will use to refer to the button. The names are arbitrary , and can be chosen to have mnemonic value (for example, a<-b  denotes pushing the button that assigns the contents of register b to register a). The source of data for a register can be another register (as in the a<-b assignment), an operation result (as in the t<-r  assignment), or a constant (a built-in value that cannot be changed, represented in a data-path diagram by a triangle containing the constant).An operation that computes a value from constants and the contents of registers is represented in a data-path diagram by a trapezoid containing a name for t"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6991232h
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "he operation. For example, the box marked rem in figure 5.1 represents an operation that computes the remainder of the contents of the registers a and b to which it is attached. Arrows (without buttons) point from the input registers and constants to the box, and arrows connect the operation's output value to registers. A test is represented by a circle containing a name for the test. For example, our GCD machine has an operation that tests whether the contents of register b is zero. A test also has arrows from its input registers and constants, but it has no output arrows; its value is used by the controller rather than by the data paths. Overall, the data-path diagram shows the registers and operations that are required for the machine and how they must be connected. If we view the arrows as wires and the X buttons as switches, the data-path diagram is very like the wiring diagram for a machine that could be constructed from electrical components. Figur e 5.1:   Data paths for a GCD machine. In order for th"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6992256e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "e data paths to actually compute GCDs, the buttons must be pushed in the correct sequence. We will describe this sequence in terms of a controller diagram, as illustrated in figure 5.2. The elements of the controller diagram indicate how the data- path components should be operated. The rectangular boxes in the controller diagram identify data-path buttons to be pushed, and the arrows describe the sequencing from one step to the next. The diamond in the diagram represents a decision. One of the two sequencing arrows will be followed, depending on the value of the data-path test identified in the diamond. We can interpret the controller in terms of a physical analogy: Think of the diagram as a maze in which a marble is rolling. When the marble rolls into a box, it pushes the data-path button that is named by the box. When the marble rolls into a decision node (such as the test for b = 0), it leaves the node on the path determined by the result of the indicated test. Taken together , the data paths and the cont"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6993280r
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "roller completely describe a machine for computing GCDs. We start the controller (the rolling marble) at the place marked start , after placing numbers in registers a and b. When the controller reaches done , we will find the value of the GCD in register a. Figur e 5.2:   Controller for a GCD machine.Exer cise 5.1.   Design a register machine to compute factorials using the iterative algorithm specified by the following procedure. Draw data-path and controller diagrams for this machine. (define (factorial n)     (define (iter product counter)       (if (> counter n)           product           (iter (* counter product)                 (+ counter 1))))     (iter 1 1))   5.1.1  A Language for Describing Register Machines Data-path and controller diagrams are adequate for representing simple machines such as GCD, but they are unwieldy for describing lar ge machines such as a Lisp interpreter . To make it possible to deal with complex machines, we will create a language that presents, in textual form, all the inf"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6994304o
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ormation given by the data-path and controller diagrams. We will start with a notation that directly mirrors the diagrams. We define the data paths of a machine by describing the registers and the operations. To describe a register , we give it a name and specify the buttons that control assignment to it. We give each of these buttons a name and specify the source of the data that enters the register under the button's control. (The source is a register , a constant, or an operation.) To describe an operation, we give it a name and specify its inputs (registers or constants). We define the controller of a machine as a sequence of instructions  together with labels that identify entry points  in the sequence. An instruction is one of the following:The name of a data-path button to push to assign a value to a register . (This corresponds to a box in the controller diagram.) A test  instruction, that performs a specified test. A conditional branch ( branch  instruction) to a location indicated by a controller la"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6995328b
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "bel, based on the result of the previous test. (The test and branch together correspond to a diamond in the controller diagram.) If the test is false, the controller should continue with the next instruction in the sequence. Otherwise, the controller should continue with the instruction after the label. An unconditional branch ( goto  instruction) naming a controller label at which to continue execution. The machine starts at the beginning of the controller instruction sequence and stops when execution reaches the end of the sequence. Except when a branch changes the flow of control, instructions are executed in the order in which they are listed. (data-paths    (registers     ((name a)      (buttons ((name a<-b) (source (register b)))))     ((name b)      (buttons ((name b<-t) (source (register t)))))     ((name t)      (buttons ((name t<-r) (source (operation rem))))))      (operations     ((name rem)      (inputs (register a) (register b)))     ((name =)      (inputs (register b) (constant 0)))))     (cont"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6996352r
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "roller    test-b                           ; label     (test =)                       ; test     (branch (label gcd-done))      ; conditional branch      (t<-r)                         ; button push      (a<-b)                         ; button push      (b<-t)                         ; button push      (goto (label test-b))          ; unconditional branch    gcd-done)                        ; label Figur e 5.3:   A specification of the GCD machine.Figure 5.3 shows the GCD machine described in this way . This example only hints at the generality of these descriptions, since the GCD machine is a very simple case: Each register has only one button, and each button and test is used only once in the controller . Unfortunately , it is dif ficult to read such a description. In order to understand the controller instructions we must constantly refer back to the definitions of the button names and the operation names, and to understand what the buttons do we may have to refer to the definitions of the operation names."}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6997376
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " We will thus transform our notation to combine the information from the data-path and controller descriptions so that we see it all together . To obtain this form of description, we will replace the arbitrary button and operation names by the definitions of their behavior . That is, instead of saying (in the controller) \u201cPush button t<-r \u201d and separately saying (in the data paths) \u201cButton t<-r  assigns the value of the rem operation to register t\u201d and \u201cThe rem operation's inputs are the contents of registers a and b,\u201d we will say (in the controller) \u201cPush the button that assigns to register t the value of the rem operation on the contents of registers a and b.\u201d Similarly , instead of saying (in the controller) \u201cPerform the = test\u201d and separately saying (in the data paths) \u201cThe = test operates on the contents of register b and the constant 0,\u201d we will say \u201cPerform the = test on the contents of register b and the constant 0.\u201d We will omit the data-path description, leaving only the controller sequence. Thus, t"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6998400h
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "he GCD machine is described as follows: (controller    test-b     (test (op =) (reg b) (const 0))      (branch (label gcd-done))      (assign t (op rem) (reg a) (reg b))      (assign a (reg b))      (assign b (reg t))      (goto (label test-b))    gcd-done)   This form of description is easier to read than the kind illustrated in figure 5.3, but it also has disadvantages: It is more verbose for lar ge machines, because complete descriptions of the data- path elements are repeated whenever the elements are mentioned in the controller instruction sequence. (This is not a problem in the GCD example, because each operation and button is used only once.) Moreover , repeating the data-path descriptions obscures the actual data-path structure of the machine; it is not obvious for a lar ge machine how many registers, operations, and buttons there are and how they are interconnected.Because the controller instructions in a machine definition look like Lisp expressions, it is easy to for get that they are not arbitrary"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch6999424
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " Lisp expressions. They can notate only legal machine operations. For example, operations can operate directly only on constants and the contents of registers, not on the results of other operations. In spite of these disadvantages, we will use this register -machine language throughout this chapter , because we will be more concerned with understanding controllers than with understanding the elements and connections in data paths. We should keep in mind, however , that data-path design is crucial in designing real machines. Exer cise 5.2.   Use the register -machine language to describe the iterative factorial machine of exercise 5.1. Actions Let us modify the GCD machine so that we can type in the numbers whose GCD we want and get the answer printed at our terminal. We will not discuss how to make a machine that can read and print, but will assume (as we do when we use read  and display  in Scheme) that they are available as primitive operations.1 Read  is like the operations we have been using in that it p"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61000448r
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "roduces a value that can be stored in a register . But read  does not take inputs from any registers; its value depends on something that happens outside the parts of the machine we are designing. We will allow our machine's operations to have such behavior , and thus will draw and notate the use of read  just as we do any other operation that computes a value. Print , on the other hand, dif fers from the operations we have been using in a fundamental way: It does not produce an output value to be stored in a register . Though it has an ef fect, this ef fect is not on a part of the machine we are designing. We will refer to this kind of operation as an action . We will represent an action in a data-path diagram just as we represent an operation that computes a value \u2013 as a trapezoid that contains the name of the action. Arrows point to the action box from any inputs (registers or constants). We also associate a button with the action. Pushing the button makes the action happen. To make a controller push an ac"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61001472t
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "tion button we use a new kind of instruction called perform . Thus, the action of printing the contents of register a is represented in a controller sequence by the instruction (perform (op print) (reg a))   Figure 5.4 shows the data paths and controller for the new GCD machine. Instead of having the machine stop after printing the answer , we have made it start over , so that it repeatedly reads a pair of numbers, computes their GCD, and prints the result. This structure is like the driver loops we used in the interpreters of chapter 4. (controller     gcd-loop       (assign a (op read))       (assign b (op read))     test-b       (test (op =) (reg b) (const 0))       (branch (label gcd-done))       (assign t (op rem) (reg a) (reg b))       (assign a (reg b))       (assign b (reg t))       (goto (label test-b))     gcd-done       (perform (op print) (reg a))       (goto (label gcd-loop)))  Figur e 5.4:   A GCD machine that reads inputs and prints results. 5.1.2  Abstraction in Machine Design We will often de"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61002496f
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "fine a machine to include \u201cprimitive\u201d operations that are actually very complex. For example, in sections 5.4 and 5.5 we will treat Scheme's environment manipulations as primitive. Such abstraction is valuable because it allows us to ignore the details of parts of a machine so that we can concentrate on other aspects of the design. The fact that we have swept a lot of complexity under the rug, however , does not mean that a machine design is unrealistic. We can always replace the complex \u201cprimitives\u201d by simpler primitive operations. Consider the GCD machine. The machine has an instruction that computes the remainder of the contents of registers a and b and assigns the result to register t. If we want to construct the GCD machine without using a primitive remainder operation, wemust specify how to compute remainders in terms of simpler operations, such as subtraction. Indeed, we can write a Scheme procedure that finds remainders in this way: (define (remainder n d)     (if (< n d)         n        (remainder ("}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61003520-
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "- n d) d)))   We can thus replace the remainder operation in the GCD machine's data paths with a subtraction operation and a comparison test. Figure 5.5 shows the data paths and controller for the elaborated machine. The instruction Figur e 5.5:   Data paths and controller for the elaborated GCD machine. (assign t (op rem) (reg a) (reg b))   in the GCD controller definition is replaced by a sequence of instructions that contains a loop, as shown in figure 5.6. Figur e 5.6:   Controller instruction sequence for the GCD machine in figure 5.5.(controller    test-b     (test (op =) (reg b) (const 0))      (branch (label gcd-done))      (assign t (reg a))    rem-loop      (test (op <) (reg t) (reg b))      (branch (label rem-done))      (assign t (op -) (reg t) (reg b))      (goto (label rem-loop))    rem-done      (assign a (reg b))      (assign b (reg t))      (goto (label test-b))    gcd-done)   Exer cise 5.3.   Design a machine to compute square roots using Newton's method, as described in section 1.1.7 : (def"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61004544i
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ine (sqrt x)     (define (good-enough? guess)       (< (abs (- (square guess) x)) 0.001))     (define (improve guess)       (average guess (/ x guess)))     (define (sqrt-iter guess)       (if (good-enough? guess)           guess           (sqrt-iter (improve guess))))     (sqrt-iter 1.0))   Begin by assuming that good-enough?  and improve  operations are available as primitives. Then show how to expand these in terms of arithmetic operations. Describe each version of the sqrt  machine design by drawing a data-path diagram and writing a controller definition in the register -machine language. 5.1.3  Subr outines When designing a machine to perform a computation, we would often prefer to arrange for components to be shared by dif ferent parts of the computation rather than duplicate the components. Consider a machine that includes two GCD computations \u2013 one that finds the GCD of the contents of registers a and b and one that finds the GCD of the contents of registers c and d. We might start by assuming we have"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61005568
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " a primitive gcdoperation, then expand the two instances of gcd in terms of more primitive operations. Figure 5.7 shows just the GCD portions of the resulting machine's data paths, without showing how they connect to the rest of the machine. The figure also shows the corresponding portions of the machine's controller sequence. gcd-1   (test (op =) (reg b) (const 0))    (branch (label after-gcd-1))    (assign t (op rem) (reg a) (reg b))    (assign a (reg b))    (assign b (reg t))    (goto (label gcd-1))   after-gcd-1      \u22ee   gcd-2   (test (op =) (reg d) (const 0))    (branch (label after-gcd-2))    (assign s (op rem) (reg c) (reg d))    (assign c (reg d))    (assign d (reg s))    (goto (label gcd-2))   after-gcd-2  Figur e 5.7:   Portions of the data paths and controller sequence for a machine with two GCD computations. This machine has two remainder operation boxes and two boxes for testing equality . If the duplicated components are complicated, as is the remainder box, this will not be an economical way to"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61006592
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " build the machine. We can avoid duplicating the data-path components by using the same components for both GCD computations, provided that doing so will not af fect the rest of the lar ger machine's computation. If the values in registers a and b are not needed by the time the controller gets to gcd-2  (or if these values can be moved to other registers for safekeeping), we can change the machine sothat it uses registers a and b, rather than registers c and d, in computing the second GCD as well as the first. If we do this, we obtain the controller sequence shown in figure 5.8. We have removed the duplicate data-path components (so that the data paths are again as in figure 5.1), but the controller now has two GCD sequences that dif fer only in their entry-point labels. It would be better to replace these two sequences by branches to a single sequence \u2013 a gcd subroutine  \u2013 at the end of which we branch back to the correct place in the main instruction sequence. We can accomplish this as follows: Before branc"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61007616h
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "hing to gcd, we place a distinguishing value (such as 0 or 1) into a special register , continue . At the end of the gcd subroutine we return either to after-gcd-1  or to after-gcd-2 , depending on the value of the continue  register . Figure 5.9 shows the relevant portion of the resulting controller sequence, which includes only a single copy of the gcd instructions. gcd-1   (test (op =) (reg b) (const 0))    (branch (label after-gcd-1))    (assign t (op rem) (reg a) (reg b))    (assign a (reg b))    (assign b (reg t))    (goto (label gcd-1))   after-gcd-1     \u22ee  gcd-2   (test (op =) (reg b) (const 0))    (branch (label after-gcd-2))    (assign t (op rem) (reg a) (reg b))    (assign a (reg b))    (assign b (reg t))    (goto (label gcd-2))   after-gcd-2  Figur e 5.8:   Portions of the controller sequence for a machine that uses the same data- path components for two dif ferent GCD computations. gcd   (test (op =) (reg b) (const 0))    (branch (label gcd-done))    (assign t (op rem) (reg a) (reg b))  Figur e 5"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61008640.
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": ".9:   Using a continue  register to avoid the duplicate controller sequence in figure 5.8. (assign a (reg b))    (assign b (reg t))    (goto (label gcd))   gcd-done    (test (op =) (reg continue) (const 0))          (branch (label after-gcd-1))    (goto (label after-gcd-2))     \u22ee  ;; Before branching to gcd from the first place where   ;; it is needed, we place 0 in the continue  register    (assign continue (const 0))    (goto (label gcd))   after-gcd-1     \u22ee  ;; Before the second use of gcd, we place 1 in the continue  register    (assign continue (const 1))    (goto (label gcd))   after-gcd-2   gcd   (test (op =) (reg b) (const 0))    (branch (label gcd-done))    (assign t (op rem) (reg a) (reg b))    (assign a (reg b))    (assign b (reg t))    (goto (label gcd))   gcd-done    (goto (reg continue))      \u22ee  ;; Before calling gcd, we assign to continue   ;; the label to which gcd should return.    (assign continue (label after-gcd-1))    (goto (label gcd))   after-gcd-1      \u22ee  ;; Here is the second call to "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61009664g
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "gcd, with a different continuation.    (assign continue (label after-gcd-2))    (goto (label gcd))   after-gcd-2  Figur e 5.10:   Assigning labels to the continue  register simplifies and generalizes the strategy shown in figure 5.9.This is a reasonable approach for handling small problems, but it would be awkward if there were many instances of GCD computations in the controller sequence. To decide where to continue executing after the gcd subroutine, we would need tests in the data paths and branch instructions in the controller for all the places that use gcd. A more powerful method for implementing subroutines is to have the continue  register hold the label of the entry point in the controller sequence at which execution should continue when the subroutine is finished. Implementing this strategy requires a new kind of connection between the data paths and the controller of a register machine: There must be a way to assign to a register a label in the controller sequence in such a way that this value can "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61010688b
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "be fetched from the register and used to continue execution at the designated entry point. To reflect this ability , we will extend the assign  instruction of the register -machine language to allow a register to be assigned as value a label from the controller sequence (as a special kind of constant). We will also extend the goto  instruction to allow execution to continue at the entry point described by the contents of a register rather than only at an entry point described by a constant label. Using these new constructs we can terminate the gcd subroutine with a branch to the location stored in the continue register . This leads to the controller sequence shown in figure 5.10. A machine with more than one subroutine could use multiple continuation registers (e.g., gcd-continue , factorial-continue ) or we could have all subroutines share a single continue  register . Sharing is more economical, but we must be careful if we have a subroutine ( sub1 ) that calls another subroutine ( sub2 ). Unless sub1  save"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61011712s
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "s the contents of continue  in some other register before setting up continue  for the call to sub2 , sub1 will not know where to go when it is finished. The mechanism developed in the next section to handle recursion also provides a better solution to this problem of nested subroutine calls. 5.1.4  Using a Stack to Implement Recursion With the ideas illustrated so far , we can implement any iterative process by specifying a register machine that has a register corresponding to each state variable of the process. The machine repeatedly executes a controller loop, changing the contents of the registers, until some termination condition is satisfied. At each point in the controller sequence, the state of the machine (representing the state of the iterative process) is completely determined by the contents of the registers (the values of the state variables). Implementing recursive processes, however , requires an additional mechanism. Consider the following recursive method for computing factorials, which we fi"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61012736r
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "rst examined in section 1.2.1 :(define (factorial n)     (if (= n 1)         1        (* (factorial (- n 1)) n)))   As we see from the procedure, computing n! requires computing ( n - 1)!. Our GCD machine, modeled on the procedure (define (gcd a b)     (if (= b 0)         a        (gcd b (remainder a b))))   similarly had to compute another GCD. But there is an important dif ference between the gcd procedure, which reduces the original computation to a new GCD computation, and factorial , which requires computing another factorial as a subproblem. In GCD, the answer to the new GCD computation is the answer to the original problem. To compute the next GCD, we simply place the new ar guments in the input registers of the GCD machine and reuse the machine's data paths by executing the same controller sequence. When the machine is finished solving the final GCD problem, it has completed the entire computation. In the case of factorial (or any recursive process) the answer to the new factorial subproblem is not th"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61013760e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "e answer to the original problem. The value obtained for ( n - 1)! must be multiplied by n to get the final answer . If we try to imitate the GCD design, and solve the factorial subproblem by decrementing the n register and rerunning the factorial machine, we will no longer have available the old value of n by which to multiply the result. We thus need a second factorial machine to work on the subproblem. This second factorial computation itself has a factorial subproblem, which requires a third factorial machine, and so on. Since each factorial machine contains another factorial machine within it, the total machine contains an infinite nest of similar machines and hence cannot be constructed from a fixed, finite number of parts. Nevertheless, we can implement the factorial process as a register machine if we can arrange to use the same components for each nested instance of the machine. Specifically , the machine that computes n! should use the same components to work on the subproblem of computing ( n - 1)!"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61014784,
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": ", on the subproblem for ( n - 2)!, and so on. This is plausible because, although the factorial process dictates that an unbounded number of copies of the same machine are needed to perform a computation, only one of these copies needs to be active at any given time. When the machine encounters a recursive subproblem, it can suspend work on the main problem, reuse the same physical parts to work on the subproblem, then continue the suspended computation.In the subproblem, the contents of the registers will be dif ferent than they were in the main problem. (In this case the n register is decremented.) In order to be able to continue the suspended computation, the machine must save the contents of any registers that will be needed after the subproblem is solved so that these can be restored to continue the suspended computation. In the case of factorial, we will save the old value of n, to be restored when we are finished computing the factorial of the decremented n register .2 Since there is no a priori  limit"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61015808
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " on the depth of nested recursive calls, we may need to save an arbitrary number of register values. These values must be restored in the reverse of the order in which they were saved, since in a nest of recursions the last subproblem to be entered is the first to be finished. This dictates the use of a stack , or \u201clast in, first out\u201d data structure, to save register values. We can extend the register -machine language to include a stack by adding two kinds of instructions: V alues are placed on the stack using a save  instruction and restored from the stack using a restore  instruction. After a sequence of values has been save d on the stack, a sequence of restore s will retrieve these values in reverse order .3 With the aid of the stack, we can reuse a single copy of the factorial machine's data paths for each factorial subproblem. There is a similar design issue in reusing the controller sequence that operates the data paths. To reexecute the factorial computation, the controller cannot simply loop back to"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61016832
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " the beginning, as with an iterative process, because after solving the ( n - 1)! subproblem the machine must still multiply the result by n. The controller must suspend its computation of n!, solve the ( n - 1)! subproblem, then continue its computation of n!. This view of the factorial computation suggests the use of the subroutine mechanism described in section 5.1.3 , which has the controller use a continue  register to transfer to the part of the sequence that solves a subproblem and then continue where it left of f on the main problem. We can thus make a factorial subroutine that returns to the entry point stored in the continue  register . Around each subroutine call, we save and restore continue  just as we do the n register , since each \u201clevel\u201d of the factorial computation will use the same continue  register . That is, the factorial subroutine must put a new value in continue  when it calls itself for a subproblem, but it will need the old value in order to return to the place that called it to solv"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61017856e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "e a subproblem. Figure 5.11 shows the data paths and controller for a machine that implements the recursive factorial  procedure. The machine has a stack and three registers, called n, val, and continue . To simplify the data-path diagram, we have not named the register - assignment buttons, only the stack-operation buttons ( sc and sn to save registers, rc and rn to restore registers). To operate the machine, we put in register n the number whose factorial we wish to compute and start the machine. When the machine reaches fact- done , the computation is finished and the answer will be found in the val register . In the controller sequence, n and continue  are saved before each recursive call and restored upon return from the call. Returning from a call is accomplished by branching to thelocation stored in continue . Continue  is initialized when the machine starts so that the last return will go to fact-done . The val register , which holds the result of the factorial computation, is not saved before the rec"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61018880u
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ursive call, because the old contents of val is not useful after the subroutine returns. Only the new value, which is the value produced by the subcomputation, is needed. Although in principle the factorial computation requires an infinite machine, the machine in figure 5.11 is actually finite except for the stack, which is potentially unbounded. Any particular physical implementation of a stack, however , will be of finite size, and this will limit the depth of recursive calls that can be handled by the machine. This implementation of factorial illustrates the general strategy for realizing recursive algorithms as ordinary register machines augmented by stacks. When a recursive subproblem is encountered, we save on the stack the registers whose current values will be required after the subproblem is solved, solve the recursive subproblem, then restore the saved registers and continue execution on the main problem. The continue  register must always be saved. Whether there are other registers that need to be "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61019904s
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "saved depends on the particular machine, since not all recursive computations need the original values of registers that are modified during solution of the subproblem (see exercise 5.4). A double r ecursion Let us examine a more complex recursive process, the tree-recursive computation of the Fibonacci numbers, which we introduced in section 1.2.2 : (define (fib n)     (if (< n 2)         n        (+ (fib (- n 1)) (fib (- n 2)))))   Just as with factorial, we can implement the recursive Fibonacci computation as a register machine with registers n, val, and continue . The machine is more complex than the one for factorial, because there are two places in the controller sequence where we need to perform recursive calls \u2013 once to compute Fib( n - 1) and once to compute Fib(n - 2). To set up for each of these calls, we save the registers whose values will be needed later , set the n register to the number whose Fib we need to compute recursively (n - 1 or n - 2), and assign to continue  the entry point in the ma"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61020928i
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "in sequence to which to return (afterfib-n-1  or afterfib-n-2 , respectively). We then go to fib-loop . When we return from the recursive call, the answer is in val. Figure 5.12 shows the controller sequence for this machine. Figur e 5.1 1:  A recursive factorial machine.(controller      (assign continue (label fact-done))     ; set up final return address    fact-loop      (test (op =) (reg n) (const 1))      (branch (label base-case))      ;; Set up for the recursive call by saving n and continue .     ;; Set up continue  so that the computation will continue      ;; at after-fact  when the subroutine returns.      (save continue)      (save n)      (assign n (op -) (reg n) (const 1))      (assign continue (label after-fact))      (goto (label fact-loop))    after-fact      (restore n)      (restore continue)      (assign val (op *) (reg n) (reg val))   ; val now contains  n(n - 1)!     (goto (reg continue))                   ; return to caller    base-case      (assign val (const 1))                  ; bas"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61021952e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "e case: 1! = 1     (goto (reg continue))                   ; return to caller    fact-done)   (controller      (assign continue (label fib-done))    fib-loop      (test (op <) (reg n) (const 2))      (branch (label immediate-answer))  Figur e 5.12:   Controller for a machine to compute Fibonacci numbers.   ;; set up to compute Fib(n - 1)     (save continue)      (assign continue (label afterfib-n-1))      (save n)                           ; save old value of n     (assign n (op -) (reg n) (const 1)) ; clobber n to n - 1     (goto (label fib-loop))            ; perform recursive call    afterfib-n-1                         ; upon return, val contains Fib(n - 1)     (restore n)      (restore continue)      ;; set up to compute Fib(n - 2)     (assign n (op -) (reg n) (const 2))      (save continue)      (assign continue (label afterfib-n-2))      (save val)                         ; save Fib(n - 1)     (goto (label fib-loop))    afterfib-n-2                         ; upon return, val contains Fib(n - 2)     (as"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61022976s
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "sign n (reg val))               ; n now contains Fib(n - 2)     (restore val)                      ; val now contains Fib(n - 1)     (restore continue)      (assign val                        ;  Fib(n - 1) +  Fib(n - 2)             (op +) (reg val) (reg n))      (goto (reg continue))              ; return to caller, answer is in val   immediate-answer      (assign val (reg n))               ; base case:  Fib(n) = n     (goto (reg continue))    fib-done)   Exer cise 5.4.   Specify register machines that implement each of the following procedures. For each machine, write a controller instruction sequence and draw a diagram showing the data paths. a. Recursive exponentiation: (define (expt b n)     (if (= n 0)         1        (* b (expt b (- n 1)))))   b. Iterative exponentiation:(define (expt b n)     (define (expt-iter counter product)       (if (= counter 0)           product           (expt-iter (- counter 1) (* b product))))     (expt-iter n 1))   Exer cise 5.5.   Hand-simulate the factorial and Fibonacci "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61024000m
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "machines, using some nontrivial input (requiring execution of at least one recursive call). Show the contents of the stack at each significant point in the execution. Exer cise 5.6.   Ben Bitdiddle observes that the Fibonacci machine's controller sequence has an extra save  and an extra restore , which can be removed to make a faster machine. Where are these instructions? 5.1.5  Instruction Summary A controller instruction in our register -machine language has one of the following forms, where each < inputi> is either (reg <register-name >) or (const < constant- value>). These instructions were introduced in section 5.1.1 : (assign < register-name > (reg < register-name >))    (assign < register-name > (const < constant-value >))    (assign < register-name > (op <operation-name >) <input1> ... <inputn>)    (perform (op < operation-name >) <input1> ... <inputn>)    (test (op < operation-name >) <input1> ... <inputn>)    (branch (label < label-name >))    (goto (label < label-name >))  The use of registers to h"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61025024o
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "old labels was introduced in section 5.1.3 : (assign < register-name > (label < label-name >))    (goto (reg < register-name >)) Instructions to use the stack were introduced in section 5.1.4 : (save <register-name >)    (restore < register-name >)  The only kind of < constant-value > we have seen so far is a number , but later we will use strings, symbols, and lists. For example, (const \"abc\")  is the string \"abc\" , (const abc)  is the symbol abc, (const (a b c))  is the list (a b c) , and (const ()) is the empty list. 1 This assumption glosses over a great deal of complexity . Usually a lar ge portion of the implementation of a Lisp system is dedicated to making reading and printing work. 2 One might ar gue that we don't need to save the old n; after we decrement it and solve the subproblem, we could simply increment it to recover the old value. Although this strategy works for factorial, it cannot work in general, since the old value of a register cannot always be computed from the new one. 3 In section 5."}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch610260483
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "3 we will see how to implement a stack in terms of more primitive operations.5.2  A Register-Machine Simulator In order to gain a good understanding of the design of register machines, we must test the machines we design to see if they perform as expected. One way to test a design is to hand-simulate the operation of the controller , as in exercise 5.5. But this is extremely tedious for all but the simplest machines. In this section we construct a simulator for machines described in the register -machine language. The simulator is a Scheme program with four interface procedures. The first uses a description of a register machine to construct a model of the machine (a data structure whose parts correspond to the parts of the machine to be simulated), and the other three allow us to simulate the machine by manipulating the model: (make-machine < register-names > <operations > <controller >)  constructs and returns a model of the machine with the given registers, operations, and controller . (set-register-conten"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61027072t
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ts! < machine-model > <register-name > <value>)  stores a value in a simulated register in the given machine. (get-register-contents < machine-model > <register-name >)  returns the contents of a simulated register in the given machine. (start < machine-model >)  simulates the execution of the given machine, starting from the beginning of the controller sequence and stopping when it reaches the end of the sequence. As an example of how these procedures are used, we can define gcd- machine  to be a model of the GCD machine of section 5.1.1  as follows: (define gcd-machine     (make-machine      '(a b t)      (list (list 'rem remainder) (list '= =))      '(test-b         (test (op =) (reg b) (const 0))          (branch (label gcd-done))          (assign t (op rem) (reg a) (reg b))          (assign a (reg b))          (assign b (reg t))          (goto (label test-b))        gcd-done)))   The first ar gument to make-machine  is a list of register names. The next argument is a table (a list of two-element lists) t"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61028096h
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "hat pairs each operation name with a Scheme procedure that implements the operation (that is, produces the same output value given the same input values). The last argument specifies the controller as a list of labels and machine instructions, as in section 5.1. To compute GCDs with this machine, we set the input registers, start the machine, and examine the result when the simulation terminates: (set-register-contents! gcd-machine 'a 206)   done  (set-register-contents! gcd-machine 'b 40)   done  (start gcd-machine)   done  (get-register-contents gcd-machine 'a)   2  This computation will run much more slowly than a gcd procedure written in Scheme, because we will simulate low-level machine instructions, such as assign , by much more complex operations. Exer cise 5.7.   Use the simulator to test the machines you designed in exercise 5.4. 5.2.1  The Machine Model The machine model generated by make-machine  is represented as a procedure with local state using the message-passing techniques developedin chapter"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61029120
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " 3. To build this model, make-machine  begins by calling the procedure make-new-machine  to construct the parts of the machine model that are common to all register machines. This basic machine model constructed by make-new-machine  is essentially a container for some registers and a stack, together with an execution mechanism that processes the controller instructions one by one. Make-machine  then extends this basic model (by sending it messages) to include the registers, operations, and controller of the particular machine being defined. First it allocates a register in the new machine for each of the supplied register names and installs the designated operations in the machine. Then it uses an assembler  (described below in section 5.2.2 ) to transform the controller list into instructions for the new machine and installs these as the machine's instruction sequence. Make-machine  returns as its value the modified machine model. (define (make-machine register-names ops controller-text)     (let ((machine ("}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61030144m
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "make-new-machine)))       (for-each (lambda (register-name)                   ((machine 'allocate-register) register-name))                 register-names)       ((machine 'install-operations) ops)          ((machine 'install-instruction-sequence)        (assemble controller-text machine))       machine))   Registers We will represent a register as a procedure with local state, as in chapter 3. The procedure make-register  creates a register that holds a value that can be accessed or changed: (define (make-register name)     (let ((contents '*unassigned*))       (define (dispatch message)         (cond ((eq? message 'get) contents)               ((eq? message 'set)               (lambda (value) (set! contents value)))               (else                (error \"Unknown request -- REGISTER\" message))))       dispatch))   The following procedures are used to access registers: (define (get-contents register)     (register 'get))     (define (set-contents! register value)     ((register 'set) value))   The stack W"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61031168e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "e can also represent a stack as a procedure with local state. The procedure make-stack  creates a stack whose local state consists of a list of the items on the stack. A stack accepts requests to push  an item onto the stack, to pop the top item of f the stack and return it, and to initialize  the stack to empty . (define (make-stack)     (let ((s '()))       (define (push x)         (set! s (cons x s)))       (define (pop)         (if (null? s)             (error \"Empty stack -- POP\")             (let ((top (car s)))               (set! s (cdr s))               top)))       (define (initialize)         (set! s '())         'done)       (define (dispatch message)         (cond ((eq? message 'push) push)               ((eq? message 'pop) (pop))              ((eq? message 'initialize) (initialize))               (else (error \"Unknown request -- STACK\"                            message))))       dispatch))   The following procedures are used to access stacks: (define (pop stack)     (stack 'pop))     (define (p"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61032192u
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ush stack value)     ((stack 'push) value))   The basic machine The make-new-machine  procedure, shown in figure 5.13, constructs an object whose local state consists of a stack, an initially empty instruction sequence, a list of operations that initially contains an operation to initialize the stack, and a register table  that initially contains two registers, named flag  and pc (for \u201cprogram counter\u201d). The internal procedure allocate- register  adds new entries to the register table, and the internal procedure lookup-register  looks up registers in the table. The flag  register is used to control branching in the simulated machine. Test  instructions set the contents of flag  to the result of the test (true or false). Branch  instructions decide whether or not to branch by examining the contents of flag . The pc register determines the sequencing of instructions as the machine runs. This sequencing is implemented by the internal procedure execute . In the simulation model, each machine instruction is a data"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61033216
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " structure that includes a procedure of no ar guments, called the instruction execution procedur e, such that calling this procedure simulates executing the instruction. As the simulation runs, pc points to the place in the instruction sequence beginning with the next instruction to be executed. Execute  gets that instruction, executes it by calling the instruction execution procedure, and repeats this cycle until there are no more instructions to execute (i.e., until pc points to the end of the instruction sequence).(define (make-new-machine)     (let ((pc (make-register 'pc))           (flag (make-register 'flag))           (stack (make-stack))           (the-instruction-sequence '()))       (let ((the-ops              (list (list 'initialize-stack                          (lambda () (stack 'initialize)))))             (register-table              (list (list 'pc pc) (list 'flag flag))))         (define (allocate-register name)           (if (assoc name register-table)               (error \"Multiply defined"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61034240
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " register: \" name)               (set! register-table                     (cons (list name (make-register name))                           register-table)))           'register-allocated)         (define (lookup-register name)           (let ((val (assoc name register-table)))             (if val                 (cadr val)                 (error \"Unknown register:\" name))))         (define (execute)           (let ((insts (get-contents pc)))             (if (null? insts)                 'done                 (begin                   ((instruction-execution-proc (car insts)))                   (execute)))))         (define (dispatch message)           (cond ((eq? message 'start)                  (set-contents! pc the-instruction-sequence)                  (execute))                 ((eq? message 'install-instruction-sequence)  Figur e 5.13:   The make-new-machine  procedure, which implements the basic machine model.               (lambda (seq) (set! the-instruction- sequence seq)))                 ((eq? messag"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61035264e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "e 'allocate-register) allocate- register)                 ((eq? message 'get-register) lookup-register)                 ((eq? message 'install-operations)                  (lambda (ops) (set! the-ops (append the- ops ops))))                 ((eq? message 'stack) stack)                 ((eq? message 'operations) the-ops)                 (else (error \"Unknown request - - MACHINE\" message))))         dispatch)))   As part of its operation, each instruction execution procedure modifies pc to indicate the next instruction to be executed. Branch  and goto  instructions change pc to point to the new destination. All other instructions simply advance pc, making it point to the next instruction in the sequence. Observe that each call to execute  calls execute  again, but this does not produce an infinite loop because running the instruction execution procedure changes the contents of pc. Make-new-machine  returns a dispatch  procedure that implements message- passing access to the internal state. Notice that starting "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61036288t
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "the machine is accomplished by setting pc to the beginning of the instruction sequence and calling execute . For convenience, we provide an alternate procedural interface to a machine's start  operation, as well as procedures to set and examine register contents, as specified at the beginning of section 5.2: (define (start machine)     (machine 'start))   (define (get-register-contents machine register-name)     (get-contents (get-register machine register-name)))   (define (set-register-contents! machine register-name value)    (set-contents! (get-register machine register-name) value)     'done)   These procedures (and many procedures in sections 5.2.2  and 5.2.3 ) use the following to look up the register with a given name in a given machine: (define (get-register machine reg-name)     ((machine 'get-register) reg-name))   5.2.2  The Assembler The assembler transforms the sequence of controller expressions for a machine into a corresponding list of machine instructions, each with its execution procedure. O"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61037312v
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "verall, the assembler is much like the evaluators we studied in chapter 4 \u2013 there is an input language (in this case, the register - machine language) and we must perform an appropriate action for each type of expression in the language. The technique of producing an execution procedure for each instruction is just what we used in section 4.1.7  to speed up the evaluator by separating analysis from runtime execution. As we saw in chapter 4, much useful analysis of Scheme expressions could be performed without knowing the actual values of variables. Here, analogously , much useful analysis of register -machine-language expressions can be performed without knowing the actual contents of machine registers. For example, we can replace references to registers by pointers to the register objects, and we can replace references to labels by pointers to the place in the instruction sequence that the label designates. Before it can generate the instruction execution procedures, the assembler must know what all the labe"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61038336l
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ls refer to, so it begins by scanning the controller text to separate the labels from the instructions. As it scans the text, it constructs both a list of instructions and a table that associates each label with a pointer into that list. Then the assembler augments the instruction list by inserting the execution procedure for each instruction. The assemble  procedure is the main entry to the assembler . It takes the controller text and the machine model as ar guments and returns theinstruction sequence to be stored in the model. Assemble  calls extract- labels  to build the initial instruction list and label table from the supplied controller text. The second ar gument to extract-labels  is a procedure to be called to process these results: This procedure uses update-insts!  to generate the instruction execution procedures and insert them into the instruction list, and returns the modified list. (define (assemble controller-text machine)     (extract-labels controller-text       (lambda (insts labels)        "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61039360
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " (update-insts! insts labels machine)         insts)))   Extract-labels  takes as ar guments a list text  (the sequence of controller instruction expressions) and a receive  procedure. Receive  will be called with two values: (1) a list insts  of instruction data structures, each containing an instruction from text ; and (2) a table called labels , which associates each label from text  with the position in the list insts  that the label designates. (define (extract-labels text receive)     (if (null? text)         (receive '() '())         (extract-labels (cdr text)          (lambda (insts labels)            (let ((next-inst (car text)))              (if (symbol? next-inst)                  (receive insts                           (cons (make-label-entry next-inst                                                   insts)                                 labels))                  (receive (cons (make-instruction next-inst)                                 insts)                           labels)))))))   Extract-"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61040384l
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "labels  works by sequentially scanning the elements of the text and accumulating the insts  and the labels . If an element is a symbol (andthus a label) an appropriate entry is added to the labels  table. Otherwise the element is accumulated onto the insts  list.4 Update-insts!  modifies the instruction list, which initially contains only the text of the instructions, to include the corresponding execution procedures: (define (update-insts! insts labels machine)     (let ((pc (get-register machine 'pc))           (flag (get-register machine 'flag))           (stack (machine 'stack))           (ops (machine 'operations)))       (for-each        (lambda (inst)          (set-instruction-execution-proc!           inst           (make-execution-procedure            (instruction-text inst) labels machine            pc flag stack ops)))        insts)))   The machine instruction data structure simply pairs the instruction text with the corresponding execution procedure. The execution procedure is not yet available wh"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61041408e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "en extract-labels  constructs the instruction, and is inserted later by update-insts! . (define (make-instruction text)     (cons text '()))   (define (instruction-text inst)     (car inst))   (define (instruction-execution-proc inst)     (cdr inst))   (define (set-instruction-execution-proc! inst proc)     (set-cdr! inst proc))   The instruction text is not used by our simulator , but it is handy to keep around for debugging (see exercise 5.16).Elements of the label table are pairs: (define (make-label-entry label-name insts)     (cons label-name insts))   Entries will be looked up in the table with (define (lookup-label labels label-name)     (let ((val (assoc label-name labels)))       (if val           (cdr val)           (error \"Undefined label -- ASSEMBLE\" label-name))))   Exer cise 5.8.   The following register -machine code is ambiguous, because the label here  is defined more than once: start    (goto (label here))   here    (assign a (const 3))     (goto (label there))   here    (assign a (const 4))"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61042432
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "     (goto (label there))   there  With the simulator as written, what will the contents of register a be when control reaches there ? Modify the extract-labels  procedure so that the assembler will signal an error if the same label name is used to indicate two different locations. 5.2.3  Generating Execution Pr ocedur es for Instructions The assembler calls make-execution-procedure  to generate the execution procedure for an instruction. Like the analyze  procedure in the evaluator of section 4.1.7 , this dispatches on the type of instruction to generate the appropriate execution procedure.(define (make-execution-procedure inst labels machine                                     pc flag stack ops)     (cond ((eq? (car inst) 'assign)            (make-assign inst machine labels ops pc))           ((eq? (car inst) 'test)            (make-test inst machine labels ops flag pc))           ((eq? (car inst) 'branch)            (make-branch inst machine labels flag pc))           ((eq? (car inst) 'goto)            (ma"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61043456k
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ke-goto inst machine labels pc))           ((eq? (car inst) 'save)            (make-save inst machine stack pc))           ((eq? (car inst) 'restore)            (make-restore inst machine stack pc))           ((eq? (car inst) 'perform)            (make-perform inst machine labels ops pc))           (else (error \"Unknown instruction type -- ASSEMBLE\"                        inst))))   For each type of instruction in the register -machine language, there is a generator that builds an appropriate execution procedure. The details of these procedures determine both the syntax and meaning of the individual instructions in the register -machine language. We use data abstraction to isolate the detailed syntax of register -machine expressions from the general execution mechanism, as we did for evaluators in section 4.1.2 , by using syntax procedures to extract and classify the parts of an instruction. Assign  instructions The make-assign  procedure handles assign  instructions: (define (make-assign inst machine labels "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61044480o
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "operations pc)     (let ((target            (get-register machine (assign-reg-name inst)))           (value-exp (assign-value-exp inst)))       (let ((value-proc              (if (operation-exp? value-exp)                 (make-operation-exp                   value-exp machine labels operations)                  (make-primitive-exp                   (car value-exp) machine labels))))         (lambda ()                ; execution procedure for assig n          (set-contents! target (value-proc))           (advance-pc pc)))))   Make-assign  extracts the tar get register name (the second element of the instruction) and the value expression (the rest of the list that forms the instruction) from the assign  instruction using the selectors (define (assign-reg-name assign-instruction)     (cadr assign-instruction))   (define (assign-value-exp assign-instruction)     (cddr assign-instruction))   The register name is looked up with get-register  to produce the tar get register object. The value expression is passed to"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61045504
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " make-operation-exp  if the value is the result of an operation, and to make-primitive-exp otherwise. These procedures (shown below) parse the value expression and produce an execution procedure for the value. This is a procedure of no arguments, called value-proc , which will be evaluated during the simulation to produce the actual value to be assigned to the register . Notice that the work of looking up the register name and parsing the value expression is performed just once, at assembly time, not every time the instruction is simulated. This saving of work is the reason we use execution procedures, and corresponds directly to the saving in work we obtained by separating program analysis from execution in the evaluator of section 4.1.7 . The result returned by make-assign  is the execution procedure for the assign  instruction. When this procedure is called (by the machine model's execute  procedure), it sets the contents of the tar get register to the result obtained by executing value-proc . Then it adva"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61046528n
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "nces the pc to the next instruction by running the procedure(define (advance-pc pc)     (set-contents! pc (cdr (get-contents pc))))   Advance-pc  is the normal termination for all instructions except branch  and goto . Test , branch , and goto  instructions Make-test  handles test  instructions in a similar way . It extracts the expression that specifies the condition to be tested and generates an execution procedure for it. At simulation time, the procedure for the condition is called, the result is assigned to the flag  register , and the pc is advanced: (define (make-test inst machine labels operations flag pc)     (let ((condition (test-condition inst)))       (if (operation-exp? condition)           (let ((condition-proc                  (make-operation-exp                   condition machine labels operations)))             (lambda ()               (set-contents! flag (condition-proc))               (advance-pc pc)))           (error \"Bad TEST instruction -- ASSEMBLE\" inst))))   (define (test-condition "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61047552t
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "test-instruction)     (cdr test-instruction))   The execution procedure for a branch  instruction checks the contents of the flag  register and either sets the contents of the pc to the branch destination (if the branch is taken) or else just advances the pc (if the branch is not taken). Notice that the indicated destination in a branch  instruction must be a label, and the make-branch  procedure enforces this. Notice also that the label is looked up at assembly time, not each time the branch  instruction is simulated. (define (make-branch inst machine labels flag pc)     (let ((dest (branch-dest inst)))       (if (label-exp? dest)          (let ((insts                  (lookup-label labels (label-exp-label dest))))             (lambda ()               (if (get-contents flag)                   (set-contents! pc insts)                   (advance-pc pc))))           (error \"Bad BRANCH instruction -- ASSEMBLE\" inst))))   (define (branch-dest branch-instruction)     (cadr branch-instruction))   A goto  instructio"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61048576n
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "n is similar to a branch, except that the destination may be specified either as a label or as a register , and there is no condition to check \u2013 the pc is always set to the new destination. (define (make-goto inst machine labels pc)     (let ((dest (goto-dest inst)))       (cond ((label-exp? dest)              (let ((insts                     (lookup-label labels                                   (label-exp-label dest))))                (lambda () (set-contents! pc insts))))             ((register-exp? dest)              (let ((reg                     (get-register machine                                   (register-exp-reg dest))))                (lambda ()                  (set-contents! pc (get-contents reg)))))             (else (error \"Bad GOTO instruction -- ASSEMBLE\"                          inst)))))   (define (goto-dest goto-instruction)     (cadr goto-instruction))   Other instructions The stack instructions save  and restore  simply use the stack with the designated register and advance the pc:(def"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61049600i
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ine (make-save inst machine stack pc)     (let ((reg (get-register machine                              (stack-inst-reg-name inst))))       (lambda ()         (push stack (get-contents reg))         (advance-pc pc))))   (define (make-restore inst machine stack pc)     (let ((reg (get-register machine                              (stack-inst-reg-name inst))))       (lambda ()         (set-contents! reg (pop stack))            (advance-pc pc))))   (define (stack-inst-reg-name stack-instruction)     (cadr stack-instruction))   The final instruction type, handled by make-perform , generates an execution procedure for the action to be performed. At simulation time, the action procedure is executed and the pc advanced. (define (make-perform inst machine labels operations pc)     (let ((action (perform-action inst)))       (if (operation-exp? action)           (let ((action-proc                  (make-operation-exp                   action machine labels operations)))             (lambda ()               (action-pro"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61050624c
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "c)               (advance-pc pc)))           (error \"Bad PERFORM instruction -- ASSEMBLE\" inst))))   (define (perform-action inst) (cdr inst))   Execution pr ocedur es for subexpr essions The value of a reg, label , or const  expression may be needed for assignment to a register ( make-assign ) or for input to an operation ( make- operation-exp , below). The following procedure generates execution procedures to produce values for these expressions during the simulation:(define (make-primitive-exp exp machine labels)     (cond ((constant-exp? exp)            (let ((c (constant-exp-value exp)))              (lambda () c)))           ((label-exp? exp)            (let ((insts                   (lookup-label labels                                 (label-exp-label exp))))              (lambda () insts)))           ((register-exp? exp)            (let ((r (get-register machine                                   (register-exp-reg exp))))              (lambda () (get-contents r))))           (else            (error \"Un"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61051648k
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "known expression type -- ASSEMBLE\" exp))))   The syntax of reg, label , and const  expressions is determined by (define (register-exp? exp) (tagged-list? exp 'reg))   (define (register-exp-reg exp) (cadr exp))   (define (constant-exp? exp) (tagged-list? exp 'const))   (define (constant-exp-value exp) (cadr exp))   (define (label-exp? exp) (tagged-list? exp 'label))   (define (label-exp-label exp) (cadr exp))   Assign , perform , and test  instructions may include the application of a machine operation (specified by an op expression) to some operands (specified by reg and const  expressions). The following procedure produces an execution procedure for an \u201coperation expression\u201d \u2013 a list containing the operation and operand expressions from the instruction: (define (make-operation-exp exp machine labels operations)     (let ((op (lookup-prim (operation-exp-op exp) operations))           (aprocs            (map (lambda (e)                   (make-primitive-exp e machine labels))                 (operation-exp-ope"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61052672r
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "rands exp))))      (lambda ()         (apply op (map (lambda (p) (p)) aprocs)))))   The syntax of operation expressions is determined by (define (operation-exp? exp)     (and (pair? exp) (tagged-list? (car exp) 'op)))   (define (operation-exp-op operation-exp)     (cadr (car operation-exp)))   (define (operation-exp-operands operation-exp)     (cdr operation-exp))   Observe that the treatment of operation expressions is very much like the treatment of procedure applications by the analyze-application  procedure in the evaluator of section 4.1.7  in that we generate an execution procedure for each operand. At simulation time, we call the operand procedures and apply the Scheme procedure that simulates the operation to the resulting values. The simulation procedure is found by looking up the operation name in the operation table for the machine: (define (lookup-prim symbol operations)     (let ((val (assoc symbol operations)))       (if val           (cadr val)           (error \"Unknown operation -- ASSEMBLE\" s"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61053696y
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ymbol))))   Exer cise 5.9.   The treatment of machine operations above permits them to operate on labels as well as on constants and the contents of registers. Modify the expression-processing procedures to enforce the condition that operations can be used only with registers and constants. Exer cise 5.10.   Design a new syntax for register -machine instructions and modify the simulator to use your new syntax. Can you implement your new syntax without changing any part of the simulator except the syntax procedures in this section? Exer cise 5.1 1.  When we introduced save  and restore  in section 5.1.4 , we didn't specify what would happen if you tried to restore a register that wasnot the last one saved, as in the sequence (save y)   (save x)   (restore y)   There are several reasonable possibilities for the meaning of restore : a.  (restore y)  puts into y the last value saved on the stack, regardless of what register that value came from. This is the way our simulator behaves. Show how to take advantage of"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61054720
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " this behavior to eliminate one instruction from the Fibonacci machine of section 5.1.4  (figure 5.12). b.  (restore y)  puts into y the last value saved on the stack, but only if that value was saved from y; otherwise, it signals an error . Modify the simulator to behave this way . You will have to change save  to put the register name on the stack along with the value. c.  (restore y)  puts into y the last value saved from y regardless of what other registers were saved after y and not restored. Modify the simulator to behave this way . You will have to associate a separate stack with each register . You should make the initialize-stack  operation initialize all the register stacks. Exer cise 5.12.   The simulator can be used to help determine the data paths required for implementing a machine with a given controller . Extend the assembler to store the following information in the machine model: a list of all instructions, with duplicates removed, sorted by instruction type (assign , goto , and so on); a li"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61055744s
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "st (without duplicates) of the registers used to hold entry points (these are the registers referenced by goto  instructions); a list (without duplicates) of the registers that are save d or restore d; for each register , a list (without duplicates) of the sources from which it is assigned (for example, the sources for register val in the factorialmachine of figure 5.11 are (const 1)  and ((op *) (reg n) (reg val)) ). Extend the message-passing interface to the machine to provide access to this new information. To test your analyzer , define the Fibonacci machine from figure 5.12 and examine the lists you constructed. Exer cise 5.13.   Modify the simulator so that it uses the controller sequence to determine what registers the machine has rather than requiring a list of registers as an ar gument to make-machine . Instead of pre-allocating the registers in make-machine , you can allocate them one at a time when they are first seen during assembly of the instructions. 5.2.4  Monitoring Machine Performance Simul"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61056768a
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ation is useful not only for verifying the correctness of a proposed machine design but also for measuring the machine's performance. For example, we can install in our simulation program a \u201cmeter\u201d that measures the number of stack operations used in a computation. To do this, we modify our simulated stack to keep track of the number of times registers are saved on the stack and the maximum depth reached by the stack, and add a message to the stack's interface that prints the statistics, as shown below . We also add an operation to the basic machine model to print the stack statistics, by initializing the-ops  in make-new-machine  to (list (list 'initialize-stack               (lambda () (stack 'initialize)))         (list 'print-stack-statistics               (lambda () (stack 'print-statistics))))   Here is the new version of make-stack : (define (make-stack)     (let ((s '())           (number-pushes 0)           (max-depth 0)           (current-depth 0))       (define (push x)        (set! s (cons x s))  "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61057792
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "       (set! number-pushes (+ 1 number-pushes))         (set! current-depth (+ 1 current-depth))         (set! max-depth (max current-depth max-depth)))       (define (pop)         (if (null? s)             (error \"Empty stack -- POP\")             (let ((top (car s)))               (set! s (cdr s))               (set! current-depth (- current-depth 1))               top)))          (define (initialize)         (set! s '())         (set! number-pushes 0)         (set! max-depth 0)         (set! current-depth 0)         'done)       (define (print-statistics)         (newline)         (display (list 'total-pushes  '= number-pushes                        'maximum-depth '= max-depth)))       (define (dispatch message)         (cond ((eq? message 'push) push)               ((eq? message 'pop) (pop))               ((eq? message 'initialize) (initialize))               ((eq? message 'print-statistics)                (print-statistics))               (else                (error \"Unknown request -- STACK\" message)))) "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61058816
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "      dispatch))   Exercises 5.15 through 5.19 describe other useful monitoring and debugging features that can be added to the register -machine simulator . Exer cise 5.14.   Measure the number of pushes and the maximum stack depth required to compute n! for various small values of n using the factorial machine shown in figure 5.11. From your data determine formulasin terms of n for the total number of push operations and the maximum stack depth used in computing n! for any n > 1. Note that each of these is a linear function of n and is thus determined by two constants. In order to get the statistics printed, you will have to augment the factorial machine with instructions to initialize the stack and print the statistics. You may want to also modify the machine so that it repeatedly reads a value for n, computes the factorial, and prints the result (as we did for the GCD machine in figure 5.4), so that you will not have to repeatedly invoke get-register- contents , set-register-contents! , and start . Exer c"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61059840i
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ise 5.15.   Add instruction counting  to the register machine simulation. That is, have the machine model keep track of the number of instructions executed. Extend the machine model's interface to accept a new message that prints the value of the instruction count and resets the count to zero. Exer cise 5.16.   Augment the simulator to provide for instruction tracing . That is, before each instruction is executed, the simulator should print the text of the instruction. Make the machine model accept trace-on  and trace-off  messages to turn tracing on and of f. Exer cise 5.17.   Extend the instruction tracing of exercise 5.16 so that before printing an instruction, the simulator prints any labels that immediately precede that instruction in the controller sequence. Be careful to do this in a way that does not interfere with instruction counting (exercise 5.15). You will have to make the simulator retain the necessary label information. Exer cise 5.18.   Modify the make-register  procedure of section 5.2.1  so "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61060864t
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "that registers can be traced. Registers should accept messages that turn tracing on and of f. When a register is traced, assigning a value to the register should print the name of the register , the old contents of the register , and the new contents being assigned. Extend the interface to the machine model to permit you to turn tracing on and of f for designated machine registers. Exer cise 5.19.   Alyssa P . Hacker wants a breakpoint  feature in the simulator to help her debug her machine designs. You have been hired to install this feature for her . She wants to be able to specify a place in the controllersequence where the simulator will stop and allow her to examine the state of the machine. You are to implement a procedure (set-breakpoint < machine> <label> <n>)  that sets a breakpoint just before the nth instruction after the given label. For example, (set-breakpoint gcd-machine 'test-b 4)   installs a breakpoint in gcd-machine  just before the assignment to register a. When the simulator reaches the b"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61061888r
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "reakpoint it should print the label and the of fset of the breakpoint and stop executing instructions. Alyssa can then use get-register-contents  and set-register-contents!  to manipulate the state of the simulated machine. She should then be able to continue execution by saying (proceed-machine < machine>)  She should also be able to remove a specific breakpoint by means of (cancel-breakpoint < machine> <label> <n>)  or to remove all breakpoints by means of (cancel-all-breakpoints < machine>)  4 Using the receive  procedure here is a way to get extract-labels  to ef fectively return two values \u2013 labels  and insts  \u2013 without explicitly making a compound data structure to hold them. An alternative implementation, which returns an explicit pair of values, is (define (extract-labels text)     (if (null? text)         (cons '() '())         (let ((result (extract-labels (cdr text))))           (let ((insts (car result)) (labels (cdr result)))             (let ((next-inst (car text)))               (if (symbol? ne"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61062912x
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "xt-inst)                   (cons insts                         (cons (make-label-entry next-inst insts) labels))                  (cons (cons (make-instruction next-inst) insts)                         labels)))))))   which would be called by assemble  as follows: (define (assemble controller-text machine)     (let ((result (extract-labels controller-text)))       (let ((insts (car result)) (labels (cdr result)))         (update-insts! insts labels machine)         insts)))   You can consider our use of receive  as demonstrating an elegant way to return multiple values, or simply an excuse to show of f a programming trick. An ar gument like receive  that is the next procedure to be invoked is called a \u201ccontinuation.\u201d Recall that we also used continuations to implement the backtracking control structure in the amb evaluator in section 4.3.3 .5.3  Storage Allocation and Garbage Collection In section 5.4, we will show how to implement a Scheme evaluator as a register machine. In order to simplify the discussion,"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61063936
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " we will assume that our register machines can be equipped with a list-structur ed memory , in which the basic operations for manipulating list-structured data are primitive. Postulating the existence of such a memory is a useful abstraction when one is focusing on the mechanisms of control in a Scheme interpreter , but this does not reflect a realistic view of the actual primitive data operations of contemporary computers. To obtain a more complete picture of how a Lisp system operates, we must investigate how list structure can be represented in a way that is compatible with conventional computer memories. There are two considerations in implementing list structure. The first is purely an issue of representation: how to represent the \u201cbox-and-pointer\u201d structure of Lisp pairs, using only the storage and addressing capabilities of typical computer memories. The second issue concerns the management of memory as a computation proceeds. The operation of a Lisp system depends crucially on the ability to continual"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61064960l
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ly create new data objects. These include objects that are explicitly created by the Lisp procedures being interpreted as well as structures created by the interpreter itself, such as environments and ar gument lists. Although the constant creation of new data objects would pose no problem on a computer with an infinite amount of rapidly addressable memory , computer memories are available only in finite sizes (more's the pity). Lisp systems thus provide an automatic storage allocation  facility to support the illusion of an infinite memory . When a data object is no longer needed, the memory allocated to it is automatically recycled and used to construct new data objects. There are various techniques for providing such automatic storage allocation. The method we shall discuss in this section is called garbage collection . 5.3.1  Memory as V ectors A conventional computer memory can be thought of as an array of cubbyholes, each of which can contain a piece of information. Eachcubbyhole has a unique name, call"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61065984e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ed its address or location . Typical memory systems provide two primitive operations: one that fetches the data stored in a specified location and one that assigns new data to a specified location. Memory addresses can be incremented to support sequential access to some set of the cubbyholes. More generally , many important data operations require that memory addresses be treated as data, which can be stored in memory locations and manipulated in machine registers. The representation of list structure is one application of such address arithmetic . To model computer memory , we use a new kind of data structure called a vector . Abstractly , a vector is a compound data object whose individual elements can be accessed by means of an integer index in an amount of time that is independent of the index.5 In order to describe memory operations, we use two primitive Scheme procedures for manipulating vectors: (vector-ref < vector> <n>) returns the nth element of the vector . (vector-set! < vector> <n> <value>) sets "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61067008t
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "the nth element of the vector to the designated value. For example, if v is a vector , then (vector-ref v 5)  gets the fifth entry in the vector v and (vector-set! v 5 7)  changes the value of the fifth entry of the vector v to 7.6 For computer memory , this access can be implemented through the use of address arithmetic to combine a base addr ess that specifies the beginning location of a vector in memory with an index  that specifies the of fset of a particular element of the vector . Repr esenting Lisp data We can use vectors to implement the basic pair structures required for a list- structured memory . Let us imagine that computer memory is divided into two vectors: the-cars  and the-cdrs . We will represent list structure as follows: A pointer to a pair is an index into the two vectors. The car of the pair is the entry in the-cars  with the designated index, and the cdr of the pair is the entry in the-cdrs  with the designated index. We also need a representation for objects other than pairs (such as nu"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61068032m
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "mbers and symbols)and a way to distinguish one kind of data from another . There are many methods of accomplishing this, but they all reduce to using typed pointers , that is, to extending the notion of \u201cpointer\u201d to include information on data type.7 The data type enables the system to distinguish a pointer to a pair (which consists of the \u201cpair\u201d data type and an index into the memory vectors) from pointers to other kinds of data (which consist of some other data type and whatever is being used to represent data of that type). Two data objects are considered to be the same ( eq?) if their pointers are identical.8 Figure 5.14 illustrates the use of this method to represent the list ((1 2) 3 4) , whose box-and-pointer diagram is also shown. We use letter prefixes to denote the data-type information. Thus, a pointer to the pair with index 5 is denoted p5, the empty list is denoted by the pointer e0, and a pointer to the number 4 is denoted n4. In the box-and-pointer diagram, we have indicated at the lower left o"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61069056f
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "f each pair the vector index that specifies where the car and cdr of the pair are stored. The blank locations in the- cars  and the-cdrs  may contain parts of other list structures (not of interest here). Figur e 5.14:   Box-and-pointer and memory-vector representations of the list ((1 2) 3 4) . A pointer to a number , such as n4, might consist of a type indicating numeric data together with the actual representation of the number 4.9 To deal with numbers that are too lar ge to be represented in the fixed amount of space allocated for a single pointer , we could use a distinct bignum  data type, for which the pointer designates a list in which the parts of the number are stored.10A symbol might be represented as a typed pointer that designates a sequence of the characters that form the symbol's printed representation. This sequence is constructed by the Lisp reader when the character string is initially encountered in input. Since we want two instances of a symbol to be recognized as the \u201csame\u201d symbol by eq? "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61070080a
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "and we want eq? to be a simple test for equality of pointers, we must ensure that if the reader sees the same character string twice, it will use the same pointer (to the same sequence of characters) to represent both occurrences. To accomplish this, the reader maintains a table, traditionally called the obarray , of all the symbols it has ever encountered. When the reader encounters a character string and is about to construct a symbol, it checks the obarray to see if it has ever before seen the same character string. If it has not, it uses the characters to construct a new symbol (a typed pointer to a new character sequence) and enters this pointer in the obarray . If the reader has seen the string before, it returns the symbol pointer stored in the obarray . This process of replacing character strings by unique pointers is called interning  symbols. Implementing the primitive list operations Given the above representation scheme, we can replace each \u201cprimitive\u201d list operation of a register machine with one"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61071104
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " or more primitive vector operations. We will use two registers, the-cars  and the-cdrs , to identify the memory vectors, and will assume that vector-ref  and vector-set!  are available as primitive operations. We also assume that numeric operations on pointers (such as incrementing a pointer , using a pair pointer to index a vector , or adding two numbers) use only the index portion of the typed pointer . For example, we can make a register machine support the instructions (assign < reg1> (op car) (reg < reg2>))    (assign < reg1> (op cdr) (reg < reg2>))  if we implement these, respectively , as(assign < reg1> (op vector-ref) (reg the-cars) (reg < reg2>))    (assign < reg1> (op vector-ref) (reg the-cdrs) (reg < reg2>))  The instructions (perform (op set-car!) (reg < reg1>) (reg < reg2>))    (perform (op set-cdr!) (reg < reg1>) (reg < reg2>))  are implemented as (perform    (op vector-set!) (reg the-cars) (reg < reg1>) (reg < reg2>))    (perform    (op vector-set!) (reg the-cdrs) (reg < reg1>) (reg < reg2>)) "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61072128
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " Cons  is performed by allocating an unused index and storing the ar guments to cons  in the-cars  and the-cdrs  at that indexed vector position. We presume that there is a special register , free , that always holds a pair pointer containing the next available index, and that we can increment the index part of that pointer to find the next free location.11 For example, the instruction (assign < reg1> (op cons) (reg < reg2>) (reg < reg3>))  is implemented as the following sequence of vector operations:12 (perform    (op vector-set!) (reg the-cars) (reg free) (reg < reg2>))  (perform    (op vector-set!) (reg the-cdrs) (reg free) (reg < reg3>))  (assign < reg1> (reg free))   (assign free (op +) (reg free) (const 1))   The eq? operation(op eq?) (reg < reg1>) (reg < reg2>)  simply tests the equality of all fields in the registers, and predicates such as pair? , null? , symbol? , and number?  need only check the type field. Implementing stacks Although our register machines use stacks, we need do nothing special h"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61073152e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ere, since stacks can be modeled in terms of lists. The stack can be a list of the saved values, pointed to by a special register the-stack . Thus, (save <reg>) can be implemented as (assign the-stack (op cons) (reg < reg>) (reg the-stack))   Similarly , (restore < reg>) can be implemented as (assign < reg> (op car) (reg the-stack))   (assign the-stack (op cdr) (reg the-stack))   and (perform (op initialize-stack))  can be implemented as (assign the-stack (const ()))   These operations can be further expanded in terms of the vector operations given above. In conventional computer architectures, however , it is usually advantageous to allocate the stack as a separate vector . Then pushing and popping the stack can be accomplished by incrementing or decrementing an index into that vector . Exer cise 5.20.   Draw the box-and-pointer representation and the memory- vector representation (as in figure 5.14) of the list structure produced by (define x (cons 1 2))   (define y (list x x))   with the free  pointer init"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61074176i
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ially p1. What is the final value of free  ? What pointers represent the values of x and y ?Exer cise 5.21.   Implement register machines for the following procedures. Assume that the list-structure memory operations are available as machine primitives. a. Recursive count-leaves : (define (count-leaves tree)     (cond ((null? tree) 0)           ((not (pair? tree)) 1)           (else (+ (count-leaves (car tree))                    (count-leaves (cdr tree))))))   b. Recursive count-leaves  with explicit counter: (define (count-leaves tree)     (define (count-iter tree n)       (cond ((null? tree) n)             ((not (pair? tree)) (+ n 1))             (else (count-iter (cdr tree)                               (count-iter (car tree) n)))))     (count-iter tree 0))   Exer cise 5.22.   Exercise 3.12 of section 3.3.1  presented an append procedure that appends two lists to form a new list and an append! procedure that splices two lists together . Design a register machine to implement each of these procedures. Assu"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61075200m
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "me that the list-structure memory operations are available as primitive operations. 5.3.2  Maintaining the Illusion of Infinite Memory The representation method outlined in section 5.3.1  solves the problem of implementing list structure, provided that we have an infinite amount of memory . With a real computer we will eventually run out of free space in which to construct new pairs.13 However , most of the pairs generated in a typical computation are used only to hold intermediate results. After these results are accessed, the pairs are no longer needed \u2013 they are garbage . For instance, the computation(accumulate + 0 (filter odd? (enumerate-interval 0 n)))   constructs two lists: the enumeration and the result of filtering the enumeration. When the accumulation is complete, these lists are no longer needed, and the allocated memory can be reclaimed. If we can arrange to collect all the garbage periodically , and if this turns out to recycle memory at about the same rate at which we construct new pairs, we w"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61076224i
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ill have preserved the illusion that there is an infinite amount of memory . In order to recycle pairs, we must have a way to determine which allocated pairs are not needed (in the sense that their contents can no longer influence the future of the computation). The method we shall examine for accomplishing this is known as garbage collection . Garbage collection is based on the observation that, at any moment in a Lisp interpretation, the only objects that can af fect the future of the computation are those that can be reached by some succession of car and cdr operations starting from the pointers that are currently in the machine registers.14 Any memory cell that is not so accessible may be recycled. There are many ways to perform garbage collection. The method we shall examine here is called stop-and-copy . The basic idea is to divide memory into two halves: \u201cworking memory\u201d and \u201cfree memory .\u201d When cons constructs pairs, it allocates these in working memory . When working memory is full, we perform garbag"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61077248e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "e collection by locating all the useful pairs in working memory and copying these into consecutive locations in free memory . (The useful pairs are located by tracing all the car and cdr pointers, starting with the machine registers.) Since we do not copy the garbage, there will presumably be additional free memory that we can use to allocate new pairs. In addition, nothing in the working memory is needed, since all the useful pairs in it have been copied. Thus, if we interchange the roles of working memory and free memory , we can continue processing; new pairs will be allocated in the new working memory (which was the old free memory). When this is full, we can copy the useful pairs into the new free memory (which was the old working memory).15 Implementation of a stop-and-copy garbage collectorWe now use our register -machine language to describe the stop-and-copy algorithm in more detail. We will assume that there is a register called root that contains a pointer to a structure that eventually points at a"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61078272l
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ll accessible data. This can be arranged by storing the contents of all the machine registers in a pre-allocated list pointed at by root  just before starting garbage collection.16 We also assume that, in addition to the current working memory , there is free memory available into which we can copy the useful data. The current working memory consists of vectors whose base addresses are in registers called the-cars  and the-cdrs , and the free memory is in registers called new-cars  and new-cdrs . Garbage collection is triggered when we exhaust the free cells in the current working memory , that is, when a cons  operation attempts to increment the free  pointer beyond the end of the memory vector . When the garbage- collection process is complete, the root  pointer will point into the new memory , all objects accessible from the root  will have been moved to the new memory , and the free  pointer will indicate the next place in the new memory where a new pair can be allocated. In addition, the roles of working"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61079296
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " memory and new memory will have been interchanged \u2013 new pairs will be constructed in the new memory , beginning at the place indicated by free , and the (previous) working memory will be available as the new memory for the next garbage collection. Figure 5.15 shows the arrangement of memory just before and just after garbage collection. Figur e 5.15:   Reconfiguration of memory by the garbage-collection process.The state of the garbage-collection process is controlled by maintaining two pointers: free  and scan . These are initialized to point to the beginning of the new memory . The algorithm begins by relocating the pair pointed at by root  to the beginning of the new memory . The pair is copied, the root pointer is adjusted to point to the new location, and the free  pointer is incremented. In addition, the old location of the pair is marked to show that its contents have been moved. This marking is done as follows: In the car position, we place a special tag that signals that this is an already-moved obj"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61080320e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ect. (Such an object is traditionally called a broken heart .)17 In the cdr position we place a forwar ding addr ess that points at the location to which the object has been moved. After relocating the root, the garbage collector enters its basic cycle. At each step in the algorithm, the scan  pointer (initially pointing at the relocated root) points at a pair that has been moved to the new memory butwhose car and cdr pointers still refer to objects in the old memory . These objects are each relocated, and the scan  pointer is incremented. To relocate an object (for example, the object indicated by the car pointer of the pair we are scanning) we check to see if the object has already been moved (as indicated by the presence of a broken-heart tag in the car position of the object). If the object has not already been moved, we copy it to the place indicated by free , update free , set up a broken heart at the object's old location, and update the pointer to the object (in this example, the car pointer of the pa"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61081344i
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ir we are scanning) to point to the new location. If the object has already been moved, its forwarding address (found in the cdr position of the broken heart) is substituted for the pointer in the pair being scanned. Eventually , all accessible objects will have been moved and scanned, at which point the scan  pointer will overtake the free  pointer and the process will terminate. We can specify the stop-and-copy algorithm as a sequence of instructions for a register machine. The basic step of relocating an object is accomplished by a subroutine called relocate-old-result-in-new . This subroutine gets its ar gument, a pointer to the object to be relocated, from a register named old. It relocates the designated object (incrementing free  in the process), puts a pointer to the relocated object into a register called new, and returns by branching to the entry point stored in the register relocate- continue . To begin garbage collection, we invoke this subroutine to relocate the root  pointer , after initializing"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61082368
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " free  and scan . When the relocation of root  has been accomplished, we install the new pointer as the new root  and enter the main loop of the garbage collector . begin-garbage-collection     (assign free (const 0))     (assign scan (const 0))     (assign old (reg root))     (assign relocate-continue (label reassign-root))     (goto (label relocate-old-result-in-new))   reassign-root     (assign root (reg new))     (goto (label gc-loop))  In the main loop of the garbage collector we must determine whether there are any more objects to be scanned. We do this by testing whether the scan pointer is coincident with the free  pointer . If the pointers are equal, then all accessible objects have been relocated, and we branch to gc-flip , which cleans things up so that we can continue the interrupted computation. If there are still pairs to be scanned, we call the relocate subroutine to relocate the car of the next pair (by placing the car pointer in old). The relocate- continue  register is set up so that the sub"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61083392r
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "routine will return to update the car pointer . gc-loop    (test (op =) (reg scan) (reg free))     (branch (label gc-flip))     (assign old (op vector-ref) (reg new-cars) (reg scan))     (assign relocate-continue (label update-car))     (goto (label relocate-old-result-in-new))   At update-car , we modify the car pointer of the pair being scanned, then proceed to relocate the cdr of the pair . We return to update-cdr  when that relocation has been accomplished. After relocating and updating the cdr, we are finished scanning that pair , so we continue with the main loop. update-car     (perform      (op vector-set!) (reg new-cars) (reg scan) (reg new))     (assign old (op vector-ref) (reg new-cdrs) (reg scan))     (assign relocate-continue (label update-cdr))     (goto (label relocate-old-result-in-new))     update-cdr     (perform      (op vector-set!) (reg new-cdrs) (reg scan) (reg new))     (assign scan (op +) (reg scan) (const 1))     (goto (label gc-loop))   The subroutine relocate-old-result-in-new  relo"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61084416c
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "cates objects as follows: If the object to be relocated (pointed at by old) is not a pair , then we return the same pointer to the object unchanged (in new). (For example, we may bescanning a pair whose car is the number 4. If we represent the car by n4, as described in section 5.3.1 , then we want the \u201crelocated\u201d car pointer to still be n4.) Otherwise, we must perform the relocation. If the car position of the pair to be relocated contains a broken-heart tag, then the pair has in fact already been moved, so we retrieve the forwarding address (from the cdr position of the broken heart) and return this in new. If the pointer in old points at a yet-unmoved pair , then we move the pair to the first free cell in new memory (pointed at by free ) and set up the broken heart by storing a broken-heart tag and forwarding address at the old location. Relocate-old- result-in-new  uses a register oldcr  to hold the car or the cdr of the object pointed at by old.18 relocate-old-result-in-new     (test (op pointer-to-pair?"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61085440)
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": ") (reg old))     (branch (label pair))     (assign new (reg old))     (goto (reg relocate-continue))   pair    (assign oldcr (op vector-ref) (reg the-cars) (reg old))     (test (op broken-heart?) (reg oldcr))     (branch (label already-moved))     (assign new (reg free)) ; new location for pair     ;; Update free pointer.     (assign free (op +) (reg free) (const 1))     ;; Copy the car and cdr to new memory.     (perform (op vector-set!)              (reg new-cars) (reg new) (reg oldcr))     (assign oldcr (op vector-ref) (reg the-cdrs) (reg old))     (perform (op vector-set!)              (reg new-cdrs) (reg new) (reg oldcr))     ;; Construct the broken heart.     (perform (op vector-set!)              (reg the-cars) (reg old) (const broken-heart))     (perform      (op vector-set!) (reg the-cdrs) (reg old) (reg new))     (goto (reg relocate-continue))   already-moved    (assign new (op vector-ref) (reg the-cdrs) (reg old))     (goto (reg relocate-continue))   At the very end of the garbage-collection proces"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61086464s
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "s, we interchange the role of old and new memories by interchanging pointers: interchanging the- cars  with new-cars , and the-cdrs  with new-cdrs . We will then be ready to perform another garbage collection the next time memory runs out. gc-flip    (assign temp (reg the-cdrs))     (assign the-cdrs (reg new-cdrs))     (assign new-cdrs (reg temp))     (assign temp (reg the-cars))     (assign the-cars (reg new-cars))     (assign new-cars (reg temp))   5 We could represent memory as lists of items. However , the access time would then not be independent of the index, since accessing the nth element of a list requires n - 1 cdr operations. 6 For completeness, we should specify a make-vector  operation that constructs vectors. However , in the present application we will use vectors only to model fixed divisions of the computer memory . 7 This is precisely the same \u201ctagged data\u201d idea we introduced in chapter 2 for dealing with generic operations. Here, however , the data types are included at the primitive machin"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61087488e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "e level rather than constructed through the use of lists. 8 Type information may be encoded in a variety of ways, depending on the details of the machine on which the Lisp system is to be implemented. The execution ef ficiency of Lisp programs will be strongly dependent on how cleverly this choice is made, but it is dif ficult to formulate general design rules for good choices. The most straightforward way to implement typed pointers is to allocate a fixed set of bits in each pointer to be a type field  that encodes the data type. Important questions to be addressed in designing such a representation include the following: How many type bits are required? How lar ge must the vector indices be? How ef ficiently can the primitive machine instructions be used to manipulate the type fields of pointers? Machines that include special hardware for the ef ficient handling of type fields are said to have tagged ar chitectur es. 9 This decision on the representation of numbers determines whether eq?, which tests equali"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61088512t
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ty of pointers, can be used to test for equality of numbers. If the pointer contains the number itself, then equal numbers will have the same pointer . But if the pointer contains the index of a location where the number is stored, equal numbers will be guaranteed to have equal pointers only if we are careful never to store the same number in more than one location.10 This is just like writing a number as a sequence of digits, except that each \u201cdigit\u201d is a number between 0 and the lar gest number that can be stored in a single pointer . 11 There are other ways of finding free storage. For example, we could link together all the unused pairs into a free list . Our free locations are consecutive (and hence can be accessed by incrementing a pointer) because we are using a compacting garbage collector , as we will see in section 5.3.2 . 12 This is essentially the implementation of cons  in terms of set-car!  and set-cdr! , as described in section 3.3.1 . The operation get-new-pair  used in that implementation is "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61089536r
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "realized here by the free pointer . 13 This may not be true eventually , because memories may get lar ge enough so that it would be impossible to run out of free memory in the lifetime of the computer . For example, there are about 3\u00d7 1013, microseconds in a year , so if we were to cons  once per microsecond we would need about 1015 cells of memory to build a machine that could operate for 30 years without running out of memory . That much memory seems absurdly lar ge by today's standards, but it is not physically impossible. On the other hand, processors are getting faster and a future computer may have lar ge numbers of processors operating in parallel on a single memory , so it may be possible to use up memory much faster than we have postulated. 14 We assume here that the stack is represented as a list as described in section 5.3.1 , so that items on the stack are accessible via the pointer in the stack register . 15 This idea was invented and first implemented by Minsky , as part of the implementation of"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61090560
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " Lisp for the PDP-1 at the MIT Research Laboratory of Electronics. It was further developed by Fenichel and Yochelson (1969) for use in the Lisp implementation for the Multics time-sharing system. Later , Baker (1978) developed a \u201creal-time\u201d version of the method, which does not require the computation to stop during garbage collection. Baker's idea was extended by Hewitt, Lieberman, and Moon (see Lieberman and Hewitt 1983) to take advantage of the fact that some structure is more volatile and other structure is more permanent. An alternative commonly used garbage-collection technique is the mark-sweep  method. This consists of tracing all the structure accessible from the machine registers and marking each pair we reach. We then scan all of memory , and any location that is unmarked is \u201cswept up\u201d as garbage and made available for reuse. A full discussion of the mark-sweep method can be found in Allen 1978. The Minsky-Fenichel-Y ochelson algorithm is the dominant algorithm in use for lar ge-memory systems bec"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61091584a
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ause it examines only the useful part of memory . This is in contrast to mark-sweep, in which the sweep phase must check all of memory . A second advantage of stop-and-copy is that it is a compacting  garbage collector . That is, at the end of the garbage-collection phase the useful data will have been moved to consecutive memory locations, with all garbage pairs compressed out. This can be an extremely important performance consideration in machines with virtual memory , in which accesses to widely separated memory addresses may require extra paging operations. 16 This list of registers does not include the registers used by the storage-allocation system \u2013 root , the-cars , the-cdrs , and the other registers that will be introduced in this section. 17 The term broken heart  was coined by David Cressey , who wrote a garbage collector for MDL, a dialect of Lisp developed at MIT during the early 1970s.18 The garbage collector uses the low-level predicate pointer-to-pair?  instead of the list-structure pair?  op"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61092608e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "eration because in a real system there might be various things that are treated as pairs for garbage-collection purposes. For example, in a Scheme system that conforms to the IEEE standard a procedure object may be implemented as a special kind of \u201cpair\u201d that doesn't satisfy the pair? predicate. For simulation purposes, pointer-to-pair?  can be implemented as pair? .5.4  The Explicit-Control Evaluator In section 5.1 we saw how to transform simple Scheme programs into descriptions of register machines. We will now perform this transformation on a more complex program, the metacircular evaluator of sections 4.1.1 - 4.1.4 , which shows how the behavior of a Scheme interpreter can be described in terms of the procedures eval  and apply . The explicit-contr ol evaluator  that we develop in this section shows how the underlying procedure-calling and ar gument-passing mechanisms used in the evaluation process can be described in terms of operations on registers and stacks. In addition, the explicit-control evaluator"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61093632
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " can serve as an implementation of a Scheme interpreter , written in a language that is very similar to the native machine language of conventional computers. The evaluator can be executed by the register -machine simulator of section 5.2. Alternatively , it can be used as a starting point for building a machine-language implementation of a Scheme evaluator , or even a special-purpose machine for evaluating Scheme expressions. Figure 5.16 shows such a hardware implementation: a silicon chip that acts as an evaluator for Scheme. The chip designers started with the data-path and controller specifications for a register machine similar to the evaluator described in this section and used design automation programs to construct the integrated-circuit layout.19 Registers and operations In designing the explicit-control evaluator , we must specify the operations to be used in our register machine. We described the metacircular evaluator in terms of abstract syntax, using procedures such as quoted?  and make- procedu"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61094656r
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "re . In implementing the register machine, we could expand these procedures into sequences of elementary list-structure memory operations, and implement these operations on our register machine. However , this would make our evaluator very long, obscuring the basic structure with details. To clarify the presentation, we will include as primitive operations of the register machine the syntax procedures given in section 4.1.2  and the procedures for representing environments and other run-time data given in sections 4.1.3  and 4.1.4 . In order to completely specify an evaluator that could be programmed in a low-level machine language or implemented inhardware, we would replace these operations by more elementary operations, using the list-structure implementation we described in section 5.3. Figur e 5.16:   A silicon-chip implementation of an evaluator for Scheme. Our Scheme evaluator register machine includes a stack and seven registers: exp, env, val, continue , proc , argl , and unev . Exp is used to hold th"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61095680e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "e expression to be evaluated, and env contains the environment in which the evaluation is to be performed. At the end of an evaluation, val contains the value obtained by evaluating the expression in the designated environment. The continue  register is used to implement recursion, as explained in section 5.1.4 . (The evaluator needs to call itself recursively , since evaluating an expression requires evaluating its subexpressions.) The registers proc , argl , and unev  are used in evaluating combinations. We will not provide a data-path diagram to show how the registers and operations of the evaluator are connected, nor will we give the complete list of machine operations. These are implicit in the evaluator's controller , which will be presented in detail. 5.4.1  The Cor e of the Explicit-Contr ol EvaluatorThe central element in the evaluator is the sequence of instructions beginning at eval-dispatch . This corresponds to the eval  procedure of the metacircular evaluator described in section 4.1.1 . When th"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61096704e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "e controller starts at eval-dispatch , it evaluates the expression specified by exp in the environment specified by env. When evaluation is complete, the controller will go to the entry point stored in continue , and the val register will hold the value of the expression. As with the metacircular eval , the structure of eval-dispatch  is a case analysis on the syntactic type of the expression to be evaluated.20 eval-dispatch     (test (op self-evaluating?) (reg exp))     (branch (label ev-self-eval))     (test (op variable?) (reg exp))     (branch (label ev-variable))     (test (op quoted?) (reg exp))     (branch (label ev-quoted))     (test (op assignment?) (reg exp))     (branch (label ev-assignment))     (test (op definition?) (reg exp))     (branch (label ev-definition))     (test (op if?) (reg exp))     (branch (label ev-if))     (test (op lambda?) (reg exp))     (branch (label ev-lambda))     (test (op begin?) (reg exp))     (branch (label ev-begin))     (test (op application?) (reg exp))     (branch (l"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61097728a
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "abel ev-application))     (goto (label unknown-expression-type))   Evaluating simple expr essions Numbers and strings (which are self-evaluating), variables, quotations, and lambda  expressions have no subexpressions to be evaluated. For these, the evaluator simply places the correct value in the val register and continuesexecution at the entry point specified by continue . Evaluation of simple expressions is performed by the following controller code: ev-self-eval     (assign val (reg exp))     (goto (reg continue))   ev-variable     (assign val (op lookup-variable-value) (reg exp) (reg env))     (goto (reg continue))   ev-quoted     (assign val (op text-of-quotation) (reg exp))     (goto (reg continue))   ev-lambda     (assign unev (op lambda-parameters) (reg exp))     (assign exp (op lambda-body) (reg exp))     (assign val (op make-procedure)                 (reg unev) (reg exp) (reg env))     (goto (reg continue))   Observe how ev-lambda  uses the unev  and exp registers to hold the parameters and body of"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61098752
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " the lambda expression so that they can be passed to the make-procedure  operation, along with the environment in env. Evaluating pr ocedur e applications A procedure application is specified by a combination containing an operator and operands. The operator is a subexpression whose value is a procedure, and the operands are subexpressions whose values are the arguments to which the procedure should be applied. The metacircular eval handles applications by calling itself recursively to evaluate each element of the combination, and then passing the results to apply , which performs the actual procedure application. The explicit-control evaluator does the same thing; these recursive calls are implemented by goto  instructions, together with use of the stack to save registers that will be restored after the recursive call returns. Before each call we will be careful to identify which registers must be saved (because their values will be needed later).21We begin the evaluation of an application by evaluating the "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61099776o
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "operator to produce a procedure, which will later be applied to the evaluated operands. To evaluate the operator , we move it to the exp register and go to eval- dispatch . The environment in the env register is already the correct one in which to evaluate the operator . However , we save env because we will need it later to evaluate the operands. We also extract the operands into unev  and save this on the stack. We set up continue  so that eval-dispatch  will resume at ev-appl-did-operator  after the operator has been evaluated. First, however , we save the old value of continue , which tells the controller where to continue after the application. ev-application     (save continue)     (save env)     (assign unev (op operands) (reg exp))     (save unev)     (assign exp (op operator) (reg exp))     (assign continue (label ev-appl-did-operator))     (goto (label eval-dispatch))   Upon returning from evaluating the operator subexpression, we proceed to evaluate the operands of the combination and to accumulate"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61100800
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " the resulting arguments in a list, held in argl . First we restore the unevaluated operands and the environment. We initialize argl  to an empty list. Then we assign to the proc  register the procedure that was produced by evaluating the operator . If there are no operands, we go directly to apply-dispatch . Otherwise we save proc  on the stack and start the ar gument-evaluation loop:22 ev-appl-did-operator     (restore unev)                  ; the operands     (restore env)     (assign argl (op empty-arglist))     (assign proc (reg val))         ; the operator     (test (op no-operands?) (reg unev))     (branch (label apply-dispatch))     (save proc)  Each cycle of the ar gument-evaluation loop evaluates an operand from the list in unev  and accumulates the result into argl . To evaluate an operand, we place it in the exp register and go to eval-dispatch , after setting continue  so that execution will resume with the ar gument-accumulation phase. But first we save the ar guments accumulated so far (held in"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61101824
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " argl ), the environment (held in env), and the remaining operands to be evaluated (held in unev ). A special case is made for the evaluation of the last operand, which is handled at ev-appl-last-arg . ev-appl-operand-loop     (save argl)     (assign exp (op first-operand) (reg unev))     (test (op last-operand?) (reg unev))     (branch (label ev-appl-last-arg))     (save env)     (save unev)     (assign continue (label ev-appl-accumulate-arg))     (goto (label eval-dispatch))   When an operand has been evaluated, the value is accumulated into the list held in argl . The operand is then removed from the list of unevaluated operands in unev , and the ar gument-evaluation continues. ev-appl-accumulate-arg     (restore unev)     (restore env)     (restore argl)     (assign argl (op adjoin-arg) (reg val) (reg argl))     (assign unev (op rest-operands) (reg unev))     (goto (label ev-appl-operand-loop))   Evaluation of the last ar gument is handled dif ferently . There is no need to save the environment or the lis"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61102848t
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "t of unevaluated operands before going to eval-dispatch , since they will not be required after the last operand is evaluated. Thus, we return from the evaluation to a special entry point ev- appl-accum-last-arg , which restores the ar gument list, accumulates the new ar gument, restores the saved procedure, and goes of f to perform the application.23ev-appl-last-arg     (assign continue (label ev-appl-accum-last-arg))     (goto (label eval-dispatch))   ev-appl-accum-last-arg     (restore argl)     (assign argl (op adjoin-arg) (reg val) (reg argl))     (restore proc)     (goto (label apply-dispatch))   The details of the ar gument-evaluation loop determine the order in which the interpreter evaluates the operands of a combination (e.g., left to right or right to left \u2013 see exercise 3.8). This order is not determined by the metacircular evaluator , which inherits its control structure from the underlying Scheme in which it is implemented.24 Because the first- operand  selector (used in ev-appl-operand-loop  to"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61103872
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " extract successive operands from unev ) is implemented as car and the rest-operands  selector is implemented as cdr, the explicit-control evaluator will evaluate the operands of a combination in left-to-right order . Procedur e application The entry point apply-dispatch  corresponds to the apply  procedure of the metacircular evaluator . By the time we get to apply-dispatch , the proc register contains the procedure to apply and argl  contains the list of evaluated ar guments to which it must be applied. The saved value of continue  (originally passed to eval-dispatch  and saved at ev- application ), which tells where to return with the result of the procedure application, is on the stack. When the application is complete, the controller transfers to the entry point specified by the saved continue , with the result of the application in val. As with the metacircular apply , there are two cases to consider . Either the procedure to be applied is a primitive or it is a compound procedure. apply-dispatch     (t"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61104896e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "est (op primitive-procedure?) (reg proc))     (branch (label primitive-apply))     (test (op compound-procedure?) (reg proc))     (branch (label compound-apply))     (goto (label unknown-procedure-type))   We assume that each primitive is implemented so as to obtain its ar guments from argl  and place its result in val. To specify how the machine handles primitives, we would have to provide a sequence of controller instructions to implement each primitive and arrange for primitive-apply  to dispatch to the instructions for the primitive identified by the contents of proc . Since we are interested in the structure of the evaluation process rather than the details of the primitives, we will instead just use an apply-primitive- procedure  operation that applies the procedure in proc  to the ar guments in argl . For the purpose of simulating the evaluator with the simulator of section 5.2 we use the procedure apply-primitive-procedure , which calls on the underlying Scheme system to perform the application, just "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61105920a
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "as we did for the metacircular evaluator in section 4.1.4 . After computing the value of the primitive application, we restore continue  and go to the designated entry point. primitive-apply     (assign val (op apply-primitive-procedure)                 (reg proc)                 (reg argl))     (restore continue)     (goto (reg continue))   To apply a compound procedure, we proceed just as with the metacircular evaluator . We construct a frame that binds the procedure's parameters to the arguments, use this frame to extend the environment carried by the procedure, and evaluate in this extended environment the sequence of expressions that forms the body of the procedure. Ev-sequence , described below in section 5.4.2 , handles the evaluation of the sequence. compound-apply     (assign unev (op procedure-parameters) (reg proc))     (assign env (op procedure-environment) (reg proc))     (assign env (op extend-environment)                 (reg unev) (reg argl) (reg env))    (assign unev (op procedure-body) (reg "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61106944p
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "proc))     (goto (label ev-sequence))   Compound-apply  is the only place in the interpreter where the env register is ever assigned a new value. Just as in the metacircular evaluator , the new environment is constructed from the environment carried by the procedure, together with the ar gument list and the corresponding list of variables to be bound. 5.4.2  Sequence Evaluation and T ail Recursion The portion of the explicit-control evaluator at ev-sequence  is analogous to the metacircular evaluator's eval-sequence  procedure. It handles sequences of expressions in procedure bodies or in explicit begin  expressions. Explicit begin  expressions are evaluated by placing the sequence of expressions to be evaluated in unev , saving continue  on the stack, and jumping to ev-sequence . ev-begin     (assign unev (op begin-actions) (reg exp))     (save continue)     (goto (label ev-sequence))   The implicit sequences in procedure bodies are handled by jumping to ev- sequence  from compound-apply , at which point con"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61107968t
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "tinue  is already on the stack, having been saved at ev-application . The entries at ev-sequence  and ev-sequence-continue  form a loop that successively evaluates each expression in a sequence. The list of unevaluated expressions is kept in unev . Before evaluating each expression, we check to see if there are additional expressions to be evaluated in the sequence. If so, we save the rest of the unevaluated expressions (held in unev ) and the environment in which these must be evaluated (held in env) and call eval-dispatch  to evaluate the expression. The two saved registers are restored upon the return from this evaluation, at ev-sequence- continue .The final expression in the sequence is handled dif ferently , at the entry point ev-sequence-last-exp . Since there are no more expressions to be evaluated after this one, we need not save unev  or env before going to eval-dispatch . The value of the whole sequence is the value of the last expression, so after the evaluation of the last expression there is noth"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61108992i
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ing left to do except continue at the entry point currently held on the stack (which was saved by ev-application  or ev-begin .) Rather than setting up continue  to arrange for eval-dispatch  to return here and then restoring continue  from the stack and continuing at that entry point, we restore continue  from the stack before going to eval-dispatch , so that eval- dispatch  will continue at that entry point after evaluating the expression. ev-sequence     (assign exp (op first-exp) (reg unev))     (test (op last-exp?) (reg unev))     (branch (label ev-sequence-last-exp))     (save unev)     (save env)     (assign continue (label ev-sequence-continue))     (goto (label eval-dispatch))   ev-sequence-continue     (restore env)     (restore unev)     (assign unev (op rest-exps) (reg unev))     (goto (label ev-sequence))   ev-sequence-last-exp     (restore continue)     (goto (label eval-dispatch))   Tail recursion In chapter 1 we said that the process described by a procedure such as (define (sqrt-iter guess x)"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61110016
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "     (if (good-enough? guess x)         guess        (sqrt-iter (improve guess x)                    x)))   is an iterative process. Even though the procedure is syntactically recursive (defined in terms of itself), it is not logically necessary for an evaluator to save information in passing from one call to sqrt-iter  to the next.25 An evaluator that can execute a procedure such as sqrt-iter  without requiring increasing storage as the procedure continues to call itself is called a tail- recursive  evaluator . The metacircular implementation of the evaluator in chapter 4 does not specify whether the evaluator is tail-recursive, because that evaluator inherits its mechanism for saving state from the underlying Scheme. With the explicit-control evaluator , however , we can trace through the evaluation process to see when procedure calls cause a net accumulation of information on the stack. Our evaluator is tail-recursive, because in order to evaluate the final expression of a sequence we transfer directly to "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61111040e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "eval-dispatch  without saving any information on the stack. Hence, evaluating the final expression in a sequence \u2013 even if it is a procedure call (as in sqrt-iter , where the if expression, which is the last expression in the procedure body , reduces to a call to sqrt-iter ) \u2013 will not cause any information to be accumulated on the stack.26 If we did not think to take advantage of the fact that it was unnecessary to save information in this case, we might have implemented eval-sequence by treating all the expressions in a sequence in the same way \u2013 saving the registers, evaluating the expression, returning to restore the registers, and repeating this until all the expressions have been evaluated:27 ev-sequence     (test (op no-more-exps?) (reg unev))     (branch (label ev-sequence-end))     (assign exp (op first-exp) (reg unev))     (save unev)     (save env)     (assign continue (label ev-sequence-continue))     (goto (label eval-dispatch))  ev-sequence-continue     (restore env)     (restore unev)     (assi"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61112064g
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "gn unev (op rest-exps) (reg unev))     (goto (label ev-sequence))   ev-sequence-end     (restore continue)     (goto (reg continue))   This may seem like a minor change to our previous code for evaluation of a sequence: The only dif ference is that we go through the save-restore cycle for the last expression in a sequence as well as for the others. The interpreter will still give the same value for any expression. But this change is fatal to the tail-recursive implementation, because we must now return after evaluating the final expression in a sequence in order to undo the (useless) register saves. These extra saves will accumulate during a nest of procedure calls. Consequently , processes such as sqrt-iter  will require space proportional to the number of iterations rather than requiring constant space. This dif ference can be significant. For example, with tail recursion, an infinite loop can be expressed using only the procedure-call mechanism: (define (count n)     (newline)     (display n)     (count (+"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61113088
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " n 1)))   Without tail recursion, such a procedure would eventually run out of stack space, and expressing a true iteration would require some control mechanism other than procedure call. 5.4.3  Conditionals, Assignments, and Definitions As with the metacircular evaluator , special forms are handled by selectively evaluating fragments of the expression. For an if expression, we must evaluate the predicate and decide, based on the value of predicate, whether to evaluate the consequent or the alternative.Before evaluating the predicate, we save the if expression itself so that we can later extract the consequent or alternative. We also save the environment, which we will need later in order to evaluate the consequent or the alternative, and we save continue , which we will need later in order to return to the evaluation of the expression that is waiting for the value of the if. ev-if    (save exp)                    ; save expression for later     (save env)     (save continue)     (assign continue (label ev-if"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61114112-
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "-decide))     (assign exp (op if-predicate) (reg exp))     (goto (label eval-dispatch))  ; evaluate the predicate   When we return from evaluating the predicate, we test whether it was true or false and, depending on the result, place either the consequent or the alternative in exp before going to eval-dispatch . Notice that restoring env and continue  here sets up eval-dispatch  to have the correct environment and to continue at the right place to receive the value of the if expression. ev-if-decide     (restore continue)     (restore env)     (restore exp)     (test (op true?) (reg val))     (branch (label ev-if-consequent))     ev-if-alternative     (assign exp (op if-alternative) (reg exp))     (goto (label eval-dispatch))   ev-if-consequent     (assign exp (op if-consequent) (reg exp))     (goto (label eval-dispatch))   Assignments and definitionsAssignments are handled by ev-assignment , which is reached from eval- dispatch  with the assignment expression in exp. The code at ev- assignment  first evalua"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61115136t
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "tes the value part of the expression and then installs the new value in the environment. Set-variable-value!  is assumed to be available as a machine operation. ev-assignment     (assign unev (op assignment-variable) (reg exp))     (save unev)                   ; save variable for later     (assign exp (op assignment-value) (reg exp))     (save env)     (save continue)     (assign continue (label ev-assignment-1))     (goto (label eval-dispatch))  ; evaluate the assignment value   ev-assignment-1     (restore continue)     (restore env)     (restore unev)     (perform      (op set-variable-value!) (reg unev) (reg val) (reg env))     (assign val (const ok))     (goto (reg continue))   Definitions are handled in a similar way: ev-definition     (assign unev (op definition-variable) (reg exp))     (save unev)                   ; save variable for later     (assign exp (op definition-value) (reg exp))     (save env)     (save continue)     (assign continue (label ev-definition-1))     (goto (label eval-dispatch))"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61116160
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "  ; evaluate the definition value   ev-definition-1     (restore continue)     (restore env)     (restore unev)    (perform      (op define-variable!) (reg unev) (reg val) (reg env))     (assign val (const ok))     (goto (reg continue))   Exer cise 5.23.   Extend the evaluator to handle derived expressions such as cond , let, and so on (section 4.1.2 ). You may \u201ccheat\u201d and assume that the syntax transformers such as cond->if  are available as machine operations.28 Exer cise 5.24.   Implement cond  as a new basic special form without reducing it to if. You will have to construct a loop that tests the predicates of successive cond  clauses until you find one that is true, and then use ev- sequence  to evaluate the actions of the clause. Exer cise 5.25.   Modify the evaluator so that it uses normal-order evaluation, based on the lazy evaluator of section 4.2. 5.4.4  Running the Evaluator With the implementation of the explicit-control evaluator we come to the end of a development, begun in chapter 1, in which we"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61117184
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " have explored successively more precise models of the evaluation process. We started with the relatively informal substitution model, then extended this in chapter 3 to the environment model, which enabled us to deal with state and change. In the metacircular evaluator of chapter 4, we used Scheme itself as a language for making more explicit the environment structure constructed during evaluation of an expression. Now , with register machines, we have taken a close look at the evaluator's mechanisms for storage management, ar gument passing, and control. At each new level of description, we have had to raise issues and resolve ambiguities that were not apparent at the previous, less precise treatment of evaluation. To understand the behavior of the explicit- control evaluator , we can simulate it and monitor its performance. We will install a driver loop in our evaluator machine. This plays the role of the driver-loop  procedure of section 4.1.4 . The evaluator will repeatedly print a prompt, read an expres"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61118208s
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "sion, evaluate the expression by going toeval-dispatch , and print the result. The following instructions form the beginning of the explicit-control evaluator's controller sequence:29 read-eval-print-loop     (perform (op initialize-stack))     (perform      (op prompt-for-input) (const \";;; EC-Eval input:\"))     (assign exp (op read))     (assign env (op get-global-environment))     (assign continue (label print-result))     (goto (label eval-dispatch))   print-result     (perform      (op announce-output) (const \";;; EC-Eval value:\"))     (perform (op user-print) (reg val))     (goto (label read-eval-print-loop))   When we encounter an error in a procedure (such as the \u201cunknown procedure type error\u201d indicated at apply-dispatch ), we print an error message and return to the driver loop.30 unknown-expression-type     (assign val (const unknown-expression-type-error))     (goto (label signal-error))   unknown-procedure-type     (restore continue)    ; clean up stack (from apply-dispatch )    (assign val (const"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61119232
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " unknown-procedure-type-error))     (goto (label signal-error))   signal-error     (perform (op user-print) (reg val))     (goto (label read-eval-print-loop))   For the purposes of the simulation, we initialize the stack each time through the driver loop, since it might not be empty after an error (such as an undefined variable) interrupts an evaluation.31If we combine all the code fragments presented in sections 5.4.1 -5.4.4 , we can create an evaluator machine model that we can run using the register - machine simulator of section 5.2. (define eceval     (make-machine      '(exp env val proc argl continue unev)      eceval-operations     '(      read-eval-print-loop         <entire machine controller as given above >     )))  We must define Scheme procedures to simulate the operations used as primitives by the evaluator . These are the same procedures we used for the metacircular evaluator in section 4.1, together with the few additional ones defined in footnotes throughout section 5.4. (define eceval-opera"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61120256t
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "tions     (list (list 'self-evaluating? self-evaluating)           <complete list of operations for eceval machine> ))  Finally , we can initialize the global environment and run the evaluator: (define the-global-environment (setup-environment))     (start eceval)   ;;; EC-Eval input:   (define (append x y)     (if (null? x)         y        (cons (car x)               (append (cdr x) y))))   ;;; EC-Eval value:   ok  ;;; EC-Eval input:   (append '(a b c) '(d e f))  ;;; EC-Eval value:   (a b c d e f)   Of course, evaluating expressions in this way will take much longer than if we had directly typed them into Scheme, because of the multiple levels of simulation involved. Our expressions are evaluated by the explicit-control- evaluator machine, which is being simulated by a Scheme program, which is itself being evaluated by the Scheme interpreter . Monitoring the performance of the evaluator Simulation can be a powerful tool to guide the implementation of evaluators. Simulations make it easy not only to explore "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61121280v
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "variations of the register -machine design but also to monitor the performance of the simulated evaluator . For example, one important factor in performance is how ef ficiently the evaluator uses the stack. We can observe the number of stack operations required to evaluate various expressions by defining the evaluator register machine with the version of the simulator that collects statistics on stack use (section 5.2.4 ), and adding an instruction at the evaluator's print-result  entry point to print the statistics: print-result     (perform (op print-stack-statistics)) ; added instruction     (perform      (op announce-output) (const \";;; EC-Eval value:\"))     ... ; same as before   Interactions with the evaluator now look like this: ;;; EC-Eval input:   (define (factorial n)     (if (= n 1)         1        (* (factorial (- n 1)) n)))   (total-pushes = 3 maximum-depth = 3)   ;;; EC-Eval value:   ok  ;;; EC-Eval input:  (factorial 5)   (total-pushes = 144 maximum-depth = 28)   ;;; EC-Eval value:   120  Note"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61122304
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " that the driver loop of the evaluator reinitializes the stack at the start of each interaction, so that the statistics printed will refer only to stack operations used to evaluate the previous expression. Exer cise 5.26.   Use the monitored stack to explore the tail-recursive property of the evaluator (section 5.4.2 ). Start the evaluator and define the iterative factorial  procedure from section 1.2.1 : (define (factorial n)     (define (iter product counter)       (if (> counter n)           product           (iter (* counter product)                 (+ counter 1))))     (iter 1 1))   Run the procedure with some small values of n. Record the maximum stack depth and the number of pushes required to compute n! for each of these values. a. You will find that the maximum depth required to evaluate n! is independent of n. What is that depth? b. Determine from your data a formula in terms of n for the total number of push operations used in evaluating n! for any n > 1. Note that the number of operations used is "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61123328a
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "a linear function of n and is thus determined by two constants. Exer cise 5.27.   For comparison with exercise 5.26, explore the behavior of the following procedure for computing factorials recursively: (define (factorial n)     (if (= n 1)        1        (* (factorial (- n 1)) n)))   By running this procedure with the monitored stack, determine, as a function of n, the maximum depth of the stack and the total number of pushes used in evaluating n! for n > 1. (Again, these functions will be linear .) Summarize your experiments by filling in the following table with the appropriate expressions in terms of n: Maximum depth Number of pushes Recursive factorial Iterative factorial The maximum depth is a measure of the amount of space used by the evaluator in carrying out the computation, and the number of pushes correlates well with the time required. Exer cise 5.28.   Modify the definition of the evaluator by changing eval- sequence  as described in section 5.4.2  so that the evaluator is no longer tail- recurs"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61124352i
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ive. Rerun your experiments from exercises 5.26 and 5.27 to demonstrate that both versions of the factorial  procedure now require space that grows linearly with their input. Exer cise 5.29.   Monitor the stack operations in the tree-recursive Fibonacci computation: (define (fib n)     (if (< n 2)         n        (+ (fib (- n 1)) (fib (- n 2)))))   a. Give a formula in terms of n for the maximum depth of the stack required to compute Fib(n) for n > 2. Hint: In section 1.2.2  we ar gued that the space used by this process grows linearly with n.b. Give a formula for the total number of pushes used to compute Fib(n) for n > 2. You should find that the number of pushes (which correlates well with the time used) grows exponentially with n. Hint: Let S(n) be the number of pushes used in computing Fib(n). You should be able to ar gue that there is a formula that expresses S(n) in terms of S(n - 1), S(n - 2), and some fixed \u201coverhead\u201d constant k that is independent of n. Give the formula, and say what k is. Then sho"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61125376w
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "w that S(n) can be expressed as a Fib(n + 1) + b and give the values of a and b. Exer cise 5.30.   Our evaluator currently catches and signals only two kinds of errors \u2013 unknown expression types and unknown procedure types. Other errors will take us out of the evaluator read-eval-print loop. When we run the evaluator using the register -machine simulator , these errors are caught by the underlying Scheme system. This is analogous to the computer crashing when a user program makes an error .32 It is a lar ge project to make a real error system work, but it is well worth the ef fort to understand what is involved here. a. Errors that occur in the evaluation process, such as an attempt to access an unbound variable, could be caught by changing the lookup operation to make it return a distinguished condition code, which cannot be a possible value of any user variable. The evaluator can test for this condition code and then do what is necessary to go to signal-error . Find all of the places in the evaluator where "}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61126400s
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "such a change is necessary and fix them. This is lots of work. b. Much worse is the problem of handling errors that are signaled by applying primitive procedures, such as an attempt to divide by zero or an attempt to extract the car of a symbol. In a professionally written high- quality system, each primitive application is checked for safety as part of the primitive. For example, every call to car could first check that the argument is a pair . If the ar gument is not a pair , the application would return a distinguished condition code to the evaluator , which would then report the failure. We could arrange for this in our register -machine simulator by making each primitive procedure check for applicability and returning an appropriate distinguished condition code on failure. Then the primitive- apply  code in the evaluator can check for the condition code and go tosignal-error  if necessary . Build this structure and make it work. This is a major project. 19 See Batali et al. 1982 for more information on t"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61127424h
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "he chip and the method by which it was designed. 20 In our controller , the dispatch is written as a sequence of test  and branch  instructions. Alternatively , it could have been written in a data-directed style (and in a real system it probably would have been) to avoid the need to perform sequential tests and to facilitate the definition of new expression types. A machine designed to run Lisp would probably include a dispatch-on-type instruction that would ef ficiently execute such data-directed dispatches. 21 This is an important but subtle point in translating algorithms from a procedural language, such as Lisp, to a register -machine language. As an alternative to saving only what is needed, we could save all the registers (except val) before each recursive call. This is called a framed-stack  discipline. This would work but might save more registers than necessary; this could be an important consideration in a system where stack operations are expensive. Saving registers whose contents will not be need"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61128448e
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ed later may also hold onto useless data that could otherwise be garbage-collected, freeing space to be reused. 22 We add to the evaluator data-structure procedures in section 4.1.3  the following two procedures for manipulating ar gument lists: (define (empty-arglist) '())     (define (adjoin-arg arg arglist)     (append arglist (list arg)))   We also use an additional syntax procedure to test for the last operand in a combination: (define (last-operand? ops)     (null? (cdr ops)))   23 The optimization of treating the last operand specially is known as evlis tail r ecursion  (see Wand 1980). We could be somewhat more ef ficient in the ar gument evaluation loop if we made evaluation of the first operand a special case too. This would permit us to postpone initializing argl  until after evaluating the first operand, so as to avoid saving argl  in this case. The compiler in section 5.5 performs this optimization. (Compare the construct-arglist  procedure of section 5.5.3 .) 24 The order of operand evaluation i"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61129472n
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "n the metacircular evaluator is determined by the order of evaluation of the ar guments to cons  in the procedure list-of-values  of section 4.1.1  (see exercise 4.1). 25 We saw in section 5.1 how to implement such a process with a register machine that had no stack; the state of the process was stored in a fixed set of registers. 26 This implementation of tail recursion in ev-sequence  is one variety of a well-known optimization technique used by many compilers. In compiling a procedure that ends with a procedure call, one can replace the call by a jump to the called procedure's entry point. Building this strategy into theinterpreter , as we have done in this section, provides the optimization uniformly throughout the language. 27 We can define no-more-exps?  as follows: (define (no-more-exps? seq) (null? seq))   28 This isn't really cheating. In an actual implementation built from scratch, we would use our explicit-control evaluator to interpret a Scheme program that performs source-level transformations li"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61130496k
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ke cond->if  in a syntax phase that runs before execution. 29 We assume here that read  and the various printing operations are available as primitive machine operations, which is useful for our simulation, but completely unrealistic in practice. These are actually extremely complex operations. In practice, they would be implemented using low-level input-output operations such as transferring single characters to and from a device. To support the get-global-environment  operation we define (define the-global-environment (setup-environment))     (define (get-global-environment)     the-global-environment)   30 There are other errors that we would like the interpreter to handle, but these are not so simple. See exercise 5.30. 31 We could perform the stack initialization only after errors, but doing it in the driver loop will be convenient for monitoring the evaluator's performance, as described below . 32 Regrettably , this is the normal state of af fairs in conventional compiler -based language systems such as"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61131520
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " C. In UNIXTM the system \u201cdumps core,\u201d and in DOS/W indowsTM it becomes catatonic. The MacintoshTM displays a picture of an exploding bomb and of fers you the opportunity to reboot the computer \u2013 if you're lucky .5.5  Compilation The explicit-control evaluator of section 5.4 is a register machine whose controller interprets Scheme programs. In this section we will see how to run Scheme programs on a register machine whose controller is not a Scheme interpreter . The explicit-control evaluator machine is universal \u2013 it can carry out any computational process that can be described in Scheme. The evaluator's controller orchestrates the use of its data paths to perform the desired computation. Thus, the evaluator's data paths are universal: They are suf ficient to perform any computation we desire, given an appropriate controller .33 Commercial general-purpose computers are register machines or ganized around a collection of registers and operations that constitute an ef ficient and convenient universal set of da"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61132544t
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ta paths. The controller for a general-purpose machine is an interpreter for a register -machine language like the one we have been using. This language is called the native language  of the machine, or simply machine language . Programs written in machine language are sequences of instructions that use the machine's data paths. For example, the explicit-control evaluator's instruction sequence can be thought of as a machine-language program for a general-purpose computer rather than as the controller for a specialized interpreter machine. There are two common strategies for bridging the gap between higher -level languages and register -machine languages. The explicit-control evaluator illustrates the strategy of interpretation. An interpreter written in the native language of a machine configures the machine to execute programs written in a language (called the source language ) that may dif fer from the native language of the machine performing the evaluation. The primitive procedures of the source language"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61133568
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": " are implemented as a library of subroutines written in the native language of the given machine. A program to be interpreted (called the source program ) is represented as a data structure. The interpreter traverses this data structure, analyzing the source program. As it does so, it simulates the intended behavior of the source program by calling appropriate primitive subroutines from the library . In this section, we explore the alternative strategy of compilation . A compiler for a given source language and machine translates a source program into anequivalent program (called the object pr ogram ) written in the machine's native language. The compiler that we implement in this section translates programs written in Scheme into sequences of instructions to be executed using the explicit-control evaluator machine's data paths.34 Compared with interpretation, compilation can provide a great increase in the efficiency of program execution, as we will explain below in the overview of the compiler . On the othe"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61134592r
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "r hand, an interpreter provides a more powerful environment for interactive program development and debugging, because the source program being executed is available at run time to be examined and modified. In addition, because the entire library of primitives is present, new programs can be constructed and added to the system during debugging. In view of the complementary advantages of compilation and interpretation, modern program-development environments pursue a mixed strategy . Lisp interpreters are generally or ganized so that interpreted procedures and compiled procedures can call each other . This enables a programmer to compile those parts of a program that are assumed to be debugged, thus gaining the ef ficiency advantage of compilation, while retaining the interpretive mode of execution for those parts of the program that are in the flux of interactive development and debugging. In section 5.5.7 , after we have implemented the compiler , we will show how to interface it with our interpreter to prod"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61135616u
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "uce an integrated interpreter - compiler development system. An overview of the compiler Our compiler is much like our interpreter , both in its structure and in the function it performs. Accordingly , the mechanisms used by the compiler for analyzing expressions will be similar to those used by the interpreter . Moreover , to make it easy to interface compiled and interpreted code, we will design the compiler to generate code that obeys the same conventions of register usage as the interpreter: The environment will be kept in the env register , argument lists will be accumulated in argl , a procedure to be applied will be in proc , procedures will return their answers in val, and the location to which a procedure should return will be kept in continue . In general, the compiler translates a source program into an object program that performs essentially the same register operations as would the interpreter in evaluating the same source program. This description suggests a strategy for implementing a rudiment"}


created_at: 2024-03-15 06:19:17
topic: persistent://default/default/71054ee4-4471-4fe6-a521-830479038f85
id: batch61136640a
encoding: FLOAT32
metadata:
{"length": 1024, "chroma:document": "ary compiler: We traverse the expression in the same way the interpreter does. When weencounter a register instruction that the interpreter would perform in evaluating the expression, we do not execute the instruction but instead accumulate it into a sequence. The resulting sequence of instructions will be the object code. Observe the efficiency advantage of compilation over interpretation. Each time the interpreter evaluates an expression \u2013 for example, (f 84 96)  \u2013 it performs the work of classifying the expression (discovering that this is a procedure application) and testing for the end of the operand list (discovering that there are two operands). With a compiler , the expression is analyzed only once, when the instruction sequence is generated at compile time. The object code produced by the compiler contains only the instructions that evaluate the operator and the two operands, assemble the ar gument list, and apply the procedure (in proc ) to the arguments (in argl ). This is the same kind of optimiza"}


