COS 333: Chapter 15, Part 2

Willem van Heerden
31 Aug 202053:59

Summary

TLDRThis lecture delves into functional programming with Scheme, focusing on numeric predicate functions, control flow, and list operations. It explains how predicate functions return boolean values and introduces control structures like 'if' and 'cond' in Scheme. The lecture also covers list manipulation functions such as 'quote', 'car', 'cdr', and 'cons', highlighting their importance in functional programming. Additionally, it discusses recursion as the method for repetition in Scheme, due to its lack of loop support, and provides an example of implementing the 'member' function recursively to check for an element within a list.

Takeaways

  • πŸ“š The lecture continues the discussion on functional programming with a focus on the Scheme programming language, building upon concepts introduced in the previous lecture.
  • πŸ”’ Introduction to numeric predicate functions in Scheme, which are functions that return a boolean result and operate only on numeric literal values, not symbolic literals.
  • 🌐 Scheme's approach to control flow is explored, highlighting the absence of traditional loops due to its pure functional nature and the use of recursion for repetition.
  • πŸ“ˆ The 'if' function in Scheme is detailed, emphasizing its role as a selector function akin to 'if' statements in imperative languages, with a unique application to three parameters based on a predicate result.
  • πŸ”„ The 'cond' function is presented as Scheme's method for multiple selections, similar to a 'switch' statement, evaluating predicates in sequence until one is found to be true.
  • πŸ“ The importance of list operations in Scheme is underscored, with functions like 'quote', 'list', 'car', 'cdr', and 'cons' being fundamental for list manipulation.
  • πŸ”‘ The 'quote' function is explained as a means to prevent evaluation of list parameters, allowing them to be treated as literal data rather than function applications.
  • πŸ” Predicate functions for lists, such as 'list?' and 'null?', are introduced as essential for determining the nature and state of list structures within Scheme programs.
  • πŸ”— The 'cons' function is described as a critical tool for constructing lists, allowing for the creation of new lists with elements added in a specific order.
  • βš–οΈ The 'eqv?' and 'eq?' functions are contrasted, with 'eqv?' performing value comparisons and 'eq?' performing pointer comparisons, affecting how equivalence is determined.
  • πŸ› οΈ The 'let' function is introduced as a way to create local bindings within Scheme, allowing for the evaluation of expressions in the context of these bindings.

Q & A

  • What is the main focus of the second lecture on Chapter 15?

    -The main focus of the second lecture is to continue the discussion on Scheme programming language within the context of functional programming, specifically covering numeric predicate functions, control flow, and list operations.

  • What is a predicate function in Scheme?

    -A predicate function in Scheme is a function that produces a boolean result, either true or false.

  • How does Scheme represent true and false values?

    -In Scheme, 't' represents a true value, '#f' represents a false value, and an empty list is also interpreted as false, while a non-empty list is interpreted as true.

  • What are some of the numeric comparison functions provided by Scheme?

    -Scheme provides comparison functions such as 'equals', 'less than', 'greater than', 'not equals', 'greater than or equal to', and 'less than or equal to'.

  • How does Scheme handle control flow for selection and loops?

    -Scheme handles control flow using functions like 'if' for two-way selection and 'cond' for multiple selections. It does not provide loop structures because it is a pure functional programming language without support for variables, so repetition is handled through recursion.

  • What is the purpose of the 'quote' function in Scheme?

    -The 'quote' function in Scheme is used to avoid parameter evaluation, ensuring that lists are treated as literal data rather than function applications.

  • What are the 'car' and 'cdr' functions in Scheme used for?

    -The 'car' function is used to access the first element of a list, and the 'cdr' function is used to access the rest of the list after the first element.

  • What is the 'cons' function in Scheme and how is it used?

    -The 'cons' function in Scheme is used to construct a new list by placing its first parameter as the first element and appending the elements of the second parameter list to it.

  • What is the 'let' function in Scheme and how does it work?

    -The 'let' function in Scheme is used to create local bindings for names with associated expressions. It evaluates the expressions, binds the values to the names, and then evaluates the body using these bindings.

  • Can you provide an example of a recursive function in Scheme?

    -The 'member' function is an example of a recursive function in Scheme. It checks if an atom is contained within a list by recursively comparing the atom to each element of the list and eliminating one element at a time until a match is found or the list is empty.

  • What is the importance of the order of base cases and recursive case in a 'cond' expression in Scheme?

    -The order in a 'cond' expression is important to ensure that base cases are checked first before the recursive case to avoid non-terminating recursion. Additionally, checking for an empty list with 'null' should come before checking for equality to prevent unnecessary evaluations.

Outlines

00:00

πŸ“š Introduction to Scheme Programming and Functional Concepts

This paragraph introduces the second lecture on Chapter 15, focusing on functional programming with the Scheme programming language. The lecture builds on general functional programming concepts and the Lisp language, then delves into Scheme's basic numeric functions, custom function definitions, and output functions. The speaker outlines the lecture's agenda, which includes numeric predicate functions, control flow, list operations, and implementing a Scheme function from scratch. Predicate functions in Scheme are discussed, highlighting their boolean results and the use of 't' for true and '#f' for false, with lists also playing a significant role in boolean logic.

05:02

πŸ”’ Scheme's Numeric Predicate Functions and Control Flow

The paragraph explains Scheme's numeric predicate functions, which are used for comparisons and include 'equals', 'less than', 'greater than', and others. It emphasizes that these functions operate on numeric literals and not symbolic literals. The paragraph also covers Scheme's control flow, noting the absence of traditional loops due to the language's pure functional nature. Instead, Scheme uses 'if' for two-way selection and 'cond' for multiple selections, with examples provided to illustrate their usage. A divide function example demonstrates the application of 'if' in handling potential division by zero errors.

10:04

πŸ”„ Scheme's List Operations and Recursion

This section discusses list operations in Scheme, a core data structure in the language. It introduces the 'quote' function, which prevents evaluation of lists as function applications. The 'list' function is explained for constructing new lists, and the 'car' and 'cdr' functions are described for accessing the first element and the rest of the list, respectively. The paragraph also touches on the immutability of lists in Scheme and the importance of proper list processing to avoid errors, especially when reaching the end of a list.

15:06

πŸ”§ List Manipulation with 'cons', 'list?', 'null?', and 'eqv?'

The paragraph delves into the 'cons' function, which creates new lists by adding an element to the front of a list without modifying the original list. It also introduces the 'list?' predicate function for checking if a parameter is a list and the 'null?' function for checking if a list is empty. Additionally, the 'eqv?' function is explained for testing the equivalence of two values, applicable to both symbolic and numeric atoms, but not for lists. The importance of using 'eqv?' over 'eq?' for value comparison is highlighted, especially when dealing with different data types like floats and integers.

20:08

πŸ“ The 'let' Function and Recursive List Operations in Scheme

This paragraph introduces the 'let' function in Scheme, which allows for local binding of names to expressions within its body. It provides examples of using 'let' to perform computations with bound names and emphasizes that these names are not variables in the traditional sense, as their values are bound only once. The paragraph also presents the 'member' function, an example of a Scheme function implemented recursively to search for an atom within a simple list. The recursive nature of the 'member' function is explained, along with its base cases for an empty list and when the atom is found, and the recursive case for continuing the search through the list.

