Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

...

A local expression introduces a new local scope--the names in the definitions are visible both within the bodies of the definitions and within the body. If a local name collides with (matches) a name in the surrounding program, the local name shadows the enclosing name; i.e., the matching name in the enclosing program is invisible within the local expression; only the matching local name can be accessed. The same shadowing phenomenon happens when a parameter name in a function definition collides with a name defined in the surrounding program.

  • Wiki Markup{{(local \ [}} definitions {{...\]}} body {{)}}

...

Note that the use of square brackets \ [ \ ] here is equivalent to parentheses( ). The brackets help set off the definitions a little more clearly for readability.

In order to use local and the other features about to be introduced in class, you need to set the DrScheme language level to Intermediate Student.

Exercises

Finding the maximum element of a list.

Let's consider the problem of finding the maximum number in a list which is used an example in Lecture 7.

...

Note that you can write an efficient solution without using local; you can use an auxiliary function instead. The auxiliary function takes the expression appearing on the right hand side of the local definition as an argument. Quickly write this version of ma the function including a contra.

In general, you can take any program using local, and turn it into an equivalent program without local.
  Using local doesn't let us write programs which were impossible before, but it does let us write them more cleanly and concisely.

Generating lists of ascending numbers

Retrieve your code for the up function and its helper upfrom from last lab.

...

  1. Avoid code duplication. I.e., increase code reuse.
  2. Avoid recomputation.
    This sometimes provides dramatic benefits. Avoid re-mentioning an unchanging argument.
  3. To hide the name of helper functions. An AsideWhile important conceptually, most aside: many programming languages (including SchemeJava) provide alternate mechanisms for this (such as public/private attributes) for hiding helper functions and data which scale better to large programs . These mechanisms typically have names like modules orpackages. As this one example shows, even small programs can get a bit less clear when hiding lots of helpersand accomodate the unit testing of hidden functions (which local does not!).
  4. Name individual parts of a computation, for clarity.On the other hand, don't get carried away.

Here are two easy ways to misuse local:

Code Block
;; max-num: non-empty-list-of-number -> number
(define (max-num a-nelonalon)
  (local [(define max-rest (max-num (rest a-nelonalon)))]
    (cond [(empty? (rest a-nelonalon)) (first a-nelonalon)]
          [else  (if (<= (first a-nelonalon) max-rest)
                     max-rest
                     (first a-nelonalon))])))

Try running this example on any legal input (according to the contract). It blows up!Question: What's wrong with this?
Make sure you don't put a local too early in your code.

Code Block
;; max-num: non-empty-list-of-number -> number
(define (max-num a-nelonalon)
  (cond [(empty? (rest a-nelonalon)) (first a-nelonalon)]
        [else  (local [(define max-rest (max-num (rest a-nelonalon)))
                       (define first-smaller? (<=(first a-nelonalon) max-rest))]
                  (if first-smaller? max-rest (first a-nelonalon)))]))

This isn't wrong, but the local definition of first-smaller? is unnecessary. Since the comparison is only used once, this refactoring is not a case of code reuse or recomputation. It provides a name to a part of the computation, but whether that improves clarity in this case is a judgement call.

...

Now that we have extended our hand-evaluation model to include the definitions forming a program, we can describe how to handle define statements that bind ordinary variables rather than functions. The right hand sides of define} statements are not necessarily values. When evaluating a program, you must always reduce the leftmost expression that is not a value; in some cases this expression may be part of the right hand size of a {{define.

Sample evaluations involving local and explicit program text

...

Code Block
(define x 5)
(local (define x_0 7) x_0)

=>

Code Block

(define x 5)
(define x_0 7)
x_0

=>

Code Block

7
Example 2.
Code Block

(define x 5)
(define y x)
(define z (+ x y))
(local [(define x 7) (define y x)] 5)
(define+ x_0 7)
x_0 y z))

=>

Code Block

7

...

Code Block
(define x 5)
(define y x5)
(define z (+ x y))
(local [(define x 7) (define y x)] (+ x y z))

...

Code Block
(define x 5)
(define y 5)
(define z (+ x5 y))
(local [(define x 7) (define y x)] (+ x y z))

...

Code Block
(define x 5)
(define y 5)
(define z (+ 5 y5))
(local [(define x 7) (define y x)] (+ x y z))

...

