CSC153 2004S, Class 9: Preconditions and Postconditions Admin * HW1 not graded yet * Interesting Convo tomorrow * Read "Local Procedure Bindings with letrec" * Lots of discussion today, just a little lab * Test coming: Distributed next Monday, due a week from next Wednesday. Overview * Past topics, revisited * Efficient computation of exponentiation? [Thursday] * On the ordering of let vs. lambda * Why document? * The Six P's * Writing robust code * Reporting on errors * Husk and Kernel programming * Example: sum-of-squares * Lab Ordering let and lambda (define fourthblue (lambda (x) (let ((square (lambda (y) (* y y)))) (square (square x))))) (define fourthred (let ((square (lambda (y) (* y y)))) (lambda (x) (square (square x))))) (define square-all-blue (lambda (lst) (let ((square (lambda (y) (* y y)))) (if (null? lst) null (cons (square (car lst)) (square-all-blue (cdr lst))))))) (define square-all-red (let ((square (lambda (y) (* y y)))) (lambda (lst) (if (null? lst) null (cons (square (car lst)) (square-all-red (cdr lst))))))) See square-all.scm for an example Moral: Whenever possible, put your lets outside your lambdas /Documentation in Scheme/ * How: A comment is any text that begins with a semicolon and ends with the end of line * What: Something to be read by the human reader and not the computer * Example: (let ((stddev (sqrt variance))) ; Prof. Kupier told me so * Why? * To help "other people" read your code. * Programming is collaborative: Why reinvent when you can reuse? * Programming is incremental: Others may have to debug/correct your code. * Most people forget what they've done - You soon become the "other" * Easier to use if you know what it's supposed to do * How? revised * Write the comments, first! * What, revised * Each procedure should be documented as to its primary purpose: People can use the procedure if they know *what* it does, even if they don't understand how. * The particular details of the implementation should be documented so that the maintainer/debugger/etc. * General design of the program; Assumptions, etc.. * Authorship, table-of-contents, etc. /The Six P's/ * Procedure: Name it * Parameters: Name and Type them * Types are "how the computer looks at the information" * Examples: Integer, Real, string, list of strings, ... * Purpose: Short description of what the procedure is expected to. * Produces: Name and type of result * Preconditions: Semi-formal specification of non-type requirements. * Postconditions: Semi-formal specification of results. Example: /* * Procedure: * square * Parameters: * x, an integer (int) * Purpose: * Compute the square of x. * Produces: * square-of-x, an int * Preconditions: * x must be in the range [-sqrt(largest-int) ... sqrt(largest-int)] * Postconditions: * square-of-x = x*x */ int square(int x) { return x*x; } // square(int) Philosophy: * Computing is an adversarial enterprise * Programmer vs. other programmer * The "client" of a procedure excepts it to work in all cases for which the preconditions are met * The "author" of a procedure wants to do as little work as possible Educational Philosophy: * Thinking carefully about failure is useful. Computing Philsophy, Revisited: * Belief one: Stating them is enough. * Easier for you as a programmer. * Much more efficient: You don't have to check every time. * Belief two: Check for them and report an appropriate error. * More helpful for someone who actually has to deal with the program. * Belief three: Provide two versions of every procedure, one that checks preconditions and one that doesn't. In Scheme, you can use the error procedure to report errors. * Prints a nice bug. * Prints an error message that you supply. * Stops computation immediately If you do your checking particularly badly, it can be really inefficient. (define square-all (lambda (lst) (cond ((null? lst) null) ((all-numbers? lst) (cons (square (car lst)) (square-all (cdr lst)))) (else (error 'square-all "expects a list of all numbers"))))) The solution: Iowa's contribution to the programming world * "Husk and Kernel programming" * The kernel is the valuable part * The husk protects the kernel (define square-all (lambda (lst) (if (all-numbers? lst) (square-all-kernel lst) (else (error 'square-all "expects a list of all numbers"))))) (define square-all-kernel (if (null? lst) null) (cons (square (car lst)) (square-all-kernel (cdr lst))))