25:11

πŸ” Recursive Function Implementation and 'cond' Order Importance

The final paragraph concludes the lecture by summarizing the recursive implementation of the 'member' function, which checks for the presence of an atom in a list. It discusses the importance of the order of cases in the 'cond' function, where base cases must precede the recursive case to ensure termination of the recursion. The paragraph also highlights the significance of performing the 'null?' check before the equivalence check to avoid unnecessary evaluations. The speaker invites the audience to consider the consequences of reversing these orders and ends the lecture with a preview of upcoming topics on recursive functions and list operations.

Mindmap

Keywords

πŸ’‘Functional Programming

Functional programming is a programming paradigm that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data. In the video, this concept is central as the lecture delves into Scheme, a functional programming language, discussing its features and how it differs from imperative programming languages.

πŸ’‘Scheme Programming Language

Scheme is a minimalist, general-purpose, multi-paradigm programming language that supports functional programming. The script focuses on Scheme, exploring its functions, control flow, and list operations, which are fundamental to understanding the language's capabilities and usage.

πŸ’‘Predicate Functions

Predicate functions are functions that return a Boolean value, typically used to test certain conditions. In the script, numeric predicate functions in Scheme are discussed, which are crucial for understanding how to perform comparisons and tests within the language.

πŸ’‘Control Flow

Control flow refers to the order in which individual statements, instructions, or function calls are executed or evaluated. The script explains how Scheme handles control flow without traditional loop constructs, instead using recursion and functions like 'if' and 'cond' for decision-making.

πŸ’‘List Operations

List operations are functions that manipulate list data structures. The script discusses Scheme's functions for performing operations on lists, such as 'car', 'cdr', and 'cons', which are essential for working with this core data structure in Scheme.

πŸ’‘Recursion

Recursion is a method of solving problems where a function calls itself as a subroutine. Since Scheme does not support loops, recursion is used for repetition and is highlighted in the script as a fundamental concept for implementing iterative processes in Scheme.

πŸ’‘Quote Function

The 'quote' function in Scheme is used to prevent the evaluation of expressions, allowing the programmer to treat lists as data rather than code. The script explains its importance in avoiding the interpreter's default behavior of evaluating all parameters.

πŸ’‘Cons Function

The 'cons' function in Scheme is used to create a new list by adding an element to the front of an existing list. The script illustrates how 'cons' is used in list construction and emphasizes the importance of using it with lists as the second parameter.

πŸ’‘Car and Cdr Functions

In Scheme, 'car' and 'cdr' are functions that access the first element and the rest of the elements in a list, respectively. The script explains how these functions are used to manipulate and access list elements, which is crucial for understanding list processing in Scheme.

πŸ’‘Let Function

The 'let' function in Scheme is used to create local bindings of identifiers to expressions, allowing for the creation of block-scoped variables. The script demonstrates how 'let' is used to bind names to values for a limited scope within a function body.

πŸ’‘Member Function

The 'member' function is a Scheme function discussed in the script that checks if an element is part of a list. It illustrates the use of recursion in Scheme to perform operations that would typically require loops in other languages.

Highlights

Introduction to the second lecture on Chapter 15 focusing on functional programming and the Scheme programming language.

Continuation from the previous lecture, emphasizing basic numeric functions and defining custom functions in Scheme.

Explanation of numeric predicate functions in Scheme that return boolean results.

Discussion on Scheme's handling of control flow without traditional loops, utilizing recursion instead.

Introduction of the 'if' function in Scheme, acting as a selector similar to 'if' statements in imperative languages.

Use of the 'cond' function for multiple selections, analogous to switch statements.

Coverage of list operations, fundamental to Scheme as a core data structure, with functions like 'quote', 'list', 'car', 'cdr', and 'cons'.

The 'quote' function's role in avoiding parameter evaluation, with shorthand notation using an apostrophe.

Description of the 'car' and 'cdr' functions for accessing list elements and their implications for list processing.

The 'cons' function for constructing new lists and its importance in recursive list processing.

Predicate functions for lists, such as 'list?' for checking if a parameter is a list and 'null?' for checking if a list is empty.

Differences between 'eqv?' and 'eq?' functions for testing value and object equivalence, respectively.

The 'let' function for local bindings within Scheme and its evaluation process.

Recursive implementation of the 'member' function to search for an atom within a simple list.

Importance of the order of base and recursive cases in recursive functions to prevent non-terminating recursion.

The significance of checking for an empty list before performing equality checks in recursive list processing.

Conclusion and preview of the next lecture, which will cover more examples of recursive functions on lists.

Transcripts

play00:01

this is the second lecture on

play00:03

chapter 15 where we'll continue with our

play00:05

discussion

play00:06

on functional programming specifically

play00:09

once again focusing on the scheme

play00:12

programming language

play00:16

in the previous lecture we covered some

play00:19

underlying concepts related to

play00:21

functional programming in general and we

play00:24

briefly touched on the

play00:26

lisp programming language we then moved

play00:29

on to the main focus

play00:31

of these lectures which is the scheme

play00:34

programming language

play00:36

we looked at some basic numeric

play00:38

functions which we can use to perform

play00:40

arithmetic operations then we looked at

play00:43

how to define

play00:45

our own functions and we finished off by

play00:48

looking

play00:48

at some basic output functions

play00:51

in this lecture we'll continue with our

play00:53

discussion on scheme

play00:55

starting off with some coverage

play00:59

on numeric predicate functions a

play01:02

predicate function in scheme

play01:04

is essentially a function that produces

play01:07

a boolean result we'll then take a look

play01:11

at how scheme deals with control flow

play01:14

after which we'll move on to some

play01:16

functions that perform operations

play01:18

on lists these functions are very

play01:22

important

play01:22

for some of the function implementation

play01:26

examples that we'll look at right

play01:28

towards the end of this lecture

play01:29

and in the next lecture

play01:32

we'll then look at some predicate

play01:34

functions for

play01:36

lists after which we'll look at some

play01:38

additional predicate

play01:40

functions that are used to test for

play01:42

equivalence between values

play01:44

we'll then look at the let function and

play01:48

finally we'll look at our first example

play01:51

scheme function which we will implement

play01:54

from scratch

play01:58

we'll start by discussing numeric

play02:00

predicate functions

play02:01

in scheme but before we can do this we

play02:04

need to talk about

play02:05

predicate functions in general and how

play02:08

they are handled within scheme

play02:10

so as i've previously mentioned the

play02:11

predicate function is simply a function

play02:14

that defines

play02:14

a boolean value in other words a true or

play02:18

a false value now of course scheme also

play02:21

provides literal boolean

play02:22

values so in scheme t is

play02:26

a true value and hash f is

play02:29

a false value an empty listing scheme is

play02:33

also interpreted as a false value

play02:35

and then a non-empty list in other words

play02:38

a non-null list

play02:40

is interpreted as a true value now