Code Block
(define x 5)
(define y 5)
(define z (+ 5 5))10)
(local [(define x 7) (define y x)] (+ x y z))

...

Code Block
(define x 5)
(define y 5)
(define z 10)
(local [(define x_0 7) (define y_0 x_0)] (+ x_0 y_0 z))

=>

Code Block
(define x 5)
(define y 5)
(define z 10)
(define x_0 7)
(define y_0 x_0)
(+ x_0 y_0 z)

...

Code Block
(define x 5)
(define y 5)
(define z 10)
(define x_0 7)
(define y_0 7)
24

Note: { + } is a function of an arbitrary number of numbers in Scheme. Hence (+ 7 7 10) reduces to 24 in a single step. The longer form of this evaluation adds an extra step in lifiting the local.

...

Note the preceding definitions of filter are not acceptable to DrScheme because the name filter collides with the Scheme library function named filter (which performs the same operation). If you want to run either of the preceding programms programs in DrScheme, you will have to rename filter (as say Filter).

...

Do not bother writing a contract, purpose statement, ortemplate or template instantiation for this function since we recommend that you throw this code away after the lab is over. The name has been changed to Filter} to avoid colliding with the Scheme library function named {{filter.

Exercises

  • Load one of the more interesing Scheme programs you have written (such as HW2) into DrScheme. (If you did the preceding optional exercise, you can use this program.) Perform the "Check Syntax" command to identify where each variable is bound. Recall that when you put your cursor above a binding instance, it will draw arrows to the uses, and when you put your cursor above a use, it will draw an arrow to its binding instance.
  • X Using the Scheme library function filter, develop a function that takes a list of numbers and returns a list of
    all the positive numbers in that list.

...

These functions are very similar and can be written trivially using the Scheme library function map discussed in Lecture 9.

Exercises.
  • Write double-nums and <3-nums using map.

The Big Picture

...

Many functions we've written fit this pattern, although this fact might not be obvious at first.

Exercises

  1. X Based upon the preceding equation ([1]), what should the following evaluate to? Think about them first, then try them in DrScheme (where foldr is pre-defined).
    1. (foldr + 5 (list -1 5 -3 4 2))
    2. (foldl + 5 (list -1 5 -3 4 2))
    3. (foldr - 5 (list -1 5 -3 4 2))
    4. (foldl - 5 (list -1 5 -3 4 2))
    5. (foldr cons empty (list -1 5 -3 4 2))
    6. (foldl cons empty (list -1 5 -3 4 2))
    7. (foldr append empty (list (list 1 2) (list 4) empty (list 8 1 2)))
    8. (foldr append empty (list (list 1 2) (list 4) empty (list 8 1 2)))
  2. X What is the contract for foldr? For foldl? You should be able to determine this from the equations and the examples above.
    (We also covered the typing of foldr in lecture.) Hint: First, determine the type of foldr and foldl assuming the input list is a list of numbers, and then generalize.
  3. Using foldr, define a function to compute the product of a list of numbers. Do the same for foldl.
  4. Using foldr, define map. (Also done in lecture.) Do the smae same for foldl.
  5. Define the function Foldr to satisfy equation (1) above. As you might expect, it follows the template for a function consuming a list. (It was also done in lecture.) Test your function against Scheme's built-in foldr to make sure they give the same results for the same inputs.
  6. Define the function Foldl to satisfy equation (2) above. As you might expect, it follows the template for a function consuming a list. Test your function against Scheme's built-in foldl to make sure they give the same results for the same inputs.
  7. Define a function to compute whether all elements in a list of numbers are greater than 6. Write two version versions, one using foldr and one using foldl, choosing suitable names (distinct from filter for each.
    Then generalize both definitions to define filter. Are your two filter functions identical? Hint: look at computations that generate errors.
  8. Define a function that, given a list of numbers, returns the sum of all the positive numbers in the list. Write two versions, one using foldr and one using foldl.
  9. Without using explicit recursion, develop a function upfrom that, given i and n returns the list of length i of numbers up to n.

...

The following can be defined using some combination of the pre-defined abstract functions.

  1. X Define a function that, given a list of numbers, determines whether all of the numbers in the list are even. Write two versions one using foldr and one using foldl.
  2. Define andmap and ormap using foldr rather than recursion. Do the same using foldl.

...