patternMinor
simple "if"-less algebraic computation using CLOS
Viewed 0 times
simplecomputationcloslessusingalgebraic
Problem
For educational purposes I've tried to implement a simple algebraic OOP example using CLOS. The functionality is as far as I can say as it is supposed to be. The intended approach was to implement a simple example of ideomatic OOP polymorphism. For a greater learning experience I've wanted to show the code to get suggestions, critic and so on.
```
;; main node class, most general
(defclass node ()
;; abstract class for nodes
((value :initarg :value :reader value)))
(defgeneric evaluate (node)
(:documentation "the standard evaluation"))
;; node containing an operation, more specific
(defclass op-node (node)
;; abstract class for a node containing an operation
((left-node :initform nil :initarg :left-node :reader left-node)
(right-node :initform nil :initarg :right-node :reader right-node)))
(defmethod evaluate ((node op-node))
(apply (value node)
(list (evaluate (left-node node))
(evaluate (right-node node)))))
;; concrete nodes
(defclass add-node (op-node)
;; class for addition
((value :initform #'+ :reader value)))
(defclass minus-node (op-node)
;; class for subtraction
((value :initform #'- :reader value)))
(defclass multi-node (op-node)
;; class for multiplication
((value :initform #'* :reader value)))
(defclass value-node (node)
;; class for a node containing a numerical value
((value :initarg :value :reader value)))
(defmethod evaluate (value-node)
(value value-node))
;; 1 + 3
(setf simple-add (make-instance 'add-node
:left-node (make-instance 'value-node :value 1)
:right-node (make-instance 'value-node :value 3)))
;; 1 * 10
(setf simple-multi (make-instance 'multi-node
:left-node (make-instance 'value-node :value 1)
:right-node (make-instance 'value-node :value 10)))
;; (1 + 3) + (1 * 10)
(setf advanced-add (make-instance 'add-node
```
;; main node class, most general
(defclass node ()
;; abstract class for nodes
((value :initarg :value :reader value)))
(defgeneric evaluate (node)
(:documentation "the standard evaluation"))
;; node containing an operation, more specific
(defclass op-node (node)
;; abstract class for a node containing an operation
((left-node :initform nil :initarg :left-node :reader left-node)
(right-node :initform nil :initarg :right-node :reader right-node)))
(defmethod evaluate ((node op-node))
(apply (value node)
(list (evaluate (left-node node))
(evaluate (right-node node)))))
;; concrete nodes
(defclass add-node (op-node)
;; class for addition
((value :initform #'+ :reader value)))
(defclass minus-node (op-node)
;; class for subtraction
((value :initform #'- :reader value)))
(defclass multi-node (op-node)
;; class for multiplication
((value :initform #'* :reader value)))
(defclass value-node (node)
;; class for a node containing a numerical value
((value :initarg :value :reader value)))
(defmethod evaluate (value-node)
(value value-node))
;; 1 + 3
(setf simple-add (make-instance 'add-node
:left-node (make-instance 'value-node :value 1)
:right-node (make-instance 'value-node :value 3)))
;; 1 * 10
(setf simple-multi (make-instance 'multi-node
:left-node (make-instance 'value-node :value 1)
:right-node (make-instance 'value-node :value 10)))
;; (1 + 3) + (1 * 10)
(setf advanced-add (make-instance 'add-node
Solution
(defmethod evaluate ((node op-node))
(apply (value node)
(list (evaluate (left-node node))
(evaluate (right-node node)))))can be written:
(defmethod evaluate ((node op-node))
(funcall (value node)
(evaluate (left-node node))
(evaluate (right-node node))))For this example, instead of different subclasses for every binary operation, you could just use a
binop class and instantiate it with the respective functions, since they share the same evaluate method, but that depends on what else you want do do with the nodes. (Maybe, for example, you want to have different print-object methods for each of them.)(Also, using toplevel
setfs on unestablished symbols is considered bad style, but I suppose this was just for testing and demonstration purposes. In real code, defparameter or defvar should be used.)Code Snippets
(defmethod evaluate ((node op-node))
(apply (value node)
(list (evaluate (left-node node))
(evaluate (right-node node)))))(defmethod evaluate ((node op-node))
(funcall (value node)
(evaluate (left-node node))
(evaluate (right-node node))))Context
StackExchange Code Review Q#15039, answer score: 2
Revisions (0)
No revisions yet.