play02:43

in general we won't be using these list

play02:45

conventions

play02:46

we will in most cases be using t

play02:50

or hash f to represent a literal boolean

play02:54

value

play02:55

so scheme provides a number of built-in

play02:57

predicate functions

play02:58

for numbers and it's important to

play03:00

understand that all of these functions

play03:02

work

play03:03

only for numeric literal values not

play03:06

for symbolic literals

play03:09

so we have then a number of comparison

play03:13

functions

play03:13

and again please note that as with

play03:16

everything in scheme

play03:17

these are functions they are not

play03:19

operators

play03:20

as they would be in an imperative

play03:23

programming language

play03:24

such as c plus so we have the equals

play03:27

function

play03:28

which tests for equality of its

play03:30

parameters

play03:32

less than greater than which is the not

play03:35

equals function

play03:36

greater than less than greater than or

play03:39

equal to

play03:40

and less than or equal to

play03:43

so these functions should be applied to

play03:46

at least

play03:46

two numeric parameters but they can also

play03:48

be applied to

play03:50

more parameters so for example if we

play03:53

were to apply

play03:54

the equals function to five parameters

play03:57

then all five of those parameters would

play04:00

have to have equal values

play04:02

for the equals function to define

play04:05

a true value in a similar fashion the

play04:08

not equals function if it were applied

play04:11

to

play04:12

multiple parameters would then require

play04:14

all of those parameters to not be equal

play04:16

to each other

play04:18

in order to define a true value

play04:21

if we look at the remaining

play04:25

inequalities so for example let's look

play04:27

at

play04:28

the less than function if that were to

play04:31

be

play04:31

applied to multiple parameters then it

play04:34

would mean that the first parameter has

play04:36

to be less than

play04:37

all of the remaining parameters the

play04:40

second parameter would have to be

play04:43

greater than the first parameter but

play04:45

less than all of the remaining

play04:46

parameters

play04:47

and so on and so forth we also then

play04:51

have four functions that are applied to

play04:55

a single numeric value we have even

play04:58

which of course produces a true value if

play05:02

the parameter is even

play05:03

odd does the same thing but for odd

play05:06

values

play05:07

0 will define a true value if the

play05:10

parameter

play05:11

is a zero value and negative will

play05:14

produce a true value if the parameter is

play05:17

a negative value also note the question

play05:20

marks at the end

play05:22

of the function names there

play05:25

now scheme also provides a not function

play05:29

and this is applied to a boolean

play05:31

parameter

play05:32

and it simply inverts the logic

play05:36

of the boolean expression that is its

play05:39

parameter

play05:40

so if the parameter evaluates to

play05:44

a true value then not will produce a

play05:48

false result and vice versa

play05:52

we'll now move on to control flow and

play05:55

how it is handled in

play05:56

scheme so scheme only provides functions

play06:00

for selectors in other words

play06:04

functions that operate like if

play06:06

statements

play06:07

or switch statements in an imperative

play06:10

language

play06:11

scheme does not provide any support for

play06:14

loops

play06:14

because as we've previously seen loops

play06:17

require loop control variables

play06:19

and scheme is a pure functional

play06:21

programming language

play06:22

which means that it doesn't support any

play06:25

variables

play06:26

at all so first of all scheme provides

play06:30

an

play06:30

if function and once again note that

play06:33

if is a function it is

play06:36

not a statement as it would be in an

play06:39

imperative programming language

play06:42

so the if function then is used

play06:45

for two-way selections and we

play06:48

apply then the if function to

play06:51

three parameters the first parameter is

play06:54

a predicate

play06:56

so it must evaluate to a value which is

play07:00

either true or false and then we have

play07:02

two expressions the then expression

play07:05

and the else expression and these

play07:08

are then evaluated as you would expect

play07:11

in a regular if statement so if the

play07:14

predicate

play07:14

is true then the entire if function

play07:18

evaluates to the value of the

play07:21

then expression if the predicate is

play07:23

false

play07:24

then the entire if function evaluates

play07:27

to the value of the else expression

play07:31

so here's an example then of how we

play07:34

would use

play07:34

the if function first of all we have

play07:38

this define function application and as

play07:41

we've seen in the previous lecture

play07:43

this simply creates an

play07:46

unnamed function and then binds a name

play07:49

to that function

play07:50

so we are creating a function called

play07:53

divide

play07:54

and it is applied to two parameters the

play07:56

first parameter is called numa

play07:58

and the second parameter is called denom

play08:02

then we have the body of the function

play08:05

and this is then

play08:06

a single application of the if function

play08:10

and here we have our predicate so here

play08:13

we've used then

play08:14

the basic not equals function

play08:17

which is applied to two numeric

play08:21

parameters firstly there's the nom

play08:24

and then the second parameter is zero

play08:28

so this will then only evaluate to true

play08:32

if the denominator is not equivalent

play08:35

to zero if this is the case then we get

play08:39

to our then expression and we can see

play08:42

that this is another

play08:44

function application in this case the

play08:46

divide

play08:47

function which is then applied to two

play08:49

parameters

play08:50

numa and denom so this will then

play08:53

actually

play08:54

perform the division between the two

play08:56

values

play08:57

and then the entire if function will

play09:00

evaluate to the result

play09:02

of that division which means that the

play09:05

application of our divide function will

play09:07

then also

play09:08

evaluate to the result of that division

play09:11

however

play09:12

if our predicate is false

play09:16

in other words the denominator is

play09:19

zero then in that case we have a

play09:21

situation where the division would have

play09:23

produced an

play09:23

error so in that case we then move on to

play09:27

our else expression over here

play09:29

and we can see that our else expression

play09:31

is a simple value

play09:33

in this case a numeric atom

play09:36

which is zero so in other words if the

play09:40

predicate

play09:41

then evaluates to false then the entire

play09:45

if function

play09:46

will evaluate to zero which means the

play09:48

application

play09:50

of our divide function will also then

play09:53

evaluate to 0. so in other words what

play09:56

we've done is we have defined a function

play09:58

that receives two parameters

play10:00

and if the denominator is zero

play10:04

then the function will evaluate to zero

play10:07

if

play10:07

it is not zero then the first parameter

play10:11

is divided by

play10:12

the second parameter

play10:16

scheme also provides support for

play10:18

multiple selection

play10:19

by means of the conf function and the

play10:22

operation of the khan function

play10:24

is similar to that of a switch statement

play10:27

in an imperative programming language

play10:31

so over here we can see the syntax of

play10:34

the

play10:34

cond function we can see that cond is

play10:37

applied

play10:37

to a number of parameters and the number

play10:40

of parameters is not fixed so we can

play10:42

have

play10:42

any number of parameters where each

play10:46

parameter then

play10:47

is a pair the first

play10:50

part of the pair is a predicate so

play10:54

this must be something that evaluates to

play10:57

a true or a false value and then

play11:00

following the predicate

play11:01

we have an expression so the semantics

play11:05

for a cond then involves the evaluation

play11:09

of each predicate from top to bottom

play11:13

and once the first predicate is found

play11:16

that evaluates to true

play11:18

