Previous | Table of Contents | Next |
This procedure takes a function as its argument and returns another function that is (an approximation to) the derivative of the first function. Its common to see programs that take a function and a number as arguments and compute the numeric value of the derivative at that number, but here the result is the derivative itself, which is another function:
(define (f x) (+ (* 3 x x) (* 5 x) 6)) (define df/dx (derivative f)) > (df/dx 4) 29.0000004099511
In this example,
f(x) = 3x2 + 5x + 6
I define df/dx to be the derivative of that function, which should be
f(x) = 6x + 5
Then f(4) should have the value 29. Because I used a small finite Δx instead of taking the limit, I get a numeric result that isnt quite correct.
Object-oriented programming (OOP) is currently a popular technique for managing large programming projects. The core idea of OOP is that instead of having one overarching program that manipulates many pieces of passive data, you have intelligent data; each object knows how to carry out certain tasks. What corresponds to a data type in ordinary programming is an object class; a particular object of that type is called an instance. Each object can have local state variables, which are accessible only within the object itself but whose values persist as long as the object exists. Each object can also have methods, which are local procedures to carry out the tasks that the object knows how to perform.
Most object-oriented languages have been designed by adding new OOP syntactic forms to an existing, traditional language. You could do that in Scheme, too, and indeed there have been several OOP extensions to Scheme. Heres an example:
(define-class (counter) (instance-vars (count 0)) (method (next) (set! count (+ count 1)) count)) > (define c1 (instantiate counter)) > (define c2 (instantiate counter)) > (ask c1 next) 1 > (ask c1 next) 2 > (ask c1 next) 3 > (ask c2 next) 1 > (ask c1 next) 4 > (ask c2 next) 2
This example has one class counter and two instances c1 and c2. A counter accepts only one message, next. Each counter has a local state variable count, whose value starts at 0 and is increased by one for each next message that the counter receives.
What makes Scheme different from most languages is that there is no real need for this sort of syntactic extension. First-class procedures and lexical scope provide the necessary tools:
(define (counter) (let ((count 0)) (lambda (message) (if (equal? message next) (lambda () (set! count (+ count 1)) count) (error Unrecognized message))))) (define (instantiate class) (class)) (define (ask object message) ((object message)))
With these definitions, you can get exactly the same behavior that is provided by the hypothetical define-class syntactic extension. (In fact, define-class can be implemented as a Scheme macro.) Each instance is represented as a dispatch procedure, that is, a procedure that accepts a message (a word, such as next) as its argument and returns a method (a procedure, such as the one created by the inner lambda in this example). ask works by invoking the dispatch procedure to translate the message into a method and invoking the method.
This simple example leaves out many of the bells and whistles of an OOP language: additional arguments to methods, the ability to provide initial values to instance variables while creating an instance, and inheritance, the capability that allows one object class to use methods provided in another parent class. All of these features can be included in ordinary Scheme, using essentially the technique shown here. The resulting programs are only slightly more complicated.
The fact that Scheme can provide the power of OOP without any need for specifically OOP-related features is perhaps the most dramatic example of the Scheme philosophy of removing restrictionsin this case, the restriction in most programming languages that prevents a procedure from being the return value of another procedureinstead of adding ad hoc features for each new programming style.
Many people, when they have trouble writing their first Scheme programs, blame the syntax: I get confused by all those parentheses. Most people who teach Scheme agree that the blame is misplaced. The most common source of difficulty is trying to fit a functional program into the paradigm of sequential programs.
As a simple example, programmers who are accustomed to reading a program from left to right sometimes misinterpret an expression such as
(car (cdr sequence))
to mean first take the car, then take the cdr. You must read the expression from the inside out; it computes (cdr sequence) and then uses the resulting value as the argument to car.
It is possible to lose count of parentheses, especially when you need six close parentheses in a row, but every modern text editor has facilities that help dramatically with the notation. The two most important are automatic indentation, so that the editor makes sure the shape of the program matches the grouping of parentheses, and highlighting the matching parenthesis whenever the editor cursor is on a parenthesis.
Previous | Table of Contents | Next |