0:00

In this session, we are going to apply what we learned so far in a larger

example. The task is to develop a square root

function using Newton's method. That's the first time that we need more

than a couple of lines to actually express a program and we will learn new techniques

and new tools for doing so. So, what that means is that we want to

define a function with the signature. It takes a double and gives me back a

double, and return the value is the square root of its parameter x.

The classical way to compute that is by successive approximations using the method

of Isaac Newton. So, let's see what this method is.

To compute square root of x, we start with an initial estimate value.

It doesn't really matter what that estimate is as long as it's positive.

So, let's pick y = one. And then, we repeatedly improve the

estimate by taking the mean of the old estimate y and the value of x divided by

y, where x is the value we want to draw the root from.

So, it's, let's see that in action. For the value of x, in our example, would

be two. Our initial estimation is one, as usual,

so the initial quotient is two divided by one, that's two, the mean is 1.5.

Now, we plug in 1.5 to our estimation, we get a new quotient of 1.33, a new mean

that you'd see here and you'll see that it converges very quickly to the actual value

of two. So now, we have an abstract algorithm.

Let's see how we would put that into a function that computes that algorithm.

A typical way to code algorithms in functional languages is to go step by

step. So, we take a small task and formulate it

as a function and then probably, that task will need further tasks that then would be

defined in their own function. The first function we want to define is

the actual iteration called the square root Iter, which says, well, if you have a

guess and the value would be one to draw the root from, what do we do?

Well, either we stop the iteration and return the result or we go and do another

iteration step. The predicate that controls that we give

it a name, let's call it, isGoodEnough. So, if my guess is good enough, then I

just can return the guess. If my guess is not yet good enough, then I

have to improve my guess. Let's do that with another function in

prove and call, square root Iter again with the improved guess.

Note that square root Iter is recursive so its right-hand side calls itself.

One peculiarity in Scala is that the return type of a recursive function needs

to be given always whereas, for other functions, it's optional.

The reason for that is that to compute that return type, the Scala compiler would

have to look at the right-hand side and because the square root Iter function is

recursive it's stacked in a cycle. It would need the return type for that to

compute the type for expression. To break the cycle, we require that

recursive functions always have explicit return types.

It actually makes sense even for other publicly used functions to give an

explicitly return type because that's good documentation but it's not required by the

language. Okay.

So, let's implement this in practice. What we're going to see is a new way to

write programs. So far, we have done everything in the

repo. Now, for larger programs, we have

something that is better adapted. We go into Eclipse and we start a

worksheet. So, I take you through the steps one by

one. Here, we have an empty Eclipse thing.

We first start a new project the Scala project.

Call it, demands a name, call it [unknown] for the course.

And then, in the project what we can do is define a package, which is essentially a

directory in which we are going to put all the stuff from this week.

Call this Week one. We don't need to define packages but it's

good practice to keep stuff tidy and ordered.

And then, in that package, we define a new sheet.

It needs a name, let's call it Session. In a worksheet, what we can do is we can

type expressions and have them evaluated, just like in a repo.

So, we can do one plus two and expressions get reevaluated when we hit Save, that

will become [unknown] or Control+S. But the expressions show up on the right

in a different column that you see here. We can also define functions, let's say,

the absolute, absolute function that we've seen before.

So, we could say, well, if x is less than zero - x else x And the repo would answer

the, with the value of the absolute function, well, not the repo, the

worksheet but the two are actually related.

It's just a different mode of presentation.

One thing we can do which is nice, nicer in the worksheet is we can actually go

back and edit expressions and the reevaluation will then show the edited

value. So, it's not just a single line editing

window but a whole window that we can use. So, let's use the worksheet to complete

our square root functions. The first thing I'm going to do is take it

from the slide. So, I select square root Iter here and

paste it inside the worksheet. I can reformat with Control+Shift+F.

So, that would reformat things nicely. But the worksheet still doesn't compile

because, of course, I have two functions here that I haven't yet defined that would

be as good enough and improve. So, for isGoodEnough, let's do that first.

So, is, isGoodEnough takes these same parameters as square root Iter, and it

returns a bullion that says whether we can stop the iteration or whether we need more

steps. So, what could that predicate be?

Well, one way to do it would be to say, well, if we take the current guess here

and we square it. If we are close enough to the original

value x, then guess by itself would be close enough to the desired square root

value. So, let's code that up.

We would say, guess, guess - x, we would have to take the absolute value of that.

And it would have to be smaller than some epsilon value of, say, 001 for the moment.

Okay. So, that was number one.

The second function we need to define is Improve.

And that's now the core of the Newton method, we say, would say, well, it could

take a guess and an X. And to improve the guess, what do we do?

Well, we say, we need to take the mean of the value the current value of guess and

the quotient of x divided by guess. So, to take the average, we would first

sum the two values and then, divide by two.

7:23

Good. Now, we have a worksheet that compiles.

You see on the right-hand side, you see all of the types.

The last function we need to define is square root itself.

So, let's do that, what would that be? Well, it would be the square root

iteration. With our first initial value, we said,

that was 1.0 and x. Well, now, we have all the elements in

place and we can start to test our function.

Let's start with the square root of two and we get 1.4142.

So, that looks right. Let's do another one.

Square root of four, and we, we get something close to two, as we would

expect. So, as an exercise, I would like you to

think about the implementation we have so far and try to improve it.

One thing to improve is that the, is good enough test is not very precise for small

numbers. And it can lead to non-termination for

very, very large numbers. So here, let me demonstrate that.

And let's take a very small number. Let's say, one^-6.

So, one / 1,000,000.

So our square root, the square root we would expect to see is one over minus

three. And actually what we see is 0.03.

So, that's pretty far away from where we want to be.

And the other problem we have is if we take a large one, let's say, one to the

60th power. What would we see then?

Well, actually what we'd see is non-termination.

You see this here, with this little bar? That's as well, it doesn't find the square

root. Not so good.

Let me take that out with an escape. So, can you explain why that is?

Why is the, is good enough test not precise for small numbers and why can it

lead to non-termination for very large numbers?

And secondly, can you define a different version of isGoodEnough that doesn't have

these problems? Here are some numbers to test your, your

version, some very small and some very large numbers.

So, to answer that exercise, let's go back to our worksheet.

The problem, we had for both small numbers and, and large numbers was that our

isGoodEnough test is, takes an absolute difference.

It asks whether the absolute difference between the square of guess and X is less

than some threshold value. And that's not very good for very, very

small numbers because the number we want to find in the square root might, might

even be smaller than that. So, that, relatively speaking, that

epsilon value might be huge compared to the number we want to find.

And for very large numbers, we have the opposite problem, which is that very large

floating point numbers have, can actually be further apart than this epsilon value.

That means in the 52-bits, they have available for the [unknown].

It could be that the distance between one number and the next is actually larger

than 0.001. And in that case, of course the iteration

can never stop because, simply because there is no value that's ever good enough.

So, we can solve both problems by making the test proportional to x.

And that's very simply done by saying, okay, let's divide the absolute difference

by the current value of x and let's make that be smaller than the threshold value.

So, once I've done that, I can simply reevaluate my spreadsheet.

And now, I will see that everything computes nicely.

And for the small number, I get something very close to one^e-3 as expected, and for

the large numbe, I get something like e1 to e30, and that's also as expected.