WEBVTT

00:00.150 --> 00:07.590
Hello again! In this video, we are going to look at lambda expressions and partial evaluation. The main

00:07.590 --> 00:11.370
use for lambda expressions is for writing in-place

00:11.430 --> 00:17.730
local functions, in things like algorithm calls. Lambda expressions are first-class objects.

00:17.910 --> 00:20.040
In fact, a lambda expression is an object.

00:20.400 --> 00:24.240
It is an object of the functor class that the compiler generates.

00:26.270 --> 00:32.940
We can store a limited expression in a variable. So we can write a lambda expression, and then the compiler

00:32.960 --> 00:34.070
will create an object.

00:35.210 --> 00:39.980
We cannot know the type of this object because it is a class that is defined by the compiler.

00:40.640 --> 00:48.680
So we have to use auto, and the compiler will provide the name for us. And then we can pass this as a

00:48.680 --> 00:52.310
callable object, to something like an algorithm call.

00:52.490 --> 00:56.960
So that is the same code we had before, but we are splitting the lambda expression and the algorithm

00:56.960 --> 00:58.940
call into two separate statements.

01:01.800 --> 01:03.150
So here is that code.

01:03.600 --> 01:08.190
So this is the same code as before, but with the lambda expression split out.

01:12.140 --> 01:18.830
And we get the same result. Because a lambda expression is a first-class object, we can also return it from

01:18.830 --> 01:25.250
a function, as well as passing it to a function. So we can write a function which returns a lambda expression.

01:26.030 --> 01:30.530
Again, we have to use auto for the return type, which is allowed in C++14.

01:30.770 --> 01:35.270
Then we have a lambda expression in the function body, which is just returned.

01:36.020 --> 01:41.060
So this lambda expression will capture the function argument, because that is a variable which is in

01:41.060 --> 01:42.590
scope, in the function body.

01:42.950 --> 01:46.480
And then you can do something inside the body of the lambda. The lambda

01:46.480 --> 01:48.230
will also take an argument itself.

01:49.780 --> 01:56.680
So this lambda is going to capture this "salutation" argument, and then it will append some of the things to

01:56.680 --> 01:58.030
it, including its arguments.

01:58.150 --> 02:05.830
So if we call this function, "greeter", with the argument, "Hello", then the lambda expression will capture

02:05.830 --> 02:06.440
this argument,

02:06.470 --> 02:06.880
"Hello".

02:07.690 --> 02:10.600
So this "salutation" will now have the value

02:10.600 --> 02:10.960
"Hello".

02:11.710 --> 02:16.780
And then, when we call the lambda expression with an argument, it will append a comma and the argument

02:17.080 --> 02:17.730
to the string,

02:17.740 --> 02:18.130
"Hello".

02:20.020 --> 02:22.690
So this "greet" that is returned is a lambda expression.

02:24.690 --> 02:28.350
It takes someone's name as argument, and it adds a greeting to it.

02:31.040 --> 02:32.780
So let's look at this code again.

02:33.850 --> 02:41.020
There is the lambda expression. There is the function that returns the lambda expression. We call the function

02:41.020 --> 02:46.780
with the argument, "Hello", so salutation in this lambda is now "Hello". And then we store the returned

02:46.960 --> 02:47.590
lambda expression.

02:48.100 --> 02:53.320
So when we call this lambda expression with the argument "students", the argument here is going to be

02:53.320 --> 02:55.270
"students", the salutation is going to be "Hello".

02:55.630 --> 02:56.800
So the lambda is going to return,

02:56.800 --> 02:58.210
"Hello, students".

03:00.680 --> 03:07.970
And similarly, when we have "greet" with argument "James", the argument will be James, the salutation

03:07.970 --> 03:08.600
will be hello.

03:08.870 --> 03:10.630
So this will say "Hello, James".

03:11.750 --> 03:18.050
Partial evaluation is a functional programming technique, in which data is evaluated in stages. As an example,

03:18.050 --> 03:19.700
if you open a large document

03:20.360 --> 03:26.360
The obvious approach is for the document viewer to format the entire document before you can start

03:26.360 --> 03:26.900
reading it.

03:27.740 --> 03:32.450
If you have a 700 page document and you only want to look at the first two pages, that could be

