This is loops.info, produced by makeinfo version 4.0 from /usr/local/src/BUILD/logo-mode/loops-guide.texi.  File: loops.info, Node: DIGITAL CLOCK, Next: QUEENS, Prev: ROUND TABLE, Up: SYMBOLIC COMPUTING Digital Clock ------------- We would like to write a program that simulates the display of a digital clock. The display should look like this: 00:00:00 The easiest way to think (in terms of objects) about this display, is to consider each digit an LED element (object). The two columns in between are irrelevant--they are just fixed decorations. Next step is to decide which class to base LED objects on? This is obvious, on rewinding counter class. When we have decided that, we have to determine the range of valid values for each LED. The minimum value for all is shown above--all zeros. The maximum, considering 24 hour clock is this: `29:59:59' . They come in three pairs, and for the time being(1), we can say that the right-hand LED ranges from 0 to 9, and the left-hand from 0 to 5, except ten-hour LED from 0 to 2. Each LED object should act independently, but to create a working clock object, they need some sort of glue. The glue, in this case will be the information which object to call next, after each LED has done its part of job. The LED, after it has incremented its counter, and displayed its value, must call the next LED. There are two posibilities for calling sequence. First, and most freequent is to call the seconds LED. The other is (if the counter is at zero) to call next LED on the left. This means that each LED must have two inputs indicating the names of two possible objects to call. The last bit of information needed, is display coordinates for each object. We intend to use `setcursor' to fix LEDs, so this input will be in the form of a list [xccor ycoor]. Now we are ready for `led.class': to led.class :next :previous :cursor.pos [:roll 9] static [[digit [(object.maker "rewinding.counter.class 0 :roll)]] [up :next] [first :previous] [position :cursor.pos]] lambda :msg setcursor :position casec [ [first bf :msg] [[increment] [type send "digit "inc] [wait 0] [if zero? send "digit "get [send "up "increment]] [wait 10] [send "first "increment]] [[type] [op "led.object]] [else [op delegate "base.object :msg]]] end STATIC line is straightforward; we create a rewinding counter object with the range from 0 to 9. `roll' is an optional variable. If not given, it will default to 9. In the body of object procedure(2), we first position the cursor. LED objects responds to only one message--INCREMENT. Method INCREMENT does the following: Increments its counter. Displays the counter using `type'. That's why we need the first `wait 0', to force line--buffer flush. If the count is at zero, it sends the message INCREMENT to the next object (up). If not, waits a little (`wait 10'), and sends the massage INCREMENT to the first object. What is left to do, is to write the DIGITAL.CLOCK class, that will create all necessary LED objects, and start the clock: to digital.clock.class :pos static [[x [first :pos]] [y [last :pos]]] static [[hr10 [(object.maker "led.class "sec1 "sec1 (list :x :y) 2)]] [hr1 [(object.maker "led.class "hr10 "sec1 (list :x+1 :y))]] [min10 [(object.maker "led.class "hr1 "sec1 (list :x+3 :y) 5)]] [min1 [(object.maker "led.class "min10 "sec1 (list :x+4 :y))]] [sec10 [(object.maker "led.class "min1 "sec1 (list :x+6 :y) 5)]] [sec1 [(object.maker "led.class "sec10 "sec1 (list :x+7 :y))]]] lambda :msg setcursor (list :x-1 :y) type "| 00:00:0 | casec [ [first bf :msg] [[start] [send "sec1 "increment]] [[type] [op "digital.clock.object]] [else [ct] [op delegate "base.object :msg]]] end We need two STATIC lines, one to create static variables X, and Y (coordinates), and the second to define all six LED objects with appropriate initial values. Two lines are a must, because the second line is using values x, and y from the first. The only message is START, which sends the `sec1' object message INCREMENT. Let's try our clock: define "clock (object.maker "digital.clock.class [30 15]) pr send "clock "start This will display the six LED elements, and start counting. The only way to stop the clock, is to hit C-c C-c. We can delete the CLOCK with `pr send "clock "erase.object'. If you waited a very long time (hope you did not), you would have noticed that our clock is not perfect. It will count all the way to `29:59:59', before restarting at `00:00:00'. Now is the time to fix this. What we need is two different HR1 objects. One to count from 0 to 9, and one from 0 to 3 (to reset at a value of `23:59:59'). This creates a problem in the calling sequence. If objects are to be truly independent, they should not be aware of the problem that `hr1' object has. The `min10' object (the only one that is calling `hr1'), should not know, and indeed, does not know which of the two versions of `hr1' to call. The solution to this is to create one `hr1' object with dual personality--actually consisting of two different objects. to hr.class :next :previous :cursor.pos :roll1 :roll2 static [[digit1 [(object.maker "rewinding.counter.class 0 :roll1)]] [digit2 [(object.maker "rewinding.counter.class 0 :roll2)]] [hr.switch [(object.maker "rewinding.counter.class 0 1)]]] static [[digit [(object.maker "swap.pair.class "digit1 "digit2)]] [up :next] [first :previous] [position :cursor.pos]] lambda :msg setcursor :position casec [ [first bf :msg] [[increment] [type send send "digit "get "inc] [wait 0] [ifelse zero? send send "digit "get "get [ignore (and [zero? send "hr.switch "inc] [send "digit "swap] [equal? send "digit "get "digit2] [send "hr.switch "inc]) send "up "increment] [send "first "increment]]] [[type] [op "hr.object]] [else [op delegate "base.object :msg]]] end The first STATIC line creates two objects, with different roll-over limits, and switch object (a counter) that will aid in deciding which of the two to use. The second STATIC line creates jet another object (based on the `swap.pair.class', that will hold both HR1 objects (DIGIT1, and DIGIT2). The body of the HR.CLASS object has (compared to ordinary LED objects) become complicated. First of all, to use HR object we need `indirect addressing'. We cannot simply send message INC to the DIGIT object, because this is only a pair class object, and does not know how to INC anything. What we need to do, is to send message INC to the object that is stored in the first part of the DIGIT object. Tht's why the first line of the INCREMENT method uses nested SEND: [type send send "digit "get "inc] It would be easier to understand if it was written with parentheses, but they are not necessary. [type send (send "digit "get) "inc] The inner SEND retrieves the object (in the beginning DIGIT1) from the DIGIT object, and then sends this object the message INC. Likewise, the ifelse line uses the same technique, to get the value of the counter from DIGIT1 object. `and' is used here as a control structure, that performs series of tests on the SWITCH object, and DIGIT object, to decide whether to swap the DIGIT1, and DIGIT2 objects or not. The digital clock class has to be adjusted as well: to digital.clock.class :pos static [[x [first :pos]] [y [last :pos]]] static [[hr10 [(object.maker "led.class "sec1 "sec1 (list :x :y) 2)]] [hr1 [(object.maker "hr.class "hr10 "sec1 (list :x+1 :y) 9 3)]] [min10 [(object.maker "led.class "hr1 "sec1 (list :x+3 :y) 5)]] [min1 [(object.maker "led.class "min10 "sec1 (list :x+4 :y))]] [sec10 [(object.maker "led.class "min1 "sec1 (list :x+6 :y) 5)]] [sec1 [(object.maker "led.class "sec10 "sec1 (list :x+7 :y))]]] lambda :msg setcursor (list :x-1 :y) type "| 00:00:0 | casec [ [first bf :msg] [[start] [send "sec1 "increment]] [[type] [op "digital.clock.object]] [else [ct] [op delegate "base.object :msg]]] end We can try it the same way as before: define "clock (object.maker "digital.clock.class [30 15]) pr send "clock "start This one will work properly, however, instead of making programming with objects easier, this solution has made it more difficult. We can try to fix this using differnt approach, but first let's fix the obvious: indirect addressing problem. exercises ......... `a' Write a new definition of the HR.CLASS, that does not use nested `send'. Hint: use the procedure `indirect'. `b' Replace the `and' control structure with `if' Better strategy for solving the dual HR1 object problem would be to use the circular, or double.end class as a storage for different types of HR1 object. This way, we can completely avoid the complicated logic of switching: to hr.class :next :previous :cursor.pos :roll1 :roll2 static [[digit1 [(object.maker "rewinding.counter.class 0 :roll1)]] [digit2 [(object.maker "rewinding.counter.class 0 :roll2)]]] static [[digit [(object.maker "double.end.class [digit1 digit1 digit2])]] [up :next] [first :previous] [position :cursor.pos]] lambda :msg setcursor :position casec [ [first bf :msg] [[increment] [type indirect "digit "inc] [wait 0] [ifelse zero? indirect "digit "get [ignore send "digit "shift.left send "up "increment] [send "first "increment]]] [[type] [op "hr.object]] [else [op delegate "base.object :msg]]] end In the second STATIC line, we create the DIGIT object based on the double.end class, and initialize it with the sequence of HR1 object names [digit1 digit1 digit2]. This sequence has objects aligned in the proper order. We use DIGIT1 (from 0 to 9) twice, then DIGIT2 (from 0 to 3) once. Whenever object's counter hits zero, we SHIFT.LEFT the DIGIT object, therefore bringing the new HR1 object to the front. The sequence of DIGIT1/2 objects in the DIGIT object looks like this: [digit1 digit1 digit2] [digit1 digit2 digit1] [digit2 digit1 digit1] [digit1 digit1 digit2] ... ---------- Footnotes ---------- (1) The maximum value for hour LED is not always correct. (2) Note that we use `casec' control structure, and not `case'. LED object, does not return any values during its normal work, but values do have to be returned anyway--at least for the TYPE message, and for delegated messages (mostly ERASE.OBJECT). Therefore CASEC is used, as it appends `output "#unspecified' to all true forms. For those TRUE forms that actually do return values, you have to use explicit `output'--as in TYPE, and DELEGATE.  File: loops.info, Node: QUEENS, Prev: DIGITAL CLOCK, Up: SYMBOLIC COMPUTING Queens ------ This is a very popular problem, and has been used in many books to explain backtracking. The functional(1) solution has already been presented in this manual.*Note LOCAL PROCEDURES::. Now, let's see if we can make it simpler using objects. Problem description: We have a chessboard, and eight queens. The intention is to place all of queens onto the board in such a way, that none of the queens is immediately threatening others. This is usually solved placing queens one by one onto the board, on the first available position (each in its own column), and performing a check (before the queen is placed) whether it attacks any of the previously placed queens. If it does not, it is placed on that position. If it does, we try to place it on the next row (in the same column). Sooner or later, we will not be able to find the position for the next queen, which means that one (or more) of previously placed queens will have to be picked up, and placed on the next available row in their respective columns. This is the backtracking part. If you read the previous paragraph one more time, you will notice words like; we try to place it, we will not be able to find the position, previously placed queens will have to be picked up... It sounds as if somebody is doing something with the queens, and queens themselves are just decoration. This *we*, is actually a controlling procedure (`put.queen' in this manual), that has the complete overview of the chessboard, and all the queens. In this section we will try a different approach. We will regard each queen as an independent object, that knows what to do with itself. That is all queens will know how to place themselves on the board, and how to remove themselves from the board if need be. They will all need to have access to the chessboard, for checking if the intended next position is valid, and for inserting or removing themselves on/from the board. As with the digital clock, each queen will need some sort of glue. This glue will be the knowledge which queen to call after the present queen has successfully placed itself onto the board, and which queen to call if this queen failed to find place for itself(2). As you can see, there is no controlling procedure here--all queen objects are of equal status. To be able to terminate the program, we will use two border objects, one as the next object after the eighth queen (which will indicate success, as all eight queens have been placed), and one before the first queen (which will indicate total failure, as the backtracking has been carried right off the board). The queens will have to respond to two messages; PUT, and REMOVE. That is, if one queen has placed itself successfully onto the board, it will send the next queen message PUT, and if it failed, it will send the previous queen the message REMOVE. The only private information that each queen needs is the row of their current position. As rows range from 1 to 8, it is obvious that the easiest storage class to base the queen object is rewinding.counter.class . The chessboard object will have to hold up to 8 numbers, representing the columns that each queen occupies. Let's take a look at how queens place themselves onto the board. If a queen needs to place itself, it will push the number of its current column on the board, and then the next will do the same thing, and the next ... If a queen needs to remove itself, it will be always the last queen that has placed itself, so we end up with a FIFO(3) structure. In LOOPS base classes we have a ready made class for this; the STACK.CLASS. Now, we are ready to define the `queen.class': to queen.class :prev.q [:next.q []] static [[column [(object.maker "rewinding.counter.class 0 8)]] [previous :prev.q] [next :next.q]] lambda :message local [self msg tmp] make "self first :message make "msg bf :message case [ [first :msg] [[put] [make "tmp send "column "inc] [if zero? :tmp [op send "previous "remove]] [if (legal? send "board "get) [ignore (send "board "push :tmp) op send "next "put]] [op send "self "put]] [[remove] [ignore send "board "pop] [op send "self "put]] [[reset] [op send "column "reset]] [[type] [op "queen.object]] [else [op delegate "base.object :message]]] end The STATIC line is very simple; we create the COLUMN, a rewinding counter object that ranges from 0 to 8. One to eight being the available rows in the designated queen's column, and 0 being the signal, that the queen failed to find a place for itself. The QUEEN OBJECT responds to these messages: `put' Increments the column, and stores that number in TMP variable. If column is zero (failure), sends the previous queen the message REMOVE. If not, checks if the column number would be a legal position (with respect to the queens already placed). If true, pushes this number onto the stack (chessboard), and sends the message PUT to the next queen. If false, sends itself (object self) message PUT, which will repeat the same process. `remove' Pops its current position from the stack (chessboard), and sends itself (object self) message PUT. `reset' Resets the internal counter to 0 There is one novel concept here--the object SELF, used to carry out recursive calls. The object SELF is just a local variable, initialized every time the queen object is called, with the queen's name. Remember that each call looks like this: `send "queen.name "message'. This `queen.name' becomes the value of variable SELF. This is one of the reasons we need the procedure `send'. *Note SENDING MESSAGES::. The variable BOARD used in the queen object is the local variable created in the `queens8.class', that we have to write next. Logo's dynamic scope is used in queen object to access (and change) its value. QUEENS8 object will be the object responsible for creating all eight queen objects, the chessboard object, and for starting the position search process. to queens8.class static [[board [(object.maker "stack.class)]] [counter [(object.maker "counter.class 0)]]] static [[start.o [(object.maker "border.class [No further positions!] [self] [exit.outer])]] [end.o [(object.maker "border.class [Position] [counter board] [inc get])]]] static [[q1 [(object.maker "queen.class "start.o "q2)]] [q2 [(object.maker "queen.class "q1 "q3)]] [q3 [(object.maker "queen.class "q2 "q4)]] [q4 [(object.maker "queen.class "q3 "q5)]] [q5 [(object.maker "queen.class "q4 "q6)]] [q6 [(object.maker "queen.class "q5 "q7)]] [q7 [(object.maker "queen.class "q6 "q8)]] [q8 [(object.maker "queen.class "q7 "end.o)]]] lambda :msg case [ [first bf :msg] [[next] [op ifelse send "board "empty? [send "q1 "put] [send "q8 "remove]]] [[reset] [foreach [q1 q2 q3 q4 q5 q6 q7 q8] [[q] [ignore send "q "reset]]] [ignore send "board "flush] [op send "counter "reset]] [[type] [op "queens8.object]] [else [op delegate "base.object :msg]]] end The first STATIC line creates BOARD (stack), and a COUNTER objects. The counter will count found positions (there are totally 92 for eight queens). The second STATIC line will create two objects (start.o, and end.o) of the border.class, whose purpose is to stop the execution if the eighth queen managed to place itself (end.o), or if the first queen tried to backtrack (start.o). The third STATIC line creates the eight queen objects. Each queen is initialized with its previous, and next queens. In case of Q1 previous is START.O, and for Q8 next is END.O. The order of static lines is unimportant--they could actually be written as one line only. Note that queens do not use as initializing values objects themselves, but their names only. For the queen to be initialized with the value of the object, that object had to be created in one of the preceding STATIC lines. There is no way to do this for queen objects, even if we use separate static line for each queen. To create Q1, we need Q2, and to create Q2, we need Q1--a dead end! Using names of objects (ordinary Logo words), on the other hand is OK. That is why we have the procedure SEND, to dereference the name until it gets to the object, but that happens during run-time, when the object does exist, and not during compile-time. The QUEENS8 OBJECT responds to following messages: `next' If the board is empty (we are just starting) send Q1 the message PUT. If not, (this will not be the first position) send Q8 the message REMOVE. This will backtrack to find the next position. `reset' Sends the message RESET to all the queens, flushes the board, and resets the position counter to 0. It is obvious that the run-time part of QUEENS8 object is trivial, however, one of its STATIC lines is not. We have not jet fully explained the second line, one using the BORDER.CLASS. The END.O is an instance of the BORDER.CLASS, and takes three initial values. First is the text message [Position], which will be displayed when the object is called. The second is a list of objects [counter board], and the third, a list of messages [inc get]. What happens, when the object END.O is invoked(4), is that it first types the text from the first input, and then starts sending the objects from the second list, the messages from the third. Obviously, number of objects, and messages must match. The start object will be explained later, when we write the driver procedure. To test our queens, we need one more procedure, legal? : to legal? :plist [:up :tmp+1] [:down :tmp-1] 3 if.tf emptyp :plist [op "true] if.tf memberp first :plist (list :tmp :up :down) [op "false] op legal? bf :plist :up+1 :down-1 end Note that the `legal?' contains free variable TMP. We are counting on Logo's dynamic scope again. This does not have to be so, but since the procedure `legal?' cannot be used by anything else but an queen object, we let it go at that. Now, we can test queens: define "queens (object.maker "queens8.class) pr send "queens "next Position 1 [4 2 7 3 6 8 5 1] pr send "queens "next Position 2 [5 2 4 7 3 8 6 1] pr send "queens "next Position 3 [3 5 2 8 6 4 7 1] pr send "queens "reset 0 pr send "queens "erase.object 22707 127950 This will be very slow, more that ten times slower than the functional solution. If we want to automate finding of all solutions, we need a driver procedure: to all.queens local "result if not definedp "queens8 [define "queens8 (object.maker "queens8.class)] while [make "result send "queens8 "next last :result] ~ [pr :result] ignore send "queens8 "reset pr bl :result end Now would be a good time to explain the START.O, from the second static line. It is initialized with the text [No further positions!], a list of objects [self], and a list of messages [exit.outer]. When the START.O receives message REMOVE, from the Q1, that is trying to backtrack, it will (first ignore the REMOVE message, and send itself (object SELF) the message EXIT.OUTER, that will output `false'. This `false' will be caught by `while' in the driver procedure, and will exit the loop, displaying the text message; No further positions!. all.queens Position 1 [4 2 7 3 6 8 5 1] Position 2 [5 2 4 7 3 8 6 1] ... Position 90 [6 4 7 1 3 5 2 8] Position 91 [4 7 5 2 6 1 3 8] Position 92 [5 7 2 6 3 1 4 8] No further positions! We have defined two global procedures for the queens program; legal? and all.queens. It sounds logical to have the global driver procedure, but let's examine the other. `legal?' is, and can be used only by the queen objects, which are not global, but virtual objects. It is obvious that `legal?' should become an local procedure. LOOPS provides two ways to do this. One is obvious, and is mentioned many times before; each queen can have its own local procedure `legal?', defined using LETREC. However this would be a waste of memory. So here is the other way. The QUEENS8.CLASS can define and export a method LEGAL?. What this means is that `legal?' is defined only once, but can be used by all the objects, it was exported to (actually, all the classes). to queens8.class static [[board [(object.maker "stack.class)]] [counter [(object.maker "counter.class 0)]] [legal? [method.export [queen.class] [ [plist [up :tmp + 1] [down :tmp - 1] 3] [if.tf emptyp :plist [op "true]] [if.tf memberp first :plist (list :tmp :up :down) [op "false]] [op (legal? bf :plist :up+1 :down-1)]]]]] static [[start.o [(object.maker "border.class [No further positions!] [self] [exit.outer])]] [end.o [(object.maker "border.class [Position] [counter board] [inc get])]]] static [[q1 [(object.maker "queen.class "start.o "q2)]] [q2 [(object.maker "queen.class "q1 "q3)]] [q3 [(object.maker "queen.class "q2 "q4)]] [q4 [(object.maker "queen.class "q3 "q5)]] [q5 [(object.maker "queen.class "q4 "q6)]] [q6 [(object.maker "queen.class "q5 "q7)]] [q7 [(object.maker "queen.class "q6 "q8)]] [q8 [(object.maker "queen.class "q7 "end.o)]]] lambda :msg case [ [first bf :msg] [[next] [op ifelse send "board "empty? [send "q1 "put] [send "q8 "remove]]] [[reset] [foreach [q1 q2 q3 q4 q5 q6 q7 q8] [[q] [ignore send "q "reset]]] [ignore send "board "flush] [op send "counter "reset]] [[type] [op "queen8.object]] [else [op delegate "base.object :msg]]] end Exported methods are defined inside the STATIC line, but have a different syntax: static [method.name [method.export [list of classes to export to] [ text of the procedure ...]]] This looks similar to the `letrec' definition, but is preceded by the keyword METHOD.EXPORT, and a list of classes the method will be exported to. The invocation of the exported method LEGAL? requires the same rules as the LETREC. Invocation must be enclosed in parentheses. However the important difference is that if you define several methods in one class, they cannot be mutually recursive. There is no need to alter the QUEEN.CLASS to work with the method LEGAL?, as the invocation was enclosed in parenthesis before. Method LEGAL? can be used only in queen objects, as it is exported only to the queen class. Note that the LEGAL? in the form of exported method, uses the dynamic scope, just like the global procedure. Exercises ......... Write the version of queen class that uses letrec instead of global procedure, or exported method. ---------- Footnotes ---------- (1) It is not purely functional, because it uses the stack to store all solutions. (2) this is the backtracking part. (3) First In First Out (4) This happens when Q8 sends the message PUT to the next queen, which is END.O  File: loops.info, Node: ANSWERS, Next: LOOPS CODE, Prev: TUTORIAL, Up: Top ANSWERS ******* * Menu: * ROUND TABLE ANSWERS:: * CLOCK ANSWERS:: * QUEEN ANSWERS::  File: loops.info, Node: ROUND TABLE ANSWERS, Next: CLOCK ANSWERS, Prev: ANSWERS, Up: ANSWERS Round Table ----------- ;; a to round.table.class static [[circle [(object.maker "circular.class)]]] lambda :msg op case [ [first bf :msg] [[last?] [(send "circle "count) = 1]] [[skip] [ignore (send "circle "sety send "circle "first)] [ignore send "circle "rotate] [send "circle "gety]] [[execute output] [ignore (send "circle "sety send "circle "first)] [ignore send "circle "bf] [send "circle "gety]] [[initialize] [(send "circle "import map "identity first bf bf :msg)]] [[type] ["knights.object]] [else [delegate "base.object :msg]]] end ;; b The butfirst part of the START.O will be used for storing temporary values. If you check the `circular.class' code, you will se that the messages unknown to the circular class objects are delegated to the START.O object. In our exercise, the unknown messages are SETY, and GETY.  File: loops.info, Node: CLOCK ANSWERS, Next: QUEEN ANSWERS, Prev: ROUND TABLE ANSWERS, Up: ANSWERS Clock ----- ;; a to hr.class :next :previous :cursor.pos :roll1 :roll2 static [[digit1 [(object.maker "rewinding.counter.class 0 :roll1)]] [digit2 [(object.maker "rewinding.counter.class 0 :roll2)]] [hr.switch [(object.maker "rewinding.counter.class 0 1)]]] static [[digit [(object.maker "swap.pair.class "digit1 "digit2)]] [up :next] [first :previous] [position :cursor.pos]] lambda :msg setcursor :position casec [ [first bf :msg] [[increment] [type indirect "digit "inc] [wait 0] [ifelse zero? indirect "digit "get [ignore (and [zero? send "hr.switch "inc] [send "digit "swap] [equal? send "digit "get "digit2] [send "hr.switch "inc]) send "up "increment] [send "first "increment]]] [[type] [op "hr.object]] [else [op delegate "base.object :msg]]] end ;; b to hr.class :next :previous :cursor.pos :roll1 :roll2 static [[digit1 [(object.maker "rewinding.counter.class 0 :roll1)]] [digit2 [(object.maker "rewinding.counter.class 0 :roll2)]] [hr.switch [(object.maker "rewinding.counter.class 0 1)]]] static [[digit [(object.maker "swap.pair.class "digit1 "digit2)]] [up :next] [first :previous] [position :cursor.pos]] lambda :msg setcursor :position casec [ [first bf :msg] [[increment] [type indirect "digit "inc] [wait 0] [ifelse zero? indirect "digit "get [if (or [equal? send "digit "get "digit2] [zero? send "hr.switch "inc]) [ignore send "digit "swap] send "up "increment] [send "first "increment]]] [[type] [op "digital.clock.object]] [else [op delegate "base.object :msg]]] end  File: loops.info, Node: QUEEN ANSWERS, Prev: CLOCK ANSWERS, Up: ANSWERS Queeens ------- to queen.class :prev.q [:next.q []] static [[column [(object.maker "rewinding.counter.class 0 8)]] [previous :prev.q] [next :next.q]] lambda :message local [self msg tmp] make "self first :message make "msg bf :message op letrec [[ [legal? [[plist [up :tmp + 1] [down :tmp - 1] 3] [if.tf emptyp :plist [op "true]] [if.tf memberp first :plist (list :tmp :up :down) [op "false]] [op (legal? bf :plist :up+1 :down-1)]]]] [case [ [first :msg] [[put] [make "tmp send "column "inc] [if zero? :tmp [op send "previous "remove]] [if (legal? send "board "get) [ignore (send "board "push :tmp) op send "next "put]] [op send "self "put]] [[remove] [ignore send "board "pop] [op send "self "put]] [[reset] [op send "column "reset]] [[type] [op "queen.object]] [else [delegate "base.object :message]]]]] end