Com S 342 meeting -*- Outline -*- * generalizing patterns using higher-order procedures (1.3) ** analogy procedures, like cube, allow one to abstract from particular arguments we want to abstract from programming patterns ** procedures as arguments (1.3.1) see the file some-examples.scm in this directory (define (sum-integers a b) (if (> a b) 0 (+ a (sum-integers (+ a 1) b)))) (define (sum-cubes a b) (if (> a b) 0 (+ (cube a) (sum-cubes (+ a 1) b)))) (define (pi-sum a b) (if (> a b) 0 (+ (/ 1.0 (* a (+ a 2))) (pi-sum (+ a 4) b)))) point out the parts they have in common, and the parts that differ pass in the parts that differ as arguments to sum use this to recover the examples point out connection with the mathematical summation notation sum itself can be used as a building block for example to approximate definite integrals by summing area of small rectangles integral from a to b of f = [f(a + dx/2) + f(a + dx + dx/2) + f(a + 2dx + dx/2) + ... + f(b - dx)] * dx (define (integral f a b dx) (define (add-dx x) (+ x dx)) (* (sum f (+ a (/ dx 2)) add-dx b) dx)) ** constructing procedures using lambda notation (1.3.2) useful in procedures such as pi-term and pi-next in such cases one wants to just described the procedure without giving it a name use lambda to write the procedures for pi-term and pi-next explained define sugar in Scheme. Relate to the language we defined. ** general methods in Java overall plan: first do this as a "translation", then group into objects for a more OO approach. In Java, there are two problems in duplicating these ideas (since there is no explicit lambda notation): 1. the types of the procedure arguments have to be declared 2. the "procedure objects" have to be created somehow *** interfaces as collections of method types write interfaces for term and next in series (as separate interfaces). ------------------------------------------ public interface DoubleTerm { double value(double a); } public interface DoubleNext { double next(double a); } ------------------------------------------ explain: interfaces all declare public (nonstatic) methods, brief idea of object and closure. Declare classes PiOver8DoubleNext and PiOver8DoubleTerm that implement these, note non-static methods, (default) constructor. Then show that these classes can be made inner classes of Sum. Then explain the idea of anonymous inner classes based on the above. In Java (1.1 and following) anonymous inner classes are almost like lambda (except that they only retain static variables of surrounding class if static, instance variables (fields) of surrounding class if nonstatic.) ------------------------------------------ public class Sum { public static double sum(DoubleTerm term, double a, DoubleNext next, double b) { double total = 0.0; while (a <= b) { total = total + term.value(a); a = next.next(a); } return total; } public static void main(String argv[]) { System.out.println(sum(new DoubleTerm() { public double value(double x) { return 1.0; } }, 1.0, new DoubleNext() { public double next(double x) { return x + 1.0; } }, 10.0) ); System.out.println(8.0 * sum(new DoubleTerm() { public double value(double x) { return 1.0 / (x * (x + 2.0)); } }, 1.0, new DoubleNext() { public double next(double x) { return x + 4.0; } }, 1000.0) ); } } ------------------------------------------ *** grouping operations into classes Use an abstract class Series that implements DoubleTerm and DoubleNext, put sum in it! Talk about method invocation, inheritance, this.next = next in a subclass Do a concrete class PiOver8Series that implements these. ** let expressions pp. 63-66 goals: syntactic sugars, declarations, expression languages in Java, one can declare local variables within a method, one can use " define" to the same thing in Scheme, however, that does not work with the substitution model of procedure applications that we have discussed earlier. The following shows how we can use a let expressions in Scheme to have local definitions of values and still work within the substitution model. Suppose we want to compute the following function. f(x,y) = x(1+xy)^2 + y(1-y) + (1+xy)(1-y) we want to avoid recomputing (1+xy) and (1-y) (define (f x y) (define (f-helper a b) (+ (* x (square a)) (* y b) (* a b))) (f-helper (+ 1 (* x y)) (- 1 y))) (define (f x y) ((lambda (a b) (+ (* x (square a)) (* y b) (* a b))) (+ 1 (* x y)) (- 1 y))) (define (f x y) (let ((a (+ 1 (* x y))) (b (- 1 y))) (+ (* x (square a)) (* y b) (* a b)))) we define the meaning of the last of these to be the middle one, that is, this is a syntactic sugar pitfalls of let the variables are computed simultaneously this is not sequential (mention let*) advantages of let can be used in any expression context (+ (let ((x 3)) (+ x (* x 10))) x) scoping have them draw arrows from uses to definitions ** general methods or patterns the advantage of functional abstraction (and the functional paradigm) is that one can use procedures to directly express patterns or general methods of computation. *** finding roots by the half-interval method start with a and b such that f(a) < 0 < f(b), if f is continuous, there must be roots between a and b write the procedure search in Scheme *** finding fixed points of functions define fixed point of f is an x s.t. f(x) = x write the class FixedPoint in Java use a non-static method! formulate the square root computation as a fixed point search finding the square root of x: find y s.t. y^2 = x i.e. y s.t. y = x/y so we can look for the square root of the function (lambda (y) (/ x y) (define (sqrt x) (fixed-point (lambda (y) (/ x y)) 1.0)) But this does not work! if guess is y1, next guess, y2, is x/y1, and so y3 is x/(x/y1) = y1! the root is between our guess, y, and x/y, so we can prevent oscillation by averaging these so use (lambda (y) (average y (/ x y))) this is called average damping.