03:32.450 --> 03:33.920
quite a long time.

03:34.910 --> 03:39.710
A better solution is to format each page as the user looks at it.

03:40.130 --> 03:42.710
So when you open the document, it will format page one.

03:43.310 --> 03:46.190
Then when you go to page two, it will the format page two and display it.

03:46.610 --> 03:47.570
And that will be much quicker.

03:47.990 --> 03:55.190
And if you jump to Page 537, it will just process Page 537 and ignore all the intervening pages.

03:56.840 --> 04:03.170
Another example is if you get data in stages and you can process each bit of data independently.

04:03.650 --> 04:09.170
So if you get some data, and you wait, and you get some data, and you wait, and you get some more data and

04:09.170 --> 04:15.500
you wait. And then you process it all at the end. So all that waiting time was wasted.

04:15.770 --> 04:21.320
Alternatively, you could get some data. Process it. Then get some more data, then attach it to what

04:21.320 --> 04:24.890
you have already processed. Then get some more data, process that, and so on.

04:25.580 --> 04:27.800
Obviously, this will not work for every problem.

04:28.160 --> 04:30.950
But if you can do it, it will speed things up, quite a lot.

04:32.360 --> 04:38.210
So, partial evaluation reduces computation, and it can make processing simpler. And there are lots of

04:38.210 --> 04:43.760
applications where it is used: artificial intelligence, database query engines, scientific computing and

04:43.760 --> 04:49.880
so on. And we can implement partial evaluation by using lambda expressions.

04:50.720 --> 04:52.610
In fact, we have already done that.

04:53.060 --> 04:58.010
With this lambda expression, we have a function which has processed the salutation,

04:58.250 --> 04:58.670
"Hello".

04:59.240 --> 05:02.600
And then it just processes the name of the person as it gets it.

05:03.470 --> 05:05.870
So "greeter" performs a partial evaluation.

05:06.320 --> 05:10.160
So the important point is that it only needs to process the salutation once.

05:10.580 --> 05:15.680
And then, once it has done that, the code which processes the person's name does not need to know what

05:15.680 --> 05:18.350
the salutation is, or how it is processed, or where to get it.

05:21.620 --> 05:26.180
If we want a different salutation, then we call "greet" again with a different argument.

05:26.990 --> 05:30.320
So this time we are going to say "Good morning" and then the person's name.

05:31.130 --> 05:37.730
So here is the code again, with our function returning lamba. And we are greeting with "hello".

05:38.480 --> 05:40.760
Then we can call this again with a different argument.

05:40.760 --> 05:42.470
So that is now going to bind

05:42.620 --> 05:44.060
"Good morning" to the salutation.

05:44.780 --> 05:51.620
So if we call this "greet_formal" with someone's name, it is going to say "Good morning" and then the person's

05:51.620 --> 05:51.950
name.

05:54.130 --> 05:54.910
So there we are.

05:55.090 --> 05:55.670
"Good morning,

05:55.870 --> 05:56.550
Dr Stroustrup"

05:59.510 --> 06:05.000
And finally, one thing you need to be careful about when you are using stored lambda expressions, if

06:05.000 --> 06:10.340
they capture by reference. You probably know that returning a reference to a local variable

06:10.340 --> 06:11.210
is dangerous.

06:11.690 --> 06:18.320
If we have something like this, so we return a variable which is local by reference. That variable

06:18.320 --> 06:20.120
will be destroyed when the function returns.

06:20.510 --> 06:23.330
And then you have a reference which is referring to nothing.

06:23.900 --> 06:27.170
So that will probably crash the program if you try to use it.

06:28.950 --> 06:33.900
When we have a lambda which captures by reference, that will have a reference to the captured variable.

06:34.980 --> 06:38.640
If we call this lambda after the captured varaible has gone out of scope,

06:39.060 --> 06:40.920
then we have the same dangling reference problem.

06:41.880 --> 06:48.150
So if you are using capture by reference, make sure that the captured variable is still valid when you

06:48.150 --> 06:48.910
call the lambda.

06:49.800 --> 06:51.360
Okay, so that is it for this video.

06:51.840 --> 06:55.380
I'll see you next time, but meanwhile, keep coding!