then its accompanying expression is

play11:21

evaluated

play11:22

and then the result of that expression

play11:25

becomes the result

play11:27

of the cond function now of course it's

play11:30

possible

play11:31

that none of the predicates may be true

play11:34

and in this case we then have the

play11:37

optional else expression at the bottom

play11:39

here

play11:40

so here we have then else which is

play11:43

essentially a predicate that is

play11:45

always true and then we have an

play11:47

expression that

play11:48

is paired with that so in a situation

play11:52

where

play11:52

none of the predicates are true then the

play11:56

expression

play11:56

paired with the else will be evaluated

play12:00

and then the result of that expression

play12:02

becomes the result

play12:04

of the cond if no else

play12:07

is provided and none of the predicates

play12:10

evaluate

play12:10

to true then the result is unspecified

play12:14

and this generally then causes an error

play12:18

so in general we will always provide

play12:21

an else expression in

play12:24

any con that we implement within scheme

play12:30

so we've looked at selection and

play12:32

multiple selection in scheme

play12:34

the last remaining kind of control flow

play12:37

is repetition

play12:38

which in an imperative programming

play12:40

language is handled by means

play12:42

of a loop now as we've seen scheme is a

play12:46

pure functional programming language so

play12:47

this means

play12:48

it does not support variables in any

play12:51

form

play12:52

and therefore as a result we can't have

play12:54

loop control variables

play12:55

and therefore we can't have loop

play12:57

structures

play12:58

so because of this scheme does not

play13:01

provide

play13:02

any kind of loop function instead all

play13:06

repetition is handled by means

play13:08

of recursion and we'll see this in terms

play13:12

of

play13:12

the example function implementations

play13:14

that will get to

play13:16

at the end of this lecture and then the

play13:18

beginning of the next lecture

play13:23

we've so far looked at numeric predicate

play13:26

functions

play13:26

and how scheme deals with program

play13:29

control flow

play13:31

we'll now move on to functions that

play13:33

perform

play13:34

operations on lists and as we've seen

play13:37

previously

play13:38

lists are a core data structure within

play13:41

the scheme programming language

play13:42

and therefore functions that perform

play13:45

operations on these lists

play13:47

are crucial now the first function that

play13:50

we'll look at is a primitive function

play13:52

provided

play13:53

by scheme and this function is called

play13:56

quote

play13:57

for us to understand how the quote

play13:58

function works we need to look back at

play14:01

two

play14:02

concepts that we discussed in the

play14:04

previous lecture

play14:06

so first of all we need to look at the

play14:08

scheme interpreter and how it deals with

play14:10

function

play14:11

applications and what we saw in the

play14:13

previous lecture

play14:14

was that the scheme interpreter will

play14:17

always

play14:17

evaluate all of the parameters for

play14:20

a function application

play14:24

next we need to remember that lists and

play14:27

functions have exactly the same form in

play14:29

scheme

play14:31

so over here we have a list that

play14:33

contains

play14:34

three symbolic atoms as elements

play14:37

and these symbolic atoms are a b and c

play14:42

over here we have a function application

play14:45

so this is an application of the member

play14:46

function

play14:47

to two parameters the symbolic atom a

play14:50

and something called list

play14:54

now as far as scheme is concerned it

play14:57

can't

play14:58

tell these two apart from each other so

play15:01

it is equally valid to interpret this

play15:03

list as the application of a function

play15:06

called

play15:06

a to two parameters b and c

play15:10

and we can also then interpret this

play15:12

function application

play15:13

as a list that contains three elements

play15:15

member

play15:16

a and list

play15:20

so what this means then is that whenever

play15:23

we want

play15:24

to use a list as a parameter

play15:28

for a function application then we have

play15:31

to

play15:31

prevent the scheme interpreter from

play15:34

evaluating this list as a

play15:38

function application

play15:41

so this is then where the quote

play15:43

primitive function

play15:44

comes in so the quote primitive function

play15:46

is used

play15:48

to avoid parameter evaluation whenever

play15:50

it

play15:51

is not appropriate the quote function

play15:54

takes one parameter and it returns this

play15:57

parameter

play15:57

without any evaluation

play16:00

so here we then have an example let's

play16:03

look at the scheme code over here

play16:06

and this is then an application of the

play16:09

foo function to a parameter

play16:13

which is a list containing three

play16:15

elements

play16:16

a b and c now

play16:19

if we were to provide the code as it

play16:22

appears

play16:22

here to the scheme interpreter then the

play16:24

scheme interpreter will first

play16:26

try to evaluate the parameter of the

play16:29

foo function and this evaluation will

play16:32

then involve the application of a

play16:33

function called

play16:34

a to two parameters b and c

play16:38

now what will normally happen in this

play16:39

case is that a function called a

play16:41

does not exist and therefore the

play16:44

interpreter will produce an

play16:45

error saying that you are attempting to

play16:48

apply a function

play16:50

that it doesn't recognize so instead

play16:53

we should then use the quote function as

play16:56

we see over here

play16:58

so once again we are then applying the

play17:01

foo function to one parameter but now we

play17:04

are applying the quote function

play17:07

to this list over here and what this

play17:10

will do then

play17:11

is it will ensure that the list is not

play17:14

evaluated by the interpreter

play17:16

and therefore it's dealt with simply as

play17:18

a list that contains

play17:19

three elements and that will then be

play17:23

the parameter to which the foo

play17:26

function will be applied now of course

play17:29

it's quite a mouthful and a lot of

play17:32

typing to

play17:33

have to provide the full

play17:36

function name quote every time that we

play17:39

want to do this and of course we will be

play17:41

using the quote function fairly

play17:43

regularly in our programs

play17:46

so as a result of this scheme then

play17:49

provides a

play17:50

shorthand notation so the application

play17:53

then of the quote function

play17:55

to a parameter in this case a list

play17:58

containing two elements a and b

play18:00

can be abbreviated as you can see over

play18:03

there with a single apostrophe

play18:05

and then followed by the list

play18:08

so in general from this point on we will

play18:10

be using this

play18:12

shorthand apostrophe notation rather

play18:14

than

play18:15

the full function name quote

play18:21

next we have the list function and the

play18:24

list function

play18:25

is used to construct a new list

play18:28

so the list function is applied to any

play18:30

number of parameters

play18:32

and then it simply constructs a new list

play18:36

and it places the parameters in order

play18:39

into the list as elements so over here

play18:42

we have two examples

play18:44

of the use of the list function over

play18:47

here

play18:47

we are applying list to three parameters

play18:51

and these parameters are symbolic atoms

play18:55

now notice that we have quoted each of

play18:58

those parameters

play19:00

and the reason that we have to quote

play19:02

them is that if we don't

play19:04

they are interpreted as names

play19:07

so a name would have been bound using

play19:10

the define function which we discussed

play19:13

in the previous lecture

play19:15

of course a b and c have probably

play19:18

not been defined as names with values

play19:22

bound to them

play19:23

and therefore the scheme interpreter

play19:26

would

play19:26

again report an error so if we

play19:29

then quote these symbolic atoms it means

