📄 tut.html
字号:
<ol><li>A Class <ul> <li>Has a name (interned symbol) <li>Has method definitions and superclass(es) in the value cell <li>May have class variables (attributes) in the property list </ul><li>An Object <ul> <li>Has no name (anonymous symbol) or is an external symbol <li>Has class(es) and (optionally method definitions) and in the value cell <li>Has instance variables (attributes) in the property list </ul></ol><p>So the main difference between classes and objects is that the former onesusually are internal symbols. By convention, their names start with a'<code>+</code>'. Sometimes it makes sense, however, to create named objects (asglobal singletons, for example), or even anonymous classes.<p>Both classes and objects have a list in their value cell, consisting ofmethod definitions (often empty for objects) and (super)class(es). And bothclasses and objects have local data in their property lists (often empty forclasses). This implies, that any given object (as an instance of a class) mayhave private (object-local) methods defined.<p>It is rather difficult to contrive a simple OOP example. We constructed ahierarchy of geometric shapes, with a base class <code>+Shape</code> and twosubclasses <code>+Rectangle</code> and <code>+Circle</code>.<p>The source code is included as "<code><ahref="shape.l">doc/shape.l</a></code>" in the Pico Lisp distribution, so youdon't have to type it in. Just <code><a href="refL.html#load">load</a></code>the file, or start it from the shell as:<p><pre><code>$ ./p dbg.l doc/shape.l</code></pre><p>Let's look at it piece by piece. Here's the base class:<p><pre><code>(class +Shape)# x y(dm T (X Y) (=: x X) (=: y Y) )(dm move> (DX DY) (inc (:: x) DX) (inc (:: y) DY) )</code></pre><p>The first line '<code>(class +Shape)</code>' defines the symbol<code>+Shape</code> as a class without superclasses. The following methoddefinitions will go to that class.<p>The comment '<code># x y</code>' in the second line is just a convention, toindicate what instance variables (properties) that class uses. As Pico Lisp is adynamic language, a class can be extended at runtime with any number ofproperties, and there is nothing like a fixed object size or structure. Thiscomment is a hint of what the programmer thinks to be essential and typical forthat class. In the case of <code>+Shape</code>, <code>x</code> and<code>y</code> are the coordinates of the shape's origin.<p>Then we have two method definitions, using the keyword <code><ahref="refD.html#dm">dm</a></code> for "define method". The first method isspecial, in that its name is <code>T</code>. Each time a new object is created,and a method with that name is found in its class hierarchy, that method will beexecuted. Though this looks like a "constructor" in other programming languages,it should probably better be called "initializer". The <code>T</code> method of<code>+Shape</code> takes two arguments <code>X</code> and <code>Y</code>, andstores them in the object's property list.<p>The second method <code>move></code> changes the object's origin by addingthe offset values <code>DX</code> and <code>DY</code> to the object's origin.<p>Now to the first derived class:<p><pre><code>(class +Rectangle +Shape)# dx dy(dm T (X Y DX DY) (super X Y) (=: dx DX) (=: dy DY) )(dm area> () (* (: dx) (: dy)) )(dm perimeter> () (* 2 (+ (: dx) (: dy))) )(dm draw> () (drawRect (: x) (: y) (: dx) (: dy)) )</code></pre><p><code>+Rectangle</code> is defined as a subclass of <code>+Shape</code>.The comment '<code># dx dy</code>' indicates that <code>+Rectangle</code> has awidth and a height in addition to the origin coordinates inherited from<code>+Shape</code>.<p>The <code>T</code> method passes the origin coordinates <code>X</code> and<code>Y</code> to the <code>T</code> method of the superclass(<code>+Shape</code>), then stores the width and height parameters into<code>dx</code> and <code>dy</code>.<p>Next we define the methods <code>area></code> and<code>perimeter></code> which do some obvious calculations, and a method<code>draw></code> which is supposed to draw the shape on the screen bycalling some hypothetical function <code>drawRect</code>.<p>Finally, we define a <code>+Circle</code> class in an analog way, postulatingthe hypothetical function <code>drawCircle</code>:<p><pre><code>(class +Circle +Shape)# r(dm T (X Y R) (super X Y) (=: r R) )(dm area> () (*/ (: r) (: r) 31415927 10000000) )(dm perimeter> () (*/ 2 (: r) 31415927 10000000) )(dm draw> () (drawCircle (: x) (: y) (: r)) )</code></pre><p>Now we can experiment with geometrical shapes. We create a rectangle at point(0,0) with a width of 30 and a height of 20, and keep it in the variable<code>R</code>:<p><pre><code>: (setq R (new '(+Rectangle) 0 0 30 20)) # New rectangle-> $134432824 # returned anonymous symbol: (show R)$134432824 (+Rectangle) # Show the rectangle dy 20 dx 30 y 0 x 0</code></pre><p>We see that the symbol <code>$134432824</code> has a list of classes'<code>(+Rectangle)</code>' in its value cell, and the coordinates, width andheight in is property list.<p>Sending messages to that object<p><pre><code>: (area> R) # Calculate area-> 600: (perimeter> R) # and perimeter-> 100</code></pre><p>will return the values for area and perimeter, respectively.<p>Then we move the object's origin:<p><pre><code>: (move> R 10 5) # Move 10 right and 5 down-> 5: (show R)$134432824 (+Rectangle) y 5 # Origin changed (0,0) -> (10,5) x 10 dy 20 dx 30</code></pre><p>Though a method <code>move></code> wasn't defined for the<code>+Rectangle</code> class, it is inherited from the <code>+Shape</code>superclass.<p>Similarly, we create and use a circle object:<p><pre><code>: (setq C (new '(+Circle) 10 10 30)) # New circle-> $134432607 # returned anonymous symbol: (show C)$134432607 (+Circle) # Show the circle r 30 y 10 x 10-> $134432607: (area> C) # Calculate area-> 2827: (perimeter> C) # and perimeter-> 188: (move> C 10 5) # Move 10 right and 5 down-> 15: (show C)$134432607 (+Circle) # Origin changed (10,10) -> (20,15) y 15 x 20 r 30</code></pre><p>It is also easy to send messages to objects in a list:<p><pre><code>: (mapcar 'area> (list R C)) # Get list of areas-> (600 2827): (mapc '((Shape) (move> Shape 10 10)) # Move all 10 right and down (list R C) )-> 25: (show R)$134431493 (+Rectangle) y 15 x 20 dy 20 dx 30-> $134431493: (show C)$134431523 (+Circle) y 25 x 30 r 30</code></pre><p>Assume that we want to extend our shape system. From time to time, we needshapes that behave exactly like the ones above, but are tied to a fixedposition. That is, they do not change their position even if they receive a<code>move></code> message.<p>One solution would be to modify the <code>move></code> method in the<code>+Shape</code> class to a no-operation. But this would require to duplicatethe whole shape hierarchy (e.g. by defining <code>+FixedShape</code>,<code>+FixedRectangle</code> and <code>+FixedCircle</code> classes).<p>The Pico Lisp Way is the use of <u>Prefix Classes</u> through multipleinheritance. It uses the fact that searching for method definitions is adepth-first, left-to-right search of the class tree. We define a prefix class:<p><pre><code>: (class +Fixed)(dm move> (DX DY)) # A do-nothing method</code></pre><p>We can now create a fixed rectangle, and try to move it:<p><pre><code>: (setq R (new '(+Fixed +Rectangle) 0 0 30 20)) # '+Fixed' prefix class-> $134432881: (move> R 10 5) # Send 'move>' message-> NIL: (show R)$134432881 (+Fixed +Rectangle) dy 20 dx 30 y 0 # Did not move! x 0</code></pre><p>We see, prefix classes can surgically change the inheritance tree forselected objects or classes.<p>Alternatively, if fixed rectangles are needed often, it might make sense todefine a new class <code>+FixRect</code>:<p><pre><code>: (class +FixRect +Fixed +Rectangle)-> +FixRect</code></pre><p>and then use it directly:<p><pre><code>: (setq R (new '(+FixRect) 0 0 30 20))-> $13455710</code></pre><p><hr><h2><a name="ext">Persistence (External Symbols)</a></h2><p>Pico Lisp has persistent objects built-in as a first class data type. With"first class" we mean not just the ability of being passed around, or returnedfrom functions (that's a matter of course), but that they are a primary datatype with their own interpreter tag bits. They are, in fact, a special type ofsymbolic atoms (called "<a href="ref.html#external">External Symbols</a>"), thathappen to be read from a pool file when accessed, and written back automaticallywhen modified.<p>In all other aspects they are normal symbols. They have a value cell, aproperty list and a name.<p>The name cannot be directly controlled by the programmer, as it is assignedwhen the symbol is created. It is an encoded index of the symbol's location inthe pool file ("database"). In its visual representation (output by the <code><ahref="refP.html#print">print</a></code> functions and input by the <code><ahref="refR.html#read">read</a></code> functions) it is surrounded by braces.<p>To make use of external symbols, you need to open a database file first:<p><pre><code>: (pool "test.db")</code></pre><p>If a file with that name did not exist, it got created now. Also created atthe same moment was <code>{1}</code>, the very first symbol in the file. Thissymbol is of great importance, and is handled especially by Pico Lisp. Thereforea global constant <code><a href="refD.html#*DB">*DB</a></code> exists, whichpoints to that symbol <code>{1}</code>, which should be used exclusively toaccess the symbol <code>{1}</code>, and which should never be modified by theprogrammer.<p><pre><code>: *DB # The value of '*DB'-> {1} # is '{1}': (show *DB){1} NIL # Value of '{1}' is NIL, property list empty</code></pre><p>Now let's put something into the value cell and property list of<code>{1}</code>.<p><pre><code>: (set *DB "Hello world") # Set value of '{1}' to a transient symbol (string)-> "Hello world": (put *DB 'a 1) # Property 'a' to 1-> 1: (put *DB 'b 2) # Property 'b' to 2-> 2: (show *DB) # Now show the symbol '{1}'{1} "Hello world" b 2 a 1</code></pre><p>Note that instead of '<code>(set *DB "Hello world")</code>', we mightalso have written '<code>(setq {1} "Hello world")</code>', and instead of'<code>(put *DB 'a 1)</code>' we might have written '<code>(put '{1} 'a1)</code>'. This would have the same effect, but as a rule external symbolsshould never be be accessed literally in application programs, because thegarbage collector might not be able to free these symbols and all symbolsconnected to them (and that might well be the whole database). It is all right,however, to access external symbols literally during interactive debugging.<p>Now we can create our first own external symbol. This can be done with<code><a href="refN.html#new">new</a></code> when a <code>T</code> argument issupplied:<p><pre><code>: (new T)-> {2} # Got a new symbol</code></pre><p>We store it in the database root <code>{1}</code>:<p><pre><code>: (put *DB 'newSym '{2}) # Literal '{2}' (ok during debugging)-> {2}: (show *DB){1} "Hello world" newSym {2} # '{2}' is now stored in '{1}' b 2 a 1</code></pre><p>Put some property value into '{2}'<p><pre><code>: (put *DB 'newSym 'x 777) # Put 777 as 'x'-property of '{2}'-> 777: (show *DB 'newSym) # Show '{2}' (indirectly){2} NIL x 777-> {2}: (show '{2}) # Show '{2}' (directly){2} NIL x 777</code></pre><p>All modifications to - and creations of - external symbols done so far arenot written to the database yet. We could call <code><ahref="refR.html#rollback">rollback</a></code> (or simply exit Pico Lisp) to undoall the changes. But as we want to keep them:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -