120 likes | 230 Views
Object-Oriented Programming. In Lisp. Lisp vs. Scheme. Lisp has much more built-in functions and special forms, the Scheme language definition takes 45 pages while Common Lisp takes 1029 pages) Apart from lexical variables Lisp also has special variables
E N D
Object-Oriented Programming In Lisp
Lisp vs. Scheme • Lisp has much more built-in functions and special forms, the Scheme language definition takes 45 pages while Common Lisp takes 1029 pages) • Apart from lexical variables Lisp also has special variables • Scheme uses one name space for functions, variables, etc., Lisp doesn’t. • Scheme evaluates the function part of a function call in exactly the same way as arguments, Lisp doesn’t. • Lisp functions can have rest, optional and keyword parameters. Scheme functions only can have the equivalent of a rest parameter.
(defstruct account (name "") (balance 0.00) (interest-rate .06)) (defun account-withdraw (account amt) "Make a withdrawal from this account." (if (<= amt (account-balance account)) (decf (account-balance account) amt) 'insufficient-funds)) (defun account-deposit (account amt) "Make a deposit to this account." (incf (account-balance account) amt)) (defun account-interest (account) "Accumulate interest in this account." (incf (account-balance account) (* (account-interest-rate account) (account-balance account))))
(defun new-account (name &optional (balance 0.00) (interest-rate .06)) "Create a new account that knows the following messages:" #'(lambda (message) (case message (withdraw #'(lambda (amt) (if (<= amt balance) (decf balance amt) 'insufficient-funds))) (deposit #'(lambda (amt) (incf balance amt))) (balance #'(lambda () balance)) (name #'(lambda () name)) (interest #'(lambda () (incf balance (* interest-rate balance)))))))
> (setf acct (new-account "J. Random Customer" 1000.00)) > (send acct 'withdraw 500.00) => 500.0 > (send acct 'deposit 123.45) => 623.45 > (send acct 'name) => "J. Random Customer" > (send acct 'balance) => 623.45
(defun get-method (object message) "Return the method that implements message for this object." (funcall object message)) (defun send (object message &rest args) "Get the function to implement the message, and apply the function to the args." (apply (get-method object message) args)) (defun withdraw (object &rest args) "Define withdraw as a generic function on objects." (apply (get-method object 'withdraw) args))
(defmacro define-class (class inst-vars class-vars &body methods) "Define a class for object-oriented programming." ;;Define constructor and generic functions for methods `(let ,class-vars (mapcar #'ensure-generic-fn ',(mapcar #'first methods)) (defun ,class ,inst-vars #'(lambda (message) (case message ,@(mapcar #'make-clause methods)))))) (defun make-clause (clause) "Translate a message from define-class into a case clause." `(,(first clause) #'(lambda ,(second clause) .,(rest2 clause)))) (defun ensure-generic-fn (message) "Define an object-oriented dispatch function for a message, unless it has already been defined as one." (unless (generic-fn-p message) (let ((fn #'(lambda (object &rest args) (apply (get-method object message) args)))) (setf (symbol-function message) fn (get message 'generic-fn) fn)))) (defun generic-fn-p (fn-name) "Is this a generic function?" (and (fboundp fn-name) (eq (get fn-name 'generic-fn) (symbol-function fn-name))))
(define-class account (name &optional (balance 0.00)) ((interest-rate .06)) (withdraw (amt) (if (<= amt balance) (decf balance amt) 'insufficient-funds)) (deposit (amt) (incf balance amt)) (balance () balance) (name () name) (interest () (incf balance (* interest-rate balance)))) (setf acct2 (account "A. User" 2000.00)) > (deposit acct2 42.00) => 2042.0 > (interest acct2) => 2164.52 > (balance acct2) => 2164.52 > (balance acct) => 623.45
(define-class password-account (password acct) () (change-password (pass new-pass) (if (equal pass password) (setf password new-pass) 'wrong-password)) (otherwise (pass &rest args) (if (equal pass password) (apply message acct args) 'wrong-password))) (setf acct3 (password-account "secret" acct2)) > (balance acct3 "secret") => 2164.52 > (withdraw acct3 "guess" 2000.00) => WRONG-PASSWORD > (withdraw acct3 "secret" 2000.00) => 164.52
(defclass account () ((name :initarg :name :reader name) (balance :initarg :balance :initform 0.00 :accessor balance) (interest-rate :allocation :class :initform .06 :reader interest-rate))) (defmethod withdraw ((acct account) amt) (if (< amt (balance acct)) (decf (balance acct) amt) 'insufficient-funds)) (setf a1 (make-instance 'account :balance 5000.00 :name "Fred")) > (name a1) => "Fred" > (balance a1) => 5000.0 > (interest-rate a1) => 0.06
(defclass limited-account (account) ((limit :initarg :limit :reader limit)))) (defmethod withdraw ((acct limited-account) amt) (if (> amt (limit acct)) 'over-limit (call-next-method)))) (setf a2 (make-instance 'limited-account :name "A. Thrifty Spender" :balance 500.00 :limit 100.00)) > (name a2) => "A. Thrifty Spender" > (withdraw a2 200.00) => OVER-LIMIT > (withdraw a2 20.00) => 480.0