play19:33

they shouldn't be

play19:34

evaluated in other words they shouldn't

play19:36

be interpreted as names

play19:38

which means that they are then simply

play19:40

placed into

play19:41

the new list which is yielded by the

play19:44

application of the list function

play19:47

so the result of this thing would be a

play19:48

list containing the

play19:50

three symbolic atoms a b and c

play19:54

over here we have another use of the

play19:56

list function so again

play19:58

we've applied this function to three

play20:01

parameters the first two parameters

play20:04

are again symbolic atoms and once again

play20:07

they

play20:08

need to be quoted then the third

play20:11

parameter

play20:12

is a list and of course we have to quote

play20:14

this list otherwise

play20:15

it will be interpreted as a function

play20:18

application

play20:19

of a function named c which is not

play20:22

defined

play20:23

so this thing yields the following list

play20:26

over here so we can see that we have

play20:28

then two symbolic

play20:29

atoms as the first and second elements

play20:33

within this list

play20:34

and the third element is then a nested

play20:37

list next we have two very important

play20:42

list functions

play20:43

the first is named car which is usually

play20:46

pronounced

play20:47

car the second is named cdr which is

play20:50

usually pronounced

play20:51

cuda or kada i will be saying kuda for

play20:55

the remainder of this lecture

play20:56

and the next lecture now as to where the

play21:00

names of these functions come from they

play21:02

are

play21:02

abbreviations that refer to hardware

play21:05

related details

play21:06

for the ibm 704 computer which the lisp

play21:10

programming language was

play21:11

first designed to run on so

play21:14

car stands for content of address part

play21:18

of register

play21:19

and cdr stands for content of decrement

play21:22

part

play21:23

of register now these details are not

play21:25

important for our purposes

play21:27

i simply mentioned them in case you are

play21:29

curious as to where the function names

play21:31

come from

play21:33

so the car and cuda functions are both

play21:36

applied to a single parameter

play21:38

which should be a list the car function

play21:42

yields the first element of its list

play21:45

parameter

play21:47

so over here we have a couple of

play21:49

applications

play21:50

of the car function here we have car

play21:53

applied to a simple list that contains

play21:56

three symbolic atoms

play21:58

note that we have to quote this list and

play22:01

this is to prevent the scheme

play22:02

interpreter

play22:03

from evaluating the list as though it

play22:06

were

play22:07

a function application so the result

play22:10

then

play22:11

of applying card to this list is the

play22:14

symbolic atom a

play22:15

which as we can see is the first element

play22:18

contained within this list

play22:20

note that we are not yielding

play22:24

a pointer or a reference into the

play22:26

parameter list

play22:28

this is an entirely separate value that

play22:31

is produced as a result

play22:34

over here we have an application of the

play22:36

car function to another list but this is

play22:38

a slightly more complex list

play22:40

the first element in the list is a sub

play22:42

list or a nested list

play22:45

so the result of this application then

play22:48

is

play22:48

a list containing the symbolic

play22:51

atoms a and b and that is because the

play22:55

first element in the parameter list is a

play22:57

list containing the symbolic atoms a

play22:59

and b now notice that applying the car

play23:03

function to anything

play23:04

that is not a list in this case a

play23:08

symbolic atom will produce an error

play23:12

also if we apply a card to an empty list

play23:15

then an error will be produced

play23:18

next we have the cuda function and the

play23:20

cuda function

play23:21

performs essentially the opposite

play23:24

operation

play23:25

to the car function so cuda

play23:28

yields the remainder of its list

play23:30

parameter after the first

play23:32

element has been removed again it's

play23:35

important to understand

play23:37

that an entirely new list is produced as

play23:40

a result

play23:41

it's not a reference into the parameter

play23:45

list also important to

play23:48

note at this point is that lists in

play23:51

scheme

play23:51

are immutable so in other words

play23:54

we are not modifying the parameter list

play23:57

we are returning an entirely new list as

play24:00

a result

play24:02

so over here we have then a couple of

play24:04

applications of the cuda function

play24:06

here we are applying cuda to the

play24:09

list containing three symbolic atoms a b

play24:13

and c

play24:14

so we then ignore the first element a

play24:17

and

play24:18

only consider the tail elements b and c

play24:21

and we then yield a list containing

play24:24

those

play24:24

elements so the result then is a list

play24:27

containing the symbolic atoms

play24:29

b and c here we have cuda applied

play24:32

to a more complex list and again the

play24:35

first element

play24:36

is a sub list and then that is followed

play24:39

by two symbolic atoms

play24:41

c and d as we can see there so we then

play24:44

discard the first element which is the

play24:47

nested list

play24:48

and therefore the result is a list

play24:50

containing the two symbolic atoms

play24:53

c and d now interestingly if we apply

play24:57

cuda to a list that contains a single

play25:00

element

play25:00

then the result is an empty list

play25:04

so in other words we should think of

play25:06

lists

play25:07

in scheme as a sequence of

play25:10

elements and then implicitly there is a

play25:13

last element which

play25:14

is an empty list now

play25:18

this kind of behavior may seem strange

play25:20

but

play25:21

it's very useful in determining when we

play25:24

have processed

play25:25

through all of the elements in a list

play25:28

parameter

play25:30

so typically what will happen is in

play25:33

a function that processes a list we will

play25:36

discard

play25:37

elements from the list one by one and we

play25:40

can then determine once we've

play25:41

reached the last element in the list

play25:45

when a cuda on that list yields an

play25:48

empty list as a result this means then

play25:51

we have one single element

play25:53

left to process now also

play25:57

as with the car function if we apply

play25:59

cuda to anything other than a list in

play26:01

this case

play26:02

a symbolic atom then an error will be

play26:04

produced

play26:05

and also if we apply cuda to an empty

play26:08

list

play26:09

then an error will be produced now this

play26:11

last example is quite important

play26:13

because it is the source of quite

play26:16

common errors related to list processing

play26:20

and so if you are processing through all

play26:23

of the elements within a list

play26:25

but then there's an error that is

play26:27

generated for the last element

play26:29

this generally means that the base case

play26:32

for your recursive processing of the

play26:34

list

play26:35

is incorrect you are eliminating the

play26:38

last element in the list

play26:39

and then still continuing to process

play26:42

beyond that

play26:45

the next list function we'll look at is

play26:48

the cons function

play26:49

and this is again a very important

play26:52

function which we will see

play26:54

used repeatedly in the coming examples

play26:58

so the cons function is applied to two

play27:00

parameters

play27:01

the first parameter can either be an

play27:03

atom or a list

play27:05

and then for our purposes the second

play27:07

parameter will be

play27:08

a list it is possible to use an atom

play27:12

as the second parameter but as we'll see

play27:15

this doesn't yield a result that is

play27:17

useful

play27:18

to us so the cons function

play27:22

then generates a new list

play27:25

and it places its first parameter into

play27:29

the new list as the first element and

play27:31

then the elements

play27:32

in the second parameter list are

play27:35

inserted as the remaining elements

play27:37

in the new list it's important to note

play27:40

that the result

play27:41

of the cons function application is

