In this HTDF example problem, you're going to see a case where the problem
statement isn't very specific about the function that wee need to design.
That's going to happen more and more as we go foward in the course.
Remember what I said in the first video. Design is the process of going from a
poorly formed problem, to a well structured solution.
So, making the problem more specific is part of the design process.
In this video I'm also going to talk, about how many tests you need for a
function. And we'll see another example of a case
where you get part way through the recipe.
And realize that you need to go back and refine some work that you did in an
earlier step of the recipe. In this problem we're asked to design a
function that consumes an image and determines whether the image is tall.
So let's see, it seems clear that it consumes an image.
[SOUND]. But what does it mean to determine
whether the image is tall? Well, we're asking, is this image tall?
So that's kind of a yes, no question. By convention, whenever we design a
function that produces the answer to a yes, no or true, false question.
We designed that function to produce a Boolean, so we'll say Boolean here, okay?
And now what's the purpose going to be? The purpose is going to be something
about, if the image is tall. And the key thing is so far, I've only
said it produces a boolean, but I haven't said what true means.
So you want to say in the purpose, produce true if the image is tall.
You need to distinguish which does true mean.
Does true mean that its tall or that its not tall?
So whenever you got a function producing a, a boolean, your purpose has to be very
clear about what true means, and by implication, what false means.
So let's see, here's a stub. And this didn't tell us what name of a
function to use. The problem statement didn't tell us what
name to use. That's usually going to be the case.
You're usually going to have to invent the name of a function when you design
it. And it turns out that in the programming
language we're using, there's a convention that functions that produce a
boolean. Functions that produce an answer to a
yes, no question or a true, false question, have a name that ends in
question mark. So we're going to call this function tall
question mark. It's going to consume image and a good
dummy answer is false. So that would be the stub.
And now we can write some tests. So let's see, check, expect, tall
question mark, let's see I need an image, so let's take a rectangle.
Oh, wait a minute, before I forget here, I'm using rectangle.
So let me quickly go up top here and say require 2htdp/image.
So I'm going to make a rectangle that let's say, is 2 wide and 3 high, and is
red. The color doesn't really matter here but,
rectangle needs the arguments. Let's see, it seems to me that rectangle
that's 3 high and 2 wide is tall, so, we'll say true.
Now, I want you to ask yourself right now.
How many tests does this function need? The right number of tests for a function,
is going to be an issue we talk about more, and more during the course.
How many do you think this function needs?
Write down your answer, or just remember it, and, we'll see where we end up.
For now I'm just going to kind of, assume that it only needs this one test.
That won't turn out to be right but we'll see what happens.
Let me run the test and there's one test and it runs and fails.
So it's well formed, so we can keep going.
Let's see, this is the stub, we'll comment it out, all right, the template,
the template is tall? img...img, we'll make a copy of that.
And we'll comment out the original template, we'll label it as the template.
And now let's see what does it mean the function let's see these functions have
two cases right? Sometimes its tall and sometimes its not
tall. So when you have two cases you can use an
if because that be the case where it is tall and the case where it isn't.
And what I like to do whenever I add an if to my template.
Is I put in the f, and then I make myself three copies of the rest of the template.
As a way of saying, okay, now I've got the if in there and I need to decide what
happens here. Now let's see , tall involves comparing
the width to the height, so let's see. If the image height of the image is
greater than the image width of the image.
Let's see, if that's true, then the image is tall, and so I should produce true,
and otherwise, I should produce false. Well, let's try running that.
Okay the test passed, the test passed, the test passed, but are we done?
Have we really tested this function? Well one clue that we haven't Dr Racket
is giving right here in the way it's colored this false.
Depending on how you have your DrRacket configured, it might actually just
highlight this false with black. But in both cases, what it's saying is
it's saying, hey, this expression here was never evaluated.
There's this concept called code coverage for a test, or test coverage, which says,
given all my tests, how much of the code is actually being evaluated?
And if the answer is not all of the code, then you don't have enough tests, because
what if the piece of code that isn't evaluated is wrong?
You haven't tested that piece of code. So whenever you run your tests, if you
see code that's highlighted that says that it didn't get evaluated, then you
know for sure you need more tests. And what's happening, of course, in this
case is we tested the case where it is tall But we didn't test the case where it
isn't tall. In this case we do that fairly
straightforwardly by copying the first test, and just swapping these two around.
And saying, well in that case, it's false.
Now let's run it, both tests pass. But are we really done?
Let me make this a little bit, give us a bit more room here.
Are we really done? At this point, what you might think of
is, hey, wait a minute. There's one more case.
What if,it's the image width and the height are the same?
What should happen then? And this is a thing that happens in
function and program design all the time. You realize partway through the design
process that there's a boundary condition, or sometimes we call it a
corner case, that you hadn't quite thought of.
It's perfectly fine for that to happen, but when it happens, when you say to
yourself, wait a minute, what if the width and height are the same?
You need to immediately do a couple of things.
First, write an example for. Then decide what the behavior is.
And I'm going to decide in this case that if your width and height are the same,
you're not tall. So write the example, write the expected
result, fix the purpose, height is greater than width.
So that's unambiguous in the purpose, and if what you decided required you to
change the function, change the function. In this case it didn't.
But if I had decided this the other way, I would have to change that to a greater
than or equal. And now, run the tests again.
Now all three tests are passing. So that's the design effect function,
that works. When I run it and the tests pass, all my
code is evaluated, so I've got good test coverage.
So, I'm pretty good here. At this point, I just want to make one
minor detail point, which is whenever you have an expression of the form, if
something, true, false. You can also replace that by just this
simpler thing. Let me comment this one out, Racket >
Comment Out with Semicolons. Why are these two the same?
Well, look this one says if this expression evaluates to true, then
produce true. Otherwise, if this expression evaluates
to false, then produce false. So that's really just the same as the
expression itself. Either this expression evaluates to true
or it evaluates to false. So whenever you have, if something true,
false, you can just replace it with the question itself.
And in a normal program, once you've done something like that, you would clean this
up, you'd Delete that out from there. And in a couple weeks, you also won't
leave the stub and template behind. So now you've seen the How To Design
Functions recipe. And you've seen me work through four
examples of it, where each example showed some more wrinkles to the process.
What you should do now is go work through all the practice problems for this week
and then do the homework problems and the quiz.
Let me stress sometrhing about this material.
I ja, I just can't make this point strongly enough.
This is the kind of material that you learn by doing.
I really want to encourage you, don't look at the problem and then look at the
solution. If you do that, you're going to say to
yourself, oh I know how to do this. You need to actually take the blank
starter file and work through the problem yourself from beginning to end.
That's the only way to learn this kind of material.
At any rate, have fun with the practice problems and the homework and I'll see
you next week.