Functional Problem Solving (CSC 151 2015F) : Handouts
Primary: [Front Door] [Schedule] - [Academic Honesty] [Disabilities] [Email] - [FAQ] [Teaching & Learning] [Grading] [Taking Notes] [Rubric] [Remote Access]
Current: [Assignment] [EBoard] [Lab] [Outline] [Reading]
Sections: [Assignments] [EBoards] [Labs] [Outlines] [Readings] - [Examples] [Handouts]
Reference: [Setup] [VM] [Errors] - [Functions A-Z] [Functions By Topic] - [Racket] [Scheme Report (R5RS)] [R6RS] [TSPL4]
Related Courses: [Curtsinger (2015F)] [Davis (2013F)] [Rebelsky (2015S)] [Weinman (2014F)]
Misc: [Submit Questions] - [SamR] [Glimmer Labs] [CS@Grinnell] [Grinnell] - [Issue Tracker (Course)]
The writeup for Lab 30, on paterns of list recursion, was to come up with a good question on the lab. Here are the questions I received and answers to those questions. They are in no particular order.
How should we be documenting our kernel and helper procedures when we use them to write recursion procedures?
Preferably with the Six P's. Minimally with the four P's. But once you know how to make the kernels/helpers local (Wednesday's class), you can just document them with a sentence.
How does the fold pattern differ from the other kinds of recursion
we've done?
It's a very specific kind of recursion. We also treat the singleton list as the base case, and return that value. Otherwise, we always combine using a basic operation.
But the main issue is for you to see that the patterns are essentially identical for a wide variety of procedures.
(define PROC
(lambda (lst)
(if (null? (cdr lst))
(car lst)
(FUN (car lst) (PROC (cdr lst))))))
For Exercise 5a, should (list-contains? (list 5.0 6.0) 5) return true or false? In other words, do we consider floating point numbers equal to integers?
No, inexact numbers don't generally equal exact numbers when you use
equal?. But they often do when you use=.
On problem 4, why can we not use the all? or any? commands? Can you ever use them in recursion?
I asked you not to use
all?andany?because they make the problem much too simple. This is a case in which I'd like you to think about how to write the solutions recursively.
In the reading List Recursion, you talked about the use of or and and
to replace if. When is using or and and preferable to if?
In cases in which you find that you are explicitly writing
#tand#f, you are probably better of usingandandortoifandcond. You may also find that it makes more logical sense over the long run.
Do you just use and and or for Boolean results?
No. As you may recall from exam 2, we can use
andandorfor all kinds of results.
In the irgb-all-dark? procedure, why if the list is empty will the
procedure return #t?
irgb-all-dark?holds if every color in the list is dark. Every color in the empty list is dark.Some folks would call this an error, but most logicians and Schemers like the vacuous base case.
What are the benefits of using recursion instead of map?
mapalways gives you back a list, and a list of the same length as the original. Recursion lets you give back a much wider variety of types. And, as we saw in the numeric recursion reading, you can do recursion with non-lists, whilemapalways requires a list..Building lists is also somewhat expensive, so avoiding it when we can is a good strategy.
What do you mean by a template?
A pattern that has you fill in a few pieces. Something like the following:
(define PROC
(lambda (lst)
(if (null? (cdr lst))
(car lst)
(FUN (car lst) (PROC (cdr lst))))))
How do I find the darker of two colors?
Compare the brightness and choose the less bright one.
(define irgb-darker-of
(lambda (color1 color2)
(if (> (irgb-brightness color1) (irgb-brightness color2))
color2
color1)))
Can you go through a recursive example using and and or?
We'll do
irgb-all-dark?from the reading.
;;; Procedure:
;;; irgb-all-dark?
;;; Parameters:
;;; colors, a list of integer-encoded RGB colors.
;;; Purpose:
;;; Determine whether all of the elements of a list of colors
;;; represent dark colors.
;;; Produces:
;;; all-dark?, a Boolean.
;;; Preconditions:
;;; All the values in the list are integer-encoded RGB colors.
;;; irgb-dark? is defined.
;;; Postconditions:
;;; all-dark? is #t if all of the elements of values are dark.
;;; all-dark? is #f if at least one element is not dark.
(define irgb-all-dark?
(lambda (colors)
(or (null? colors)
(and (irgb-dark? (car colors))
(irgb-all-dark? (cdr colors))))))
I'm going to simulate colors by writing their irgb values. We'll assume that a color is dark if its brightness is less than 50.
(irgb-all-dark? '((irgb 0 0 0) (irgb 100 0 0) (irgb 255 255 255) (irgb 0 0 0)))
; Use the definition
-> (or (null? ...) (and ...))
; or evaluates left to right
; the list is not null
-> (or #f (and ...))
; (or #f anything) is anything
-> (and (irgb-dark? (car ...)) (irgb-all-dark? (cdr ...)))
; and evaluates left to right, so deal with the first expression
-> (and (irgb-dark? (irgb 0 0 0)) (irgb-all-dark? (cdr ...)))
; (irgb 0 0 0) is dark
-> (and #t (irgb-all-dark? (cdr ...)))
; (and #t anything) is anything
-> (irgb-all-dark? (cdr '((irgb 0 0 0) (irgb 100 0 0) (irgb 255 255 255) (irgb 0 0 0))))
; Need to evaluate the cdr
-> (irgb-all-dark? '((irgb 100 0 0) (irgb 255 255 255) (irgb 0 0 0)))
; Use the definition
-> (or (null? ...) (and ...))
; or evaluates left to right
; the list is not null
-> (or #f (and ...))
; (or #f anything) is anything
-> (and (irgb-dark? (car ...)) (irgb-all-dark? (cdr ...)))
; and evaluates left to right, so deal with the first expression
-> (and (irgb-dark? (irgb 100 0 0)) (irgb-all-dark? (cdr ...)))
; (irgb 100 0 0) is dark
-> (and #t (irgb-all-dark? (cdr ...)))
; (and #t anything) is anything
-> (irgb-all-dark? '((irgb 255 255 255) (irgb 0 0 0)))
; Use the definition
-> (or (null? ...) (and ...))
; The list still isn't null
-> (and (irgb-dark? (car ...)) (irgb-all-dark? (cdr ...)))
; The car is NOT dark
-> (and #f (irgb-all-dark? (cdr ...)))
; (and #f anything) is #f
-> #f
Do all the recursive procedures that do not function with empty lists result in one of the values in the given list? In other words, do these procedures always single out one value compared to the other values? For example, a recursive call that asks for the darkest color of a list of colors always returns one color. A different recursive call that asks for the number of dark colors in a list returns a number. The latter could function if given an empty list whereas the former would not function, right?
That's a good analysis of those two kinds of procedures. However, there are also instances of functions that will not function with an empty list in which we get a value other than an element of the list. For example, finding the average color in a list of colors needs a nonempty list of colors, but won't necessarily return one of the colors in the list. But that's an exception; I will admit that most of the examples that come to mind return an element of the list.
Is there a minimum value for ints in scheme for the base case of finding the largest to return?
There is no minimum value for integers in Scheme. If you're using a helper, you should probably just use the first value as your initial guess.
In the mentor session tonight we did a problem similar to Exercise
four. It was testing if all numbers were odd. If the list was null
Zachary said technically the list was all odd. Does this apply to
Exercise 4 as well? If the list is empty/null, are they all bright and
it is true? Whereas with any-bright? it would be false?
Yes, that is correct. According to the rules of logic, all the numbers in an empty list are odd. And the empty list does not contain any odd numbers.
Similarly, all the colors in the empty list are bright. (Amazingly, all the colors in the empty list are also not bright.) And the empty list does not contain and bright colors, so
any-bright?would be false.
Do you find it better to think about recursion all at once, or to break it down through each step, as you did in class?
Interestingly, the CSC 151 team (faculty and mentors) were discussing this issue today. It's very useful to expand a recursive function to understand what it is doing. But, once you've done that a few times and understand how recursion works, it's often easier to just step back and say "Yeah, recursion works; let's look at how to use it."
When I approach a recursive problem, I tend to ask myself three questions. 1. What is a case that is simple enough that I can solve? 2. How do I get closer to that case? (That is, how do I simplify the input?) 3. If I've solved the simpler version, how does that help me solve the less simple version?
Is it best to write a separate kernel procedure you can call, and then write a procedure that calls that with the correct inputs?
Yes, I find it useful to write separate kernels while developing my code. As you'll learn in class on Wednesday, we eventually want to make those kernels useful so that people don't accidentally call them with the "wrong" parameters.
Should we always prefer tail recursion to other forms of recursion if we can do it?
"Always" is a strong term. All other things being equal, we prefer tail recursion because the computer can implement it better. But I tend to prioritize readability over speed in many cases.
What is the base case for extra 4?
We've included it in the examples. The base case is the empty list, which unriffles into two empty lists.
How do I solve problem 4 without any if statements?
(define irgb-all-bright?
(lambda (colors)
(or (null? colors)
(and ...))))
(define irgb-any-bright?
(lambda (colors)
(and (not (null? colors))
(or ...))))
How do you work with lists of lists in exercise 2?
You can have lists of anything. That includes lists of lists.
For example
(list (list 1 2 3) (list 10 11 12))is a list of two lists. The first is'(1 2 3). The second is'(10 11 12).Similarly
(list (list 1 2 3) (list 10 11 12) (list 20 21))is a list of three lists.Your goal in this problem is to shove all of the lists together into a single list. You can use
appendto join two lists together, but here you're joining potentially more than two lists, and you have them all listed together.
Do you recommend using helper procedures or ones without when using recursion. Which do you believe is easier to code and read?
It depends on the problem. At my stage, I find either equally easy to read. At your stage, I think tail recursion with helper procedures is easier to read (we'll even look at a pattern today). But, as we saw in the numeric recursion lab, there are some problems that are better solved by direct recursion.
In Exercise 3, the pattern is that we ask Scheme first to check if the cdr of the list is empty, and if so return the car of the list. Otherwise we use the built-in procedure that takes two parameters on car of the list and the recursion. What if the built in function takes one or more than two variables, is folding still applicable?
Folding is generally only used with two-parameter procedures.