play27:44

a new list so we do not modify

play27:48

the second parameter list

play27:51

so over here we have some applications

play27:53

of the cons function

play27:56

here we are applying const to two

play27:58

parameters the first parameter is a

play28:00

symbolic atom

play28:01

a and the second parameter is a list

play28:04

containing the symbolic

play28:06

atoms b and c so over here we have then

play28:10

the result of this application of the

play28:12

cons

play28:13

function we can see that the symbolic

play28:15

atom a has been inserted as the first

play28:17

element

play28:18

and the remaining elements are the

play28:20

symbolic atoms b

play28:21

and c which were originally contained in

play28:24

the second parameter list

play28:27

here we have another application of the

play28:29

cons function this time to

play28:31

two lists and this is the result that is

play28:34

produced so

play28:35

notice that the first parameter which

play28:38

is a list is inserted as the

play28:42

first element within the resulting list

play28:45

so in other words we have created

play28:47

a complex list where the first element

play28:50

is

play28:50

a nested list note that c

play28:53

and d are the remaining elements within

play28:56

the list

play28:57

and those are the elements that were

play28:59

contained in

play29:00

the original second parameter list

play29:04

here we have an application of the cons

play29:06

function once again to two lists

play29:08

but the first parameter is an empty list

play29:11

and we see that the result produced then

play29:13

has the empty list as the first element

play29:17

within that list so this empty list in

play29:20

other words then is not discarded

play29:22

and ignored it is inserted as an element

play29:27

but this element is a list that contains

play29:30

nothing now notice

play29:33

that we then in general only use

play29:37

lists as the second parameter for

play29:40

the cons function here are

play29:43

examples where we apply the const

play29:46

function

play29:48

to a second parameter which is a

play29:51

symbolic

play29:52

atom so here we are are applying const

play29:55

to

play29:55

two symbolic atoms a and b and this

play29:59

yields what's referred to as

play30:01

a dotted pair and this is what the

play30:03

dotted pair looks like

play30:04

so we have a and b but we have this dot

play30:07

symbol

play30:08

between them so a dotted pair

play30:11

is not a normal list that we would

play30:14

generally

play30:15

work with in scheme a dotted pair

play30:19

is specifically a pair of

play30:22

two elements that are grouped together

play30:24

as a whole

play30:26

over here we have another application of

play30:28

the cons function

play30:29

this time again to two parameters where

play30:32

the first parameter is a list

play30:33

but the second parameter is a symbolic

play30:36

atom

play30:36

c in this case and again this yields a

play30:39

dotted pair because the second parameter

play30:42

is not a list and this is what the

play30:45

dotted pair looks like so in general

play30:48

if you are writing a function that

play30:51

constructs

play30:52

a list element by element and you see

play30:55

that the

play30:55

result has full stops contained in it

play30:59

it means in general that you are

play31:01

probably applying the

play31:03

cons function to some element that you

play31:07

are inserting

play31:08

into what you think is a list but the

play31:11

thing that you are inserting into is in

play31:13

fact

play31:14

an atom this is generally an incorrect

play31:18

result

play31:19

so you will need to debug your program

play31:22

in order to ensure that you are only

play31:26

applying cons

play31:26

to a second parameter which is a list

play31:30

in all cases

play31:33

we'll now move on to two predicate

play31:36

functions

play31:36

that are intended for lists

play31:40

the first predicate function is the list

play31:43

function

play31:44

note again that it has a question mark

play31:47

at the end of its name

play31:49

and the list function is applied

play31:52

to one parameter it yields

play31:55

a true result if the parameter is a list

play31:58

and a false result otherwise

play32:02

a more useful function for our purposes

play32:05

is the null function again note that it

play32:08

has a question mark

play32:09

at the end of its name and the null

play32:12

function

play32:12

is again applied to a single parameter

play32:16

and yields a true result if the

play32:18

parameter

play32:19

is an empty list otherwise it yields a

play32:23

false result now it's important to note

play32:26

that the parameter must be completely

play32:29

empty in order to yield

play32:31

a true result so if we

play32:34

apply null to a single empty list

play32:38

as we see over there then the result

play32:41

will be

play32:42

true however if we apply it to

play32:45

a list containing an empty list we are

play32:47

no longer applying it

play32:49

to an empty list and therefore the

play32:51

result

play32:52

will be false now this

play32:55

null predicate function is again fairly

play32:58

often used

play32:59

as a base case for recursive functions

play33:03

that perform processing

play33:05

on lists in general at least one of the

play33:09

base cases would be

play33:10

if the list is empty

play33:15

next we will look at two predicate

play33:18

functions

play33:19

that are used to test the equivalence of

play33:22

two things so first of all we have the

play33:25

eqv function notice the question mark at

play33:28

the end of the function

play33:30

name and this predicate function is

play33:33

applied to two parameters and yields

play33:37

a true result if the values of the two

play33:40

parameters

play33:41

are the same so note that this is a

play33:44

value comparison it's not a pointer or

play33:48

reference comparison so in other words

play33:50

we are testing to see where the two

play33:52

values are equivalent to each other not

play33:55

whether the two parameters

play33:56

are in fact the same object in memory

play34:00

now the eqv function works for both

play34:03

symbolic

play34:04

and numeric atoms so here we have some

play34:07

examples of how the eqv function would

play34:09

be used

play34:10

here we are applying the eqv function to

play34:14

two numeric atoms and the first

play34:17

is three and the second is three so of

play34:19

course the values of these two

play34:21

parameters

play34:22

are the same which means that eqv

play34:25

then yields a true result

play34:29

here we have an application of the eqv

play34:31

function

play34:32

again to two parameters which are atoms

play34:34

but in this case we have symbolic atoms

play34:37

we are comparing a to a and again these

play34:40

two values are the same

play34:42

so the result of this function

play34:44

application

play34:45

is true here we are again

play34:49

performing an eqv test on

play34:52

two symbolic atoms but here they are

play34:55

different so the first

play34:56

is a and the second is b of course

play35:00

here the two values are then different

play35:03

so

play35:03

the result of this function application

play35:05

is then false

play35:07

finally we have a more complex

play35:10

application of the eqv

play35:12

function so here the first parameter is

play35:15

the numeric atom 3.4 and the second

play35:19

parameter

play35:19

is a function application of the

play35:23

plus function the plus function is

play35:26

applied to two parameters

play35:28

these are both numeric atoms 3 and 0.4

play35:32

and so of course the result of this

play35:35

addition function

play35:36

application is 3.4 which is the same

play35:40

value

play35:41

as the value of the first

play35:44

parameter so because these two values

play35:47

then are equivalent

play35:48

the result of this eqv function

play35:51

application

play35:52

is true again now it's important to note

play35:56

that the eqv

play35:57

function does not work for lists so here

play36:01

we are

play36:01

applying the eqv function to two lists

play36:04

and we can see that

play36:05

both lists contain the same

play36:08

elements namely the

play36:11

symbolic atoms a and b even though these

play36:15

two lists are equivalent to each other

play36:18

the result that is produced by the

play36:20

function application

play36:22

is false so in other words the eqv

play36:25

function should never be used for

play36:27

comparing

play36:28

where the two lists contain the same

play36:31

elements

play36:32

now it's important also to note that

play36:34

floats

play36:35

and integers are different values

play36:38

so if we apply the eqv function

play36:42

to 3.0 as the first parameter

play36:45

and three as the second parameter

play36:47

because we are comparing

play36:49

a float value to an integer

play36:52

value we are comparing two different

play36:55

values

play36:56

and therefore the result is false

play37:00

so in general we should rather use

play37:03

the equals function when we are

play37:05

comparing numeric values

play37:07

to take care of cases where we may be

play37:10

comparing floats to integers or

play37:13

vice versa so in this case here we have

play37:16

an application of the equals function

play37:18

to 3.0 and 3 as the parameters

play37:22

and in this case the result that is

play37:24

yielded

play37:25

is a true value

play37:29

the second equivalence predicate we'll

play37:32

consider

play37:32

is the eq function again notice

play37:36

the question mark at the end of the

play37:37

function name

play37:39

and the eq function like the eqv

play37:42

function

play37:42

is applied to two parameters

play37:46

now the eq function will only yield

play37:49

a true result if the two parameters

play37:52

are the same object in memory so in

play37:55

other words this is a pointer comparison

play37:58

not a value comparison so here are some

play38:02

examples of the application of the eq

play38:05

function here we are comparing

play38:08

two symbolic atoms to one another

play38:11

and the symbolic atoms are the same so

play38:14

we have the symbolic atom a

play38:15

compared to the symbolic atom a so

play38:19

it turns out that scheme then will store

play38:22

the same symbolic atom as the same

play38:25

object within memory

play38:27

and so the result of this comparison

play38:30

is a true value

play38:33

if we compare two symbolic atoms that

play38:37

are different from one another so here

play38:38

we're comparing a

play38:40

to b then the result is false because

play38:44

these will obviously be

play38:46

two separate objects in memory

play38:49

and here we have a third application

play38:52

where we are comparing a symbolic atom

play38:54

to a list obviously these two must be

play38:57

different values in memory

play38:59

so the result produced by this function

play39:01

application

play39:03

is false now notice

play39:06

that for two list parameters the result

play39:09

is

play39:09

not reliable so if we apply for example

play39:13

the eq

play39:13

function to two lists where the two

play39:16

lists contain

play39:18

the same values then this may yield

play39:21

a true or a false result

play39:24

this depends largely on the specific

play39:27

context within the

play39:28

program that eq is being used and it

play39:32

also depends

play39:33

on the scheme interpreters

play39:35

implementation

play39:37

so essentially the result of this kind

play39:39

of comparison

play39:40

is unreliable also

play39:43

the eq function is not reliable for

play39:46

numeric atoms so over here we have the

play39:49

eq function applied

play39:51

to 3.4 as the first parameter

play39:55

and then an application of the addition

play39:58

function

play39:59

to 3 and 0.4

play40:02

so these values then are of course the

play40:05

same

play40:05

but this may then be a case

play40:09

where the two values are represented as

play40:12

separate objects in memory

play40:14

or possibly the same object so

play40:17

again the result is unreliable and this

play40:20

function application may yield

play40:22

a true or a false result

play40:26

so in general due to this uncertainty

play40:29

in multiple instances

play40:32

the eqv function is usually more

play40:35

appropriate than the eq function

play40:38

so the bottom line is if you use the eq

play40:42

function in a scheme program you must be

play40:45

very sure that this is the function that

play40:48

you intend to use

play40:50

and that you know what you are doing

play40:52

with this function's application

play40:56

the next function we'll look at is the

play40:59

let function

play41:00

also provided by scheme itself

play41:03

and this is the general syntax

play41:06

of the let function so we apply the let

play41:10

function

play41:11

to a list followed by a body

play41:15

and the first list then contains

play41:19

a series of pairs where we have

play41:23

a name and an associated expression

play41:26

within each pair we can have as many

play41:29

pairs

play41:30

as we wish so the way that the slit

play41:33

function

play41:34

then is evaluated is that the

play41:37

lists of names and expressions

play41:40

is run through from top to bottom each

play41:44

expression

play41:44

is evaluated and then the value

play41:48

of that expression is bound to the name

play41:51

that it is associated with

play41:53

so in this example we would first

play41:55

evaluate expression one

play41:57

and then the value of that expression

play41:59

would be bound

play42:00

to name one then we would move on to

play42:03

expression two which would be

play42:05

evaluated and then the value of that

play42:07

expression is bound to

play42:09

name two and so on and so forth

play42:13

then finally the body is evaluated after

play42:16

all of the name bindings

play42:18

have occurred and the body can then use

play42:22

any of the names included in the first

play42:26

part of the let function

play42:29

finally the lit construct then yields a

play42:33

result

play42:34

and that result is the value

play42:37

of the body once it has been evaluated

play42:43

here we have two examples of how the let

play42:46

function could be used

play42:48

so we'll start by looking at the left

play42:51

example

play42:52

in this example we use the define

play42:54

function to create

play42:55

a named function which is called

play42:57

computation

play42:58

and this is applied to four parameters

play43:02

a b c and d in the body

play43:05

of our function we have an application

play43:09

of the

play43:10

lead function and the first portion

play43:14

of the let function performs a series of

play43:17

bindings

play43:18

so first of all we add parameter a

play43:21

to parameter b and the result of that

play43:24

addition

play43:25

is then bound to the name top

play43:28

secondly we then apply the subtraction

play43:32

function to the parameters

play43:33

c and d and the result of this

play43:37

subtraction is then bound to the name

play43:40

button so again it's very important to

play43:43

understand that top and bottom are not

play43:46

variables as one would use

play43:50

them in an imperative programming

play43:52

language instead

play43:53

they are only names the value

play43:56

that is bound to each of those names is

play43:59

only bound once and it is impossible to

play44:03

bind

play44:03

another value to top or bottom after

play44:06

this first

play44:07

binding has taken place so

play44:11

after the bindings we then have the body

play44:14

of

play44:14

our lead function which is over there

play44:17

and here we then can use the

play44:20

names that we've bound to in the first

play44:23

part of the late function

play44:24

so here we use top and bottom and we

play44:28

divide top by bottom the result of this

play44:31

division

play44:32

then becomes the result of the entire

play44:35

lead function application and then

play44:38

because the let function is the

play44:42

only function application that occurs in

play44:45

the body of

play44:46

our computation function the result of

play44:49

the division then is also

play44:51

the result of the application of our

play44:54

computation

play44:55

function now it is also possible to have

play44:58

multiple function

play44:59

applications in the body of our lead

play45:02

function

play45:03

and in this case these function

play45:05

applications are evaluated in sequence

play45:08

and then the let construct yields the

play45:11

value

play45:12

of the last function application

play45:15

so that is illustrated in the second

play45:18

example

play45:18

on the right over here here again we are

play45:21

defining a function called computation

play45:23

and applying it to four parameters a b c

play45:27

and d and inside the body of our

play45:29

function we only have an application of

