Upload
others
View
0
Download
0
Embed Size (px)
Citation preview
Structure of Programming LanguagesLecture 7 Functional Programming
CSCI 6636 – 4536
March 17, 2020
CSCI 6636 – 4536 Lecture 6b. . . 1/30 March 17, 2020 1 / 30
Outline
1 SchemeBasicsConditionalsRepetitionClosures in Scheme
2 HaskellHaskell BasicsFunctions and SyntaxOrder of EvaluationHaskell Lists
3 Homework
CSCI 6636 – 4536 Lecture 6b. . . 2/30 March 17, 2020 2 / 30
Scheme
Think about it. . .
What do you already know about Scheme?
What should you ask about first? Do you have questions?
CSCI 6636 – 4536 Lecture 6b. . . 3/30 March 17, 2020 3 / 30
Scheme Basics
Scheme History
Scheme is an old (1975) language that evolved from Lisp.
A few keywords are different, the basic structure is the same.
Both rely on conditional expressions and recursion for control.
Both are prefix languages, not postfix like Forth or infix like mostothers.
However, Lisp is dynamically scoped, and Scheme uses lexicalscoping. (This will be discussed in a future week.)
Use (define . . . ) to define a function name and parameter list andbind it to a body of code.
Parameter binding is the primary means naming a value.
CSCI 6636 – 4536 Lecture 6b. . . 4/30 March 17, 2020 4 / 30
Scheme Basics
Scheme Types
Types supported are:
Booleans: #t and #f
Numbers (integer?, rational?, real?, complex?)Characters (char?) where each char is written with a \# prefixSymbols: used as names for variables, as in (define size 15)
This definition can be changed using (set! size 10), but we willnot use destructive assignment in Scheme for labs in this course.Lists: (list -1 0 1) . null and () denote the empty list.Compound data types include strings, vectors, dotted pairs.
CSCI 6636 – 4536 Lecture 6b. . . 5/30 March 17, 2020 5 / 30
Scheme Conditionals
Scheme Conditional Example
This is a definition of the gcd function. The function name and argumentlist are separated by whitespace.
; Note: the gcd function is predefined in Scheme.
(define mygcd(x y)
(cond (
(or (< x 0) (< y 0)) (mygcd (abs x) (abs y)) )
((< x y) (mygcd y x) )
((= y 0) x )
(else (mygcd y (remainder x y)) )
))
or, abs, and remainder are all predefined Scheme functions.A single = means comparison for equality.
CSCI 6636 – 4536 Lecture 6b. . . 6/30 March 17, 2020 6 / 30
Scheme Conditionals
The Scheme Conditional Expression
Scheme uses a conditional expression, instead of conditional statements.
Except for separating tokens, whitespace does not matter. Formattingdoes not matter for the translator, but it is critically important forhumans.
(define ...) delimits the function definition.
(cond ...) delimits the conditional expression.
Inside those parentheses is a series of clauses delimited by (...).
Each clause is a condition followed by an expression to evaluate.
The conditions are evaluated in order, top to bottom.
The expression following the first true condition will be evaluatedand its result is the value of the cond.
The (else clause is used if no conditions are true.
A function returns the last value that was computed.
CSCI 6636 – 4536 Lecture 6b. . . 7/30 March 17, 2020 7 / 30
Scheme Repetition
Repetition: Loops vs. Recursion
A programming language needs a way to specify repetition.
This can be done using recursion. Often, a recursion is more concisethan a loop.
It can be done using loops. Generally, a loop is faster than a recursionto do the same thing, and requires less space on the stack.
Sometimes repetition is controlled by functions that iterate anoperation over the elements of a list.
CSCI 6636 – 4536 Lecture 6b. . . 8/30 March 17, 2020 8 / 30
Scheme Repetition
Tail Recursion
A tail recursion is a recursion in which no operations happen after therecursive call, and the function’s result is just the value of another functioncall.
Scheme was designed to implement proper tail recursion, that is toplace no limit on the number of recursive calls that can besimultaneously active.
The outcome of this design is that tail recursive functions can beconverted to loops to increase time and space efficiency. (Theprogrammer cannot write these loops, but the translator can.)
CSCI 6636 – 4536 Lecture 6b. . . 9/30 March 17, 2020 9 / 30
Scheme Repetition
Loop Expressions
A loop expression evaluates a function repeatedly and calculates ananswer, often in the form of an array or list. In Scheme, map operates onlists and produces a list as its result. Here are three examples:
(map 1+ (list 1 2 3 4 5))
;Value 2: (2 3 4 5 6)
(map + (list 1 2 3) (list 4 5 6))
;Value 3: (5 7 9)
(define numbers (list 2 3 5 7 9))
(map (lambda (x) (* x 2)) numbers)
;Value 4: (4 6 10 14 18)
CSCI 6636 – 4536 Lecture 6b. . . 10/30 March 17, 2020 10 / 30
Scheme Closures in Scheme
Binding example: A Scheme Closure
A closure is a function that is constructed from another function bysubstituting a value for one of the parameters. The resulting function hasone fewer parameter.
Here is a basic 2-argument conversion function in Scheme that usestoday’s conversion rate to convert funds from one currency toanother.( define (convert amount rate) (amount * rate))
Suppose that, daily, someone sets a global constant to the exchangerate between dollars and Euros:
(define DolEuroRate 0.901668)
We can use the 2-argument function to create a 1-argument functionthat is more convenient:
(define (dolEuro x) (convert x DolEuroRate ) )
(let (( fun (convert DolEuroRate) )) (fun x) )Here is a call on the closure, and its output: -- > dolEuro
1.00 Output: 0.901668
CSCI 6636 – 4536 Lecture 6b. . . 11/30 March 17, 2020 11 / 30
Scheme Closures in Scheme
A Scheme Closure
]=> (define (addTo x) (lambda (y) (+ x y) ))
]=> (define (binder x)
(let (( fun (addTo 3) ))
(fun x)
)
)
]=> (addTo 4)
;Value 2: #[compound-procedure 2]
]=> ((addTo 3) 10)
;Value: 13
]=> (binder 10)
;Value: 13
CSCI 6636 – 4536 Lecture 6b. . . 12/30 March 17, 2020 12 / 30
Scheme Closures in Scheme
Notes on the Scheme Closure
Although this example is not, in itself, useful, it demonstrates run-timebinding in a block-structured language.
The lambda expression in the body of addTo has a free variable, x.
When I call (addTo 3), the parameter x of addTo is bound to 3.
The result calling (addTo 3) is that we evaluate the lambda expressionin its body, and the formerly-free variable x is “captured” by (becomesbound to) addTo’s parameter x. The result is a function is a closurein which x is bound to 3, and is equivalent to: (lambda (y) (+ 3 y) )
In binder, the name fun is bound to that closure. A closure is afirst-class object and a higher-order function.
When you write (fun x), x refers to the argument of binder, not tothe x buried inside the closure.
This demonstrates that scheme uses lexical binding.
CSCI 6636 – 4536 Lecture 6b. . . 13/30 March 17, 2020 13 / 30
Haskell
Haskell
Haskell BasicsClosures in Haskell
Control in expressions: guards, mapsLazy evaluation
Control in function calls: pattern matchingList Comprehensions
Infinite lists
CSCI 6636 – 4536 Lecture 6b. . . 14/30 March 17, 2020 14 / 30
Haskell Haskell Basics
Haskell Basics
To run Haskell, execute ghci from a command shell. Then
Name your source file “something.hs”
:cd to the directory that stores your source-code file.
:load something
You are now in the Haskell interpreter and can enter expressions suchas 3 + 5
To run a function, type its name followed by the right number ofparameters of the right types.
CSCI 6636 – 4536 Lecture 6b. . . 15/30 March 17, 2020 15 / 30
Haskell Haskell Basics
Types and Type Variables
Haskell supports a variety of primitive types, polymorphic types, andcompound types:
The basic types Char, Bool, String, Int (bounded), Integer (notbounded), Float, Double
Type constructors are [ ] (to make a list) and ( ) (to denote afunction type or a tuple)
Type classes are: Show, Ord, Eq, Num, Integral (whole numbers),Enum, Ordering (>, < or =)
Type variables are letters like a and b that are used to stand for anytype in a declaration.
CSCI 6636 – 4536 Lecture 6b. . . 16/30 March 17, 2020 16 / 30
Haskell Haskell Basics
Type Inference
. . . from Learn You a Haskell for Great Good! by Miran Lipovaca
Unlike Java or Pascal, Haskell has type inference. If we write anumber, we don’t have to tell Haskell it’s a number. It can inferthat on its own, so we don’t have to explicitly write out the typesof our functions and expressions to get things done. ... However,understanding the type system is a very important part of learningHaskell.
What does this mean? Actually functions types ARE declared. The typesof objects are deduced from context.
Further, during C/C++ compilation, types ARE deduced, just as they arein Haskell. The type of the result of an expression depends on the types ofits operands and the operations performed. C/C++ type checking requirestype deduction. The only difference is that variables are declared andtyped in C/C++ but not in Haskell.
CSCI 6636 – 4536 Lecture 6b. . . 17/30 March 17, 2020 17 / 30
Haskell Haskell Basics
Some Simple Haskell Functions
To run Haskell, execute ghci from a command shell. Then
sq :: Float -> Float
sq x = x * x
thrice:: Integer -> Integer
thrice a = a * 3
dv3 :: Integer -> Bool
dv3 a = mod a 3 == 0
Calling these functions:
sq 2 -- Output: 4.0
thrice 10 -- Output: 30
dv3 12 -- Output: True
dv3 13 -- Output: False
CSCI 6636 – 4536 Lecture 6b. . . 18/30 March 17, 2020 18 / 30
Haskell Functions and Syntax
Haskell: a guarded expression
This is the way to write a cond in Haskell:
The conditions on the left, after the vertical bars, are called guards.
The guards are evaluated in order, as they are in a Scheme cond.
The first guard that is true selects the clause to be evaluated.
The result of that clause is the result of the expression.
gcd2 :: Integer -> Integer -> Integer
gcd2 a b
| a > b = gcd2 (a-b) b
| a == b = a
| otherwise = gcd2 a (b-a)
A very elegant gcd, but not very efficient because it uses repeatedsubtraction rather than division.
CSCI 6636 – 4536 Lecture 6b. . . 19/30 March 17, 2020 19 / 30
Haskell Functions and Syntax
The Convert Functions in Haskell:
In an earlier lecture, we saw a definition of convert in Scheme, and theconstruction of a closure based on convert. Here are the same functionsin Haskell.
First, we define a global symbol: rate = 0.713818
Here is the basic 2-argument function, a call on it, and the output:convert :: Double -> Double -> Double
convert m r = m * r
-- > convert 1.00 rate Output: 0.713818
Here, we reduce the number of parameters by using a global symbol.Haskell does not let you change the meaning of that symbol.
convMay23 :: Double -> Double
convMay23 m = convert m rate
-- > convMay23 3.75 Output: 2.6768175
CSCI 6636 – 4536 Lecture 6b. . . 20/30 March 17, 2020 20 / 30
Haskell Functions and Syntax
More Functions in Haskell:
Here is a higher-order function that returns a function as its result.The backslash m means “lambda m” in Haskell.
convAtRate :: Double -> (Double -> Double)
convAtRate r = (\m -> convert m r)
-- > convAtRate 0.713818 3.75 Output: 2.6768175
We can use this higher-order function to produce a normal1-argument function:
convMay24 :: Double -> Double
convMay24 = convAtRate 0.709175
-- > convMay24 3.75 Output: 2.65940625
Now map the 1-argument form down a list of inputs.map convMay24 [3.95, 42.18, 195, 37.80]
-- [2.80124125,29.9130015,138.289125,
26.806814999999997]
CSCI 6636 – 4536 Lecture 6b. . . 21/30 March 17, 2020 21 / 30
Haskell Functions and Syntax
Pattern Matching
Haskell functions may be defined in portions. (Typical for recursivefunctions.)
factorial :: Integer -> Integer
factorial 0 = 1
factorial n = n * factorial (n - 1)
The translator uses pattern matching to decide which part of the definitionto apply in any particular call. Possible patterns include:
Specific data values.Empty square brackets, denoting the empty list.If the parameter is a list, names are given to the head of the list andthe rest of the list: func (x:xs)
An underscore can replace either the x or the xs if that part of thelist is not needed in the function.The colon notation can be repeated to bind names to the first severallist elements.CSCI 6636 – 4536 Lecture 6b. . . 22/30 March 17, 2020 22 / 30
Haskell Functions and Syntax
Pattern Matching and Type Constraints Example
The (Num b) in the first line declares the type of b to be numeric.
myLength :: (Num b) => [a] -> b
myLength[] = 0
myLength (_:xs) = 1 + myLength xs
The (Show a) in the first line says that an a can be displayed as a String.
tell :: (Show a) => [a] -> String
tell [] = "The list is empty"
tell (x:[]) = "The list has one element: " ++ show x
tell (x:y:[]) = "The list has two elements: "
++ show x ++ " and " ++ show y
tell (x:y:_) = "This list is long. The first two
elements are: " ++ show x ++ " and " ++ show y
CSCI 6636 – 4536 Lecture 6b. . . 23/30 March 17, 2020 23 / 30
Haskell Order of Evaluation
Haskell: Defining Local Variables
Either a variable has no meaning (no binding) or it has the same meaningit had originally. The meaning cannot be changed by an assignment.
Here is a function to compute the roots of a quadratic equation:
quadroot :: Float -> Float -> Float -> [Float]
quadroot a b c
| delta > 0 = [t1+t2, t1-t2]
| delta == 0 = [t1]
| otherwise = []
where
delta = b * b - 4 * a * c
t1 = (0 - b) / (2 * a)
t2 = (sqrt delta) / (2 * a)
CSCI 6636 – 4536 Lecture 6b. . . 24/30 March 17, 2020 24 / 30
Haskell Order of Evaluation
Examples: Lazy evaluation.
Example: calculate root 1 0 1
1 Bindings: a=1, b=0, c=1
2 Start evaluating guarded expression; need a value for delta
3 Evaluate delta = b*b-4*a*c = 02 − 4× a× c = −4
4 Choose 3rd alternative for guarded expression.
5 Return the null list. (Value must be a [Float])
Example: calculate root 1 -2 1
1 Bindings: a=1, b=-2, c=1
2 Start evaluating guarded expression – need a value for delta
3 Evaluate delta= b*b-4*a*c = (−2)2 − 4× 1× 1 = 4− 4 = 0
4 Choose 2nd alternative for guarded expression.
5 Evaluate t1 = -b/(2*a) = −(−2)/(2× 1) = 2/2 = 1
6 Finish step 4; return answer = [1.0]
CSCI 6636 – 4536 Lecture 6b. . . 25/30 March 17, 2020 25 / 30
Haskell Order of Evaluation
Lazy evaluation sometimes evaluates everything.
Calculate root 1 0 -1 Lazy evaluation is used throughout.
1 Bindings:: a=1, b=0, c=-1
2 Start evaluating guarded expression; need a value for delta
3 Evaluate delta = b*b-4*a*c = 02 − 4× a× c = −(−4) = 4
4 Choose 1st alternative for guarded expression.
5 Evaluate t1 = -b/(2*a) = −0/(2× 1) = 0
6 Evaluate t1 =√
4 / (2*a) = 2/(2× 1) = 1
7 Finish step 4; return a list of the answers = [1.0,−1.0]
CSCI 6636 – 4536 Lecture 6b. . . 26/30 March 17, 2020 26 / 30
Haskell Haskell Lists
Haskell Lists.
A list can be written literally or denoted by an expression.
rate = 0.713818 A global symbol.a1 = "Hello" Give a name to a string.a2 = [’H’, ’i’] Another way to write a string.alpha = [’a’..’z’] The lower-case alphabet.["one","two","three"] A list of three strings.
[] The empty list.none = length [] 0
CSCI 6636 – 4536 Lecture 6b. . . 27/30 March 17, 2020 27 / 30
Haskell Haskell Lists
More Haskell Lists.
t1 = [2..6] Integers from 2 to 6, inclusive.h = head t1 2, the first item on the list.t0 = tail t1 A copy of the list excluding the head.n = length t1 5, The number of items in t1.
t2 = reverse t1 The list, reversed.t3 = 0 : t1 Add a value to the head of the list.
(Can’t append to the tail.)t4 = [1, 3..100] An arithmetic progression, 1, 3, 5 to 99t5 = take 10 t4 The first 10 values in t4.t6 = [4, 8..] An infinite list; will be evaluated only when
used, and so far as it is used.Be ready to hit CTRL-C if you print it.
An infinite list such as t6 is implemented as the closure of a finite head(an array or list) followed by a tail which is a function.
CSCI 6636 – 4536 Lecture 6b. . . 28/30 March 17, 2020 28 / 30
Haskell Haskell Lists
List Comprehensions.
A list can be written literally or denoted by a set expression.c1 = [ n+2 | n← 3..6] [5,6,7,8]
c2 = [ n*n | n← 1,2..100] Squares up to 10000.c3 = [ x+y | x← 3..5, y← 1..4] [4,5,6,7,5,6,7,8,6,7,8,9]
c4 = [ x+y | x← 3..5, y← 1..4, x>2*y] [4,5,6,7]
The tail is prodded into action, when necessary to lengthen the list, inresponse to the program accessing an unevaluated subscript position.
CSCI 6636 – 4536 Lecture 6b. . . 29/30 March 17, 2020 29 / 30
Homework
Homework 11: Read Chapter 12, Functional Languages
1 Write a loop expression in Scheme that will take a list of numbers asits parameter and return a list of numbers that are 2x + 1 times theoriginal numbers.
2 List three important ways (one sentence each) in which a functionallanguage differs from an object-oriented language.
3 Consider the gcd function in C. Express the type of the function as aC-style prototype. Then express the type of the function again incurried notation, as in Miranda.
4 Explain how Miranda can let the programmer define and use infinitelists. How is it possible to represent an infinite list in a finite memory?
5 In Miranda, define Lst to be the list of all multiples of 7 that are lessthan 50.
6 In Miranda, define the list of all numbers that are quotients of aneven integer divided by an odd integer.
CSCI 6636 – 4536 Lecture 6b. . . 30/30 March 17, 2020 30 / 30