play45:32

the let function again we perform

play45:35

the same bindings to top and bottom as

play45:38

we saw

play45:39

in the previous example however now we

play45:41

can see that there are three

play45:43

function applications in the body of

play45:47

our lit so first of all we have an

play45:50

application of the display function

play45:53

and this then will display the result

play45:56

of performing a division where top

play46:00

is divided by bottom then we have

play46:03

a new line so this will then print a new

play46:06

line

play46:07

out to the terminal so so far we've

play46:10

printed out

play46:11

then a division result and a new line

play46:14

and then we get this final function

play46:17

application which is an application of

play46:19

the multiplication function

play46:21

and we multiply top and bottom which

play46:24

again

play46:24

are the two names that we have bound to

play46:27

in the first part

play46:29

of our lit now note that this last

play46:32

function application

play46:34

is not printed out by means of a

play46:37

display function instead this

play46:40

is the last function application and

play46:43

therefore

play46:43

the entire let evaluates to the result

play46:47

of this multiplication and because the

play46:50

let

play46:51

is again the only function in the body

play46:54

of our computation function

play46:56

the application of our computation

play46:57

function then

play46:59

also evaluates to the result of this

play47:02

multiplication

play47:06

finally we'll look at our first full

play47:08

implementation

play47:09

of scheme function in which we will

play47:12

recursively operate on a list

play47:16

this function is called member and it is

play47:18

applied to

play47:19

two parameters the first parameter is an

play47:22

atom

play47:23

while the second parameter is a simple

play47:26

list a simple list is a list that

play47:29

contains

play47:30

only atoms in other words it doesn't

play47:32

contain

play47:33

any nested sub-lists the member function

play47:37

should produce a true result if the atom

play47:40

is contained within the list

play47:42

and a false result otherwise

play47:46

so this specification implies that we

play47:48

need to work

play47:49

through the list one element at a time

play47:53

and in an imperative programming

play47:55

language we would use

play47:56

a loop in order to do this however as

play48:00

we've seen

play48:00

scheme is a pure functional programming

play48:04

language

play48:04

which means that it doesn't provide any

play48:07

loop

play48:08

structure in other words when we work

play48:11

through the list

play48:11

we must do so recursively

play48:14

so how will we attack this problem well

play48:17

we will recursively

play48:19

compare the atom provided as the first

play48:22

parameter

play48:23

to the member function to each atom

play48:26

contained within the list eliminating

play48:30

one atom from the start of the list at a

play48:33

time

play48:35

we then have two base cases the first

play48:38

base case is if we find a match between

play48:41

the atom that is the first parameter

play48:44

of the member function and the current

play48:48

atom

play48:49

in the list that we are considering

play48:52

if this is the case then we know that we

play48:55

have

play48:55

found the atom and therefore we don't

play48:58

need to search the remainder of the list

play49:00

we can then simply produce a true result

play49:04

now if we process through the entire

play49:07

list without having found

play49:08

any matches between the atom parameter

play49:12

and any atom contained within the list

play49:15

then we

play49:16

know that the atom is not contained in

play49:18

the list at all

play49:19

and in this situation we can then

play49:23

evaluate to a false result

play49:27

so over here we have the implementation

play49:30

of our member function

play49:32

we use a define and we specify that the

play49:35

name of the function

play49:36

is member and there are two parameters

play49:40

atm and lis atm is the atom that we are

play49:44

searching for

play49:45

and lis is the list that we are

play49:47

searching

play49:48

now note that we don't provide any types

play49:51

for these parameters at all

play49:53

the type of each parameter is implied by

play49:56

means of how the parameter is used

play49:59

within the function

play50:01

so then we have a cond the cond

play50:04

is our selection structure and this is

play50:07

responsible

play50:08

for deciding whether we execute one of

play50:11

the two base

play50:12

cases or the recursive case

play50:15

over here we have our first base case

play50:19

here we are testing to see whether our

play50:22

list is empty by means of the

play50:25

null function so if this predicate then

play50:28

evaluates

play50:29

to true it means that we have reached

play50:32

the end of our list without having found

play50:35

a match and in this case we can then

play50:37

just produce

play50:38

a false result the entire cond will then

play50:42

evaluate to false

play50:43

which means that the application of our

play50:45

member function

play50:47

will also evaluate two false

play50:50

our second base case is over here so

play50:53

here we perform

play50:54

an equivalence comparison between

play50:57

the atom provided as the first parameter

play51:01

and the car of our list the car of the

play51:04

list will be

play51:05

the first element within the list so if

play51:07

we find a match between that

play51:09

and the atom that we've provided as the

play51:12

first parameter of our member function

play51:14

we know that we have found our atom

play51:18

and therefore we can terminate our

play51:20

search

play51:21

and simply then produce a result of

play51:24

true finally in the else portion of the

play51:28

cond

play51:28

we have our recursive step and here

play51:32

we then execute our member function

play51:35

recursively

play51:36

with the same atom as the first

play51:38

parameter we obviously don't want to

play51:40

change the atom that we

play51:42

are searching for but then the list that

play51:45

we perform

play51:45

the search on must have the first

play51:49

element removed and this is because at

play51:52

this point we know that there isn't a

play51:54

match between the first element

play51:56

and the atom provided as the first

play51:59

parameter for

play52:00

our member function so we then use the

play52:03

cuda function

play52:04

in order to get the tail of the list

play52:06

discarding the

play52:07

first element and this will then

play52:10

continue the search

play52:11

with the next element within the list

play52:15

so this then overall is our member

play52:18

function definition however let's look

play52:21

at our cond in a bit more detail

play52:25

so in our cond the order

play52:28

of our cases is important

play52:32

we must provide all of our base cases

play52:35

first before our recursive case

play52:39

and so what i'd like you to do is to

play52:42

think

play52:42

about what would happen if we provided

play52:45

the recursive case

play52:47

first and the base cases following the

play52:50

recursive case

play52:51

what would occur is we would have a

play52:54

non-terminating recursion

play52:56

but i want you to think about why this

play52:58

would be the case

play53:01

next the order of our base cases is also

play53:04

important we must provide

play53:06

our null check first and then

play53:09

our equality check after that

play53:13

so what i would like you to do again

play53:15

here

play53:16

is think about what would happen if we

play53:18

performed the quality check

play53:20

first and then the null check after that

play53:24

and as a hint consider what would happen

play53:26

to

play53:27

this application of the car function

play53:30

if we were to place this second base

play53:33

case

play53:34

first all right so

play53:37

that then concludes our discussion

play53:41

for this lecture in the next lecture we

play53:44

will move on to

play53:45

a few more examples of

play53:49

recursive functions that perform

play53:50

operations on

play53:52

lists and we will then conclude the

play53:55

rest of this chapter

Rate This
β˜…
β˜…
β˜…
β˜…
β˜…

5.0 / 5 (0 votes)

Related Tags
Functional ProgrammingScheme LanguageLisp FamilyNumeric PredicatesControl FlowRecursion BasicsList OperationsProgramming ConceptsScheme TutorialRecursive FunctionsControl Structures