Professional Documents
Culture Documents
CS403 Lectures
CS403 Lectures
CS403 Lectures
Programming Languages
Stefan D. Bruda
Fall 2021
H ISTORY OF PROGRAMMING LANGUAGES
“Prehistory”
The 1940s: von Neumann and Zuse
The 1950s: The first programming language
The 1960s: An explosion in programming languages
The 1970s: Simplicity, abstraction, study
The 1980s: Consolidation and new directions
The 1990s: The explosion of the World Wide Web
The 21st century
CS 403: A Brief (and Pretty Incomplete) History of Programming Languages (S. D. Bruda) Fall 2021 1 / 21
P REHYSTORY
CS 403: A Brief (and Pretty Incomplete) History of Programming Languages (S. D. Bruda) Fall 2021 2 / 21
W RITTEN LANGUAGE TO DESCRIBE COMPUTATIONAL
PROCEDURES
A cistern.
The length equals the height.
A certain volume of dirt has been excavated.
The cross-sectional area plus this volume comes to 110.
The length is 30. What is the width?
You should multiply the length, 30, by . . .
— Translation by Donald Knuth
No variables
Instead, numbers serve as a running example of the procedure being
described
“This is the procedure”
Programming is among the earliest uses to which written language was
put
Programming languages design has tried to get as close to that as possible
from the very beginning. . .
CS 403: A Brief (and Pretty Incomplete) History of Programming Languages (S. D. Bruda) Fall 2021 3 / 21
A LGORITHMS
CS 403: A Brief (and Pretty Incomplete) History of Programming Languages (S. D. Bruda) Fall 2021 4 / 21
T HE FIRST PROGRAMMING ENVIRONMENTS
CS 403: A Brief (and Pretty Incomplete) History of Programming Languages (S. D. Bruda) Fall 2021 5 / 21
T HE 1940 S : VON N EUMANN AND Z USE
Harvard Mark I (1943) – Howard Aiken (IBM), Grace Hopper (Navy) →
first electro-mechanical computer
Harvard Mark II: First computer bug
ENIAC (1946) – Presper Eckert, John Mauchly (U. Penn.) → First
electronic computer
Programming was manual, with switches and cables
John von Neumann led a team that built computers with stored programs
and a central processor (as we know them today)
Konrad Zuse designed the first programming language as we know it
(Plankalkul = program calculus)
In Germany, in isolation because of the war; work finally published in 1972
Advanced data type features: floating point, arrays, records
Invariants for correctness
Rather cumbersome notation
5 ∗ B ⇒ A
A[7] := 5 × B[6] → V 6 7 (subscripts)
S 1.n 1.n (data types)
Never implemented
CS 403: A Brief (and Pretty Incomplete) History of Programming Languages (S. D. Bruda) Fall 2021 6 / 21
T HE FIRST COMPUTER BUG !
CS 403: A Brief (and Pretty Incomplete) History of Programming Languages (S. D. Bruda) Fall 2021 7 / 21
T HE 1950 S : T HE FIRST PROGRAMMING LANGUAGES
CS 403: A Brief (and Pretty Incomplete) History of Programming Languages (S. D. Bruda) Fall 2021 8 / 21
FORTRAN EXAMPLE
DATA DIVISION.
WORKING-STORAGE SECTION.
01 INPUT-FIELD.
05 INPUT-VALUE PIC 99 VALUE ZERO.
01 CALCULATION-FIELD.
05 SUM-VALUE PIC 9(03) VALUE ZERO.
05 AVERAGE-VALUE PIC 9(03)V99 VALUE ZERO.
01 OUTPUT-FIELD.
05 EDIT-FIELD PIC ZZ9.99 VALUE ZERO.
PROCEDURE DIVISION.
1000-MAIN.
PERFORM 2000-INPUT-ADD 10 TIMES.
DIVIDE 10 INTO SUM-VALUE GIVING AVERAGE-VALUE.
2000-INPUT-ADD. ...
CS 403: A Brief (and Pretty Incomplete) History of Programming Languages (S. D. Bruda) Fall 2021 10 / 21
T HE 1950 S : T HE FIRST LANGUAGES ( CONT ’ D )
Algol 60
General, expressive language; most current imperatives are derivatives
Introduced many modern concepts
structured programming reserved keywords type declarations
recursion stack dynamic arrays call-by-value
user defined types free-format
Stack-based run time environment
Great success and also great failure (ahead of its time, too complex, lack of
I/O, lack of support from IBM) → entrenchment of Fortran
LISP (John McCarthy, MIT)
LISt Processing → the main data structure is the (singly linked) list
Untyped, messy language, but good for problems we solve by trial and error
(quick prototyping) → used in many AI applications
Historically inefficient on Von Neumann machines
Main processing unit: the recursive function → influenced the modern
functional languages such as ML, Miranda, Haskell
Contemporary variants include Common Lisp, Scheme, Emacs Lisp
CS 403: A Brief (and Pretty Incomplete) History of Programming Languages (S. D. Bruda) Fall 2021 11 / 21
A LGOL EXAMPLE
CS 403: A Brief (and Pretty Incomplete) History of Programming Languages (S. D. Bruda) Fall 2021 12 / 21
L ISP EXAMPLE
CS 403: A Brief (and Pretty Incomplete) History of Programming Languages (S. D. Bruda) Fall 2021 13 / 21
T HE 1960 S : A N EXPLOSION IN LANGUAGES
PL/1 (1964)
Combined features of FORTRAN, COBOL, Algol 60 and more!
Translators were slow, huge, and unreliable
Some say it was ahead of its time. . .
Algol 68 → still ahead of its time!
Simula (or what would be called today Object-oriented Algol)
BASIC
etc.
CS 403: A Brief (and Pretty Incomplete) History of Programming Languages (S. D. Bruda) Fall 2021 14 / 21
T HE 1970 S : S IMPLICITY, ABSTRACTION , STUDY
CS 403: A Brief (and Pretty Incomplete) History of Programming Languages (S. D. Bruda) Fall 2021 15 / 21
C EXAMPLE
#include <stdio.h>
main(t,_,a)
char*a;
{return!0<t?t<3?
main(-79,-13,a+
main(-87,1-_,
main(-86, 0, a+1 )
+a)):
1,
t<_?
main(t+1, _, a )
:3,
main ( -94, -27+t, a )
&&t == 2 ?_
<13 ?
main ( 2, _+1, "%s %d %d\n" )
:9:16:
t<0?
t<-72?
main( _, t,
"@n’+,#’/*{}w+/w#cdnr/+,{}r/*de}+,/*{*+,/w{%+,/w#q#n+,/#{l,+,/n{n+,/+#n+,/#;\
#q#n+,/+k#;*+,/’r :’d*’3,}{w+K w’K:’+}e#’;dq#’l q#’+d’K#!/+k#;\
q#’r}eKK#}w’r}eKK{nl]’/#;#q#n’){)#}w’){){nl]’/+#n’;d}rw’ i;# ){nl]!/n{n#’; \
r{#w’r nc{nl]’/#{l,+’K {rw’ iK{;[{nl]’/w#q#\
\
n’wk nw’ iwk{KK{nl]!/w{%’l##w#’ i; :{nl]’/*{q#’ld;r’}{nlwb!/*de}’c ;;\
{nl’-{}rw]’/+,}##’*}#nc,’,#nw]’/+kd’+e}+;\
#’rdq#w! nr’/ ’) }+}{rl#’{n’ ’)# }’+}##(!!/")
:
t<-50?
_==*a ?
putchar(31[a]):
main(-65,_,a+1)
:
main((*a == ’/’) + t, _, a + 1 )
:
0<t?
main ( 2, 2 , "%s")
:*a==’/’||
CSmain(0,
403: A Brief (and Pretty Incomplete) History of Programming Languages (S. D. Bruda) Fall 2021 16 / 21
T HE 1980 S : C ONSOLIDATION & NEW DIRECTIONS
CS 403: A Brief (and Pretty Incomplete) History of Programming Languages (S. D. Bruda) Fall 2021 17 / 21
T HE 1990 S AND 200 S
1990s
Java → eliminate the non-object-oriented features of C++
Haskell → purely functional programming language
quicksort [] = []
quicksort (x:xs) = quicksort [y|x <- xs, y < x] ++ [x] ++
quicksort [y|x <- xs, y >= x]
2000s
Python → multi-paradigm language (procedural, object-oriented, functional,
etc.)
Languages for the Web
Java applets
Languages within Web pages (PHP, server-side includes)
Emphasis on cross-platform development
Develop on PC, run on cell phones, game consoles, and toasters
CS 403: A Brief (and Pretty Incomplete) History of Programming Languages (S. D. Bruda) Fall 2021 18 / 21
2010s: Connectivity Explosion
CS 403: A Brief (and Pretty Incomplete) History of Programming Languages (S. D. Bruda) Fall 2021 19 / 21
W HERE ARE WE NOW
CS 403: A Brief (and Pretty Incomplete) History of Programming Languages (S. D. Bruda) Fall 2021 19 / 21
S TRANGE LANGUAGES
Strange languages definitely exist
Case in point: Brainf**k
A Brainf**k program has an implicit byte pointer, called “the pointer”, which is
free to move around within an array of 30,000 bytes, initially all set to zero
The pointer is initialized to point to the beginning of this array
The Brainf**k programming language consists of eight commands, each of
which is represented as a single character
> Increment the pointer
< Decrement the pointer
+ Increment the byte at the pointer
- Decrement the byte at the pointer
. Output the byte at the pointer
, Input a byte and store it in the byte at the pointer
[ Jump past the matching ] if the byte at the pointer is zero
] Jump to the matching [
>+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..++
+.[-]>++++++++[<++++>-] <.#>+++++++++++[<+++++>-]<.>+++
+++++[<+++>-]<.+++.------.--------.[-]>++++++++ [ <++++
>-]<+.[-]++++++++++.
CS 403: A Brief (and Pretty Incomplete) History of Programming Languages (S. D. Bruda) Fall 2021 20 / 21
C HRONOLOGY
Fortran
Lisp
1960 Algol 60 COBOL APL
Snobol 4
Simula PL/1
Algol 68
1970 Pascal
Smalltalk Prolog
Clu C
Scheme
ML
Ada ICON
1980 Modula 2
Miranda
C++
Eiffel
Oberon
Haskell
1990
Java
CS 403: A Brief (and Pretty Incomplete) History of Programming Languages (S. D. Bruda) Fall 2021 21 / 21
CS 403: Introduction to functional programming
Gregory Mierzwinski
Fall 2022
F UNCTIONAL PROGRAMMING IS PROGRAMMING
WITHOUT . . .
square(x) = x × x
Two forms:
let v1 = e1 definition
v2 = e2 where v1 = e1
. v2 = e2
. .
. .
vk = ek .
in expression vk = ek
Definitions are qualified by where clauses, while expressions are
qualified by let clauses
Each type has associated operations that are not necessarily meaningful
to other types
Arithmetic operations (+, −, ∗, /) can be applied to numerical types, but it
does not make any sense to apply them on, say, values of type Bool
It does, however make sense to compare (using = (==), 6= (/=), ≤ (<=), <,
etc.) both numbers and boolean values
Every well formed expression can be assigned a type (strong typing)
The type of an expression can be inferred from the types of the constituents
of that expression
Those expression whose type cannot be inferred are rejected by the
compiler
badType x fact :: Integer -> Integer
| x == 0 = 0 fact x
| x > 0 = ’p’ | x < 0 = error "Negative argument."
| x < 0 = ’n’ | x == 0 = 1
| x > 0 = x * fact (x-1)
What is the type of error?
An inductive proof for a fact P(n), for all n ≥ α consists in two steps:
Proof of the base case P(α), and
The inductive step: assume that P(n − 1) is true and show that P(n) is also
true
Example
Proof that all the crows have the same colour: For all sets C of crows, |C| ≥ 1,
it holds that all the crows in set C are identical in colour
Base case, |C| = 1: immediate.
For a set of crows C, |C| = n, remove a crow for the set; the remaining (a set of
size n − 1) have the same colour by inductive assumption. Repeat by removing
other crow. The desired property follows
Note: According to the Webster’s Revised Unabridged Dictionary, a crow is “A
bird, usually black, of the genus Corvus [. . . ].”
The same process is used for building recursive functions: One should
provide the base case(s) and the recursive definition(s):
To write a function f :: Integer → Integer , write the base case (definition for
f 0) and the inductive case (use f (n − 1) to write a definition for f n)
Example: computing the factorial
Base case: fact 0 = 1
Induction step: fact n = n * fact (n-1)
To write a function f :: [a] → β, use induction over the length of the
argument; the base case is f [] and the inductive case is f (x : xs) defined
using f xs
Example: function that concatenates two lists together; we perform induction
on the length of the first argument:
Base case: concat [] ys = ys
Induction step: concat (x:xs) ys = x : concat xs ys
Induction is also an extremely useful tool to prove functions that are
already written
Membership (x ∈ A):
member x [] = False
member x (y:ys) | x == y = True
| True = member x ys
Union (A ∪ B), intersection (A ∩ B), difference (A \ B):
union [] t = t
union (x:xs) t | member x t = union xs t
| True = x : union xs t
intersection [] t = []
intersection (x:xs) t | member x t = x : intersection xs t
| True = intersection xs t
difference [] t = []
difference (x:xs) t | member x t = difference xs t
| True = x : difference xs t
Constructor: no recursion. makeSet x = [x]
In Haskell, all objects (including functions) are first class citizens. That is,
all objects can be named,
all objects can be members of a list/tuple,
all objects can be passed as arguments to functions,
all objects can be returned from functions,
all objects can be the value of some expression
twice :: (a -> a) -> (a -> a) twice :: (a -> a) -> a -> a
twice f = g twice f x = f (f x)
where g x = f (f x)
compose f g = h compose f g x = f (g x)
where h x = f (g x) -- or --
compose f g = f.g
1 mystery x = aux x []
where aux [] ret = ret
aux (x:xs) ret = aux xs (x:ret)
1 mystery x = aux x []
where aux [] ret = ret
aux (x:xs) ret = aux xs (x:ret)
2 reverse [] = []
reverse (x:xs) = reverse xs ++ [x]
What is the difference between these two implementations?
1 mystery x = aux x []
where aux [] ret = ret
aux (x:xs) ret = aux xs (x:ret)
2 reverse [] = []
reverse (x:xs) = reverse xs ++ [x]
What is the difference between these two implementations?
An accumulating argument is used for efficiency purposes
It basically transforms a general recursion into a tail recursion
Example:
mystery :: [(String,Int)] -> [String]
mystery xs = map fst (filter ( ((<=) 80) . snd ) xs)
Example:
mystery :: [(String,Int)] -> [String]
mystery xs = map fst (filter ( ((<=) 80) . snd ) xs)
foldr
[l1 , l2 , . . . , ln ] −→ l1 • (l2 • (l3 • (· · · • (ln • id) · · · )))
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr op id [] = id
foldr op id (x:xs) = x ‘op‘ (foldr op id xs)
foldl
[l1 , l2 , . . . , ln ] −→ (· · · (((id • l1 ) • l2 ) • l3 ) • · · · • ln )
foldl :: (a -> b -> a) -> a -> [b] -> a
foldl op id [] = id
foldl op id (x:xs) = foldl op (x ‘op‘ id) xs
Examples:
triples :: Int -> [(Int,Int,Int)]
triples n = [(x,y,z) | x <- [1..n],y <- [1..n],z <- [1..n]]
-- or [(x,y,z) | x <- [1..n],y <- [x..n],z <- [z..n]]
pyth :: (Int,Int,Int) -> Bool
pyth (x,y,z) = x*x + y*y == z*z
triads :: Int -> [(Int,Int,Int)]
triads n = [(x,y,z) | (x,y,z) <- triples n, pyth (x,y,z)]
General form:
Quicksort:
qsort :: (Ord a) => [a] -> [a]
qsort [] = []
qsort (x:xs) = qsort [y | y <- xs, y <= x] ++ [x] ++
qsort [y | y <- xs, y > x]
-- Operations:
addNat,mulNat :: Nat -> Nat -> Nat
addNat m Zero = m
addNat m (Succ n) = Succ (addNat m n)
mulNat m Zero = Zero
mulNat m (Succ n) = addnat (mulNat m n) m
Again, type definitions can be parameterized:
data List a = Nil | Cons a (List a)
-- data [a] = [] | a : [a]
data BinTree a = Null | Node a (BinTree a) (BinTree a)
CS 403: Introduction to functional programming Fall 2022 31 / 51
T YPE CLASSES
Each type may belong to a type class that define general operations. This
also offers a mechanism for overloading
Type classes in Haskell are similar with abstract classes in Java
data Nat = Zero | Succ Nat Main> one
deriving (Eq,Show) Succ Zero
Main> two
instance Ord Nat where Succ (Succ Zero)
Zero <= x = True Main> three
x <= Zero = False Succ (Succ (Succ Zero))
(Succ x) <= (Succ y) = x <= y Main> one > two
False
one,two,three :: Nat Main> one > Zero
one = Succ Zero True
two = Succ one Main> two < three
three = Succ two True
Main>
x <= y = compare x y /= GT
x < y = compare x y == LT
x >= y = compare x y /= LT
x > y = compare x y == GT
max x y | x >= y = x
| otherwise = y
min x y | x <= y = x
| otherwise = y
CS 403: Introduction to functional programming Fall 2022 33 / 51
T YPE CLASSES ( CONT ’ D )
m - Zero = m
(Succ m) - (Succ n) = m - n
+ - *
negate
abs Num
signum
Real
Bounded div
mod Fractional
minBound Integral even
maxBoun odd /
d recip
RealFrac Floating
pi exp log
sqrt
logBase
RealFloat sin, cos, etc.
Recall that a Haskell function accepts one argument and returns one
result
peanuts → chocolate-covered peanuts
raisins → chocolate-covered raisins
ants → chocolate-covered ants
Using the lambda calculus, a general “chocolate-covering” function (or
rather λ-expression) is described as follows:
λx.chocolate-covered x
λy.λx.y-covered x
λf .(f ) ants
LE XPRESSION → I DENTIFIER
LE XPRESSION → λI DENTIFIER.LE XPRESSION (abstraction)
LE XPRESSION → (LE XPRESSION)LE XPRESSION (combination)
LE XPRESSION → (LE XPRESSION)
Another example: map (\ x -> x+1) [1,2,3] maps (i.e., applies) the
λ-expression λx.x + 1 to all the elements of the list, thus producing
[2,3,4]
In general, for some expression E, λx.E (in Haskell-speak: \ x -> E)
denotes the function that maps x to the (value of) E
CS 403: Introduction to functional programming Fall 2022 45 / 51
M ULTIPLE REDUCTIONS
infty :: Integer
infty = infty + 1
three infty
⇒ (def. infty)
three (infty + 1)
⇒ (def. infty) three infty
three ((infty + 1) + 1) ⇒ (def. three)
⇒ (def. infty) 3
three (((infty + 1) + 1) + 1)
..
.
Haskell uses the second variant, called lazy evaluation (normal order,
outermost reduction), as opposed to eager evaluation (applicative order,
innermost reduction):
Main> three infty
3
Why is good to be lazy:
Doesn’t hurt: If an irreducible form can be obtained by both kinds of
reduction, then the results are guaranteed to be the same
More robust: If an irreducible form can be obtained, then lazy evaluation is
guaranteed to obtain it
Even useful: It is sometimes useful (and, given the lazy evaluation, possible)
to work with infinite objects
[1 .. 100] produces the list of numbers between 1 and 100, but what is
produced by [1 .. ]?
Prelude> [1 ..] !! 10
11
Prelude> [1 .. ] !! 12345
12346
Prelude> zip [’a’ .. ’g’] [1 ..]
[(’a’,1),(’b’,2),(’c’,3),(’d’,4),(’e’,5),(’f’,6),(’g’,7)]
A stream of prime numbers:
primes :: [Integer]
primes = sieve [2 .. ]
where sieve (x:xs) = x : [n | n <- sieve xs, mod n x /= 0]
-- alternate:
-- sieve (x:xs) = x : sieve (filter (\ n -> mod n x /= 0) xs)
Gregory Mierzwinski
Fall 2022
K NOWLEDGE REPRESENTATION
A proposition is a logical statement that can be either false or true
To work with propositions one needs a formal system i.e., a symbolic logic
Predicate calculus or first-order logic is one such a logic
A term is a constant, structure, or variable
An atomic proposition (or predicate) denotes a relation. It is composed of a
functor that names the relation, and an ordered list of terms (parameters):
secure(room), likes(bob, steak ), black(crow), capital(ontario, toronto)
Variables can appear only as arguments. They are free:
capital(ontario, X )
unless bounded by one of the quantifiers ∀ and ∃:
∃ X : capital(ontario, X ) ∀ Y : capital(Y , toronto)
A compound proposition (formula) is composed of atomic propositions,
connected by logical operators: ¬, ∧, ∨, → (⇒). Variables are bound using
quantifires
∀X .(crow(X ) → black(X ))
∃X .(crow(X ) ∧ white(X ))
∀X .(dog(fido) ∧ (dog(X ) → smelly(X )) → smelly(fido))
A ∨ ¬B ∨ ¬C ∨ ¬D
B∧C∧D →A
Natural Language:
The window is locked. If the light is off and the door is locked, the
room is secure. The light is off if the window is dark. The window is
dark.
Horn clauses:
locked(window)
dark(window)
off(light) ∧ locked(door ) → secure(room)
dark(window) → off(light)
?- secure(room).
No
?- locked(door).
No
?- locked(Something).
Something = window
Yes
?- locked(Something).
Something = window ;
No
Query variables are all existentially quantified
CS 403: Introduction to logic programming Fall 2022 8 / 41
C ONJUNCTIVE RULES
A family tree:
parent(ann,bob). parent(ann,calvin).
parent(bob,dave). parent(dave,helen).
parent(X,Y)
α1 , . . . , α n
α10 ∧ · · · ∧ αn0 → β
∃ σ : (α1 )σ = (α10 )σ ∧ · · · ∧ (αn )σ = (αn0 )σ (generalized
βσ modus ponens)
Proof → a sequence of applications of inference rules
Generates in effect a logically sound traversal of the proof tree
KB
Bob is a buffalo 1. buffalo(bob)
Pat is a pig 2. pig(pat)
Buffaloes outrun pigs 3. buffalo(X ) ∧ pig(Y ) → faster (X , Y )
Query
Is something outran
by something else? faster (U, V )
Negated query: 4. faster (U, V ) →
{X/bob}
parent(cecil,X) −> 3
{X/dave}
X = calvin ;
T Redo: ( 7) parent(ann, _G365)
T Exit: ( 7) parent(ann, bob)
X = bob ;
No
?− parent(ann,X).
(1)
X=calvin ;
Fail Redo
(2)
No parent(ann,X)
CS 403: Introduction to logic programming Fall 2022 14 / 41
S EARCHING THE KNOWLEDGE BASE ( CONT ’ D )
parent(ann,calvin). parent(ann,bob).
parent(bob,dave). parent(dave,helen).
grandparent(X,Y) :- parent(X,Z),parent(Z,Y).
parent(X,Z) parent(Z,Y)
grandparent(X,Y) fail
exit
X/ann
call exit Z/bob
redo Z/dave
X/ann call
Z/calvin X/ann
exit Z/calvin parent(Z,Y)
X/ann call
Z/bob X/ann
parent(X,Z)
Z/bob
call
call exit
exit
X/ann
X/ann Z/bob
Z/calvin
parent(bob,dave)
parent(ann,calvin) parent(ann,bob) parent(dave,helen)
ancestor(X,Y) :- parent(X,Y).
ancestor(X,Y) :- parent(X,Z),ancestor(Z,Y).
A recursive call is treated as a brand new call, with all the variables
renamed
parent(X,Z) ancestor(Z,Y)
ancestor(X,Y)
parent(Z,Z1) ancestor(Z1,Y)
ancestor(Z,Y)
Y = calvin No
Yes
Once a variable is bound through some unification process, it cannot
become free again
[debug] 15 ?- X=1, X=2.
T Call: ( 7) _G340=1
T Exit: ( 7) 1=1
T Call: ( 7) 1=2
T Fail: ( 7) 1=2
No
Do not confuse =/2 with assignment!
[debug] ?- member_tree(3,tree(2,tree(1,nil,nil),tree(3,nil,nil))).
T Call: ( 7) member_tree(3, tree(2, tree(1, nil, nil), tree(3, nil, nil)))
T Call: ( 8) member_tree(3, tree(3, nil, nil))
T Exit: ( 8) member_tree(3, tree(3, nil, nil))
T Exit: ( 7) member_tree(3, tree(2, tree(1, nil, nil),tree(3, nil, nil)))
Yes
[debug] ?- member_tree(5,tree(2,tree(1,nil,nil),tree(3,nil,nil))).
T Call: ( 7) member_tree(5, tree(2, tree(1, nil, nil),tree(3, nil, nil)))
T Call: ( 8) member_tree(5, tree(3, nil, nil))
T Call: ( 9) member_tree(5, nil)
T Fail: ( 9) member_tree(5, nil)
T Redo: ( 8) member_tree(5, tree(3, nil, nil))
T Fail: ( 8) member_tree(5, tree(3, nil, nil))
T Redo: ( 7) member_tree(5, tree(2, tree(1, nil, nil),tree(3, nil, nil)))
T Fail: ( 7) member_tree(5, tree(2, tree(1, nil, nil),tree(3, nil, nil)))
No
CS 403: Introduction to logic programming Fall 2022 22 / 41
L ISTS
Lists are nothing special, just a structure named “.”, and containing two
parameters
The first one is the elements at the head of the list,
The second is a structure “.”, or the empty list “[]”
That is, .(X,XS) is equivalent to Haskell’s (x::xs)
The difference from Haskell is given by the absence of types in Prolog: A
list can contain any kind of elements
As in Haskell, there is some syntactic sugar:
One can enumerate the elements: [1,[a,4,10],3]
The expression [X|Y] is equivalent to .(X,Y)
We also have the equivalence between [X,Y,Z|R] and .(X,.(Y,.(Z,R))),
and so on
?- [b,a,d] = [d,a,b].
?- [X|Y] = [a,b,c].
?- [X|Y] = [].
?- [[X1|X2]|X3] = [[1,2,3],4,5].
The absence of types in Prolog is brought to extremes: the list [1] is the
structure .(1,[]). However, the empty list [] is an atom!
CS 403: Introduction to logic programming Fall 2022 23 / 41
L ISTS
Lists are nothing special, just a structure named “.”, and containing two
parameters
The first one is the elements at the head of the list,
The second is a structure “.”, or the empty list “[]”
That is, .(X,XS) is equivalent to Haskell’s (x::xs)
The difference from Haskell is given by the absence of types in Prolog: A
list can contain any kind of elements
As in Haskell, there is some syntactic sugar:
One can enumerate the elements: [1,[a,4,10],3]
The expression [X|Y] is equivalent to .(X,Y)
We also have the equivalence between [X,Y,Z|R] and .(X,.(Y,.(Z,R))),
and so on
?- [b,a,d] = [d,a,b]. → unification failure
?- [X|Y] = [a,b,c]. → X=a,Y=[b,c]
?- [X|Y] = []. → unification failure
?- [[X1|X2]|X3] = [[1,2,3],4,5]. → X1=1,X2=[2,3],X3=[4,5]
The absence of types in Prolog is brought to extremes: the list [1] is the
structure .(1,[]). However, the empty list [] is an atom!
CS 403: Introduction to logic programming Fall 2022 23 / 41
L IST PROCESSING
Membership: member/2
Membership: member/2
member(X,[X| ]).
member(X,[ |Y]) :- member(X,Y).
What is the answer to the query ?- member(X,[1,2,3,4]).
Membership: member/2
member(X,[X| ]).
member(X,[ |Y]) :- member(X,Y).
What is the answer to the query ?- member(X,[1,2,3,4]).
Both arguments of member/2 may be bound – if so we write member(+E,+L)
In fact, member/2 also works as member(-E,+L) (first argument free)
The general specification is member(?E,+L) (last argument is bound, but the
first is not necessarily bound)
Membership: member/2
member(X,[X| ]).
member(X,[ |Y]) :- member(X,Y).
What is the answer to the query ?- member(X,[1,2,3,4]).
Both arguments of member/2 may be bound – if so we write member(+E,+L)
In fact, member/2 also works as member(-E,+L) (first argument free)
The general specification is member(?E,+L) (last argument is bound, but the
first is not necessarily bound)
There are no functions in Prolog. What if we want that our program to
compute a value?
Membership: member/2
member(X,[X| ]).
member(X,[ |Y]) :- member(X,Y).
What is the answer to the query ?- member(X,[1,2,3,4]).
Both arguments of member/2 may be bound – if so we write member(+E,+L)
In fact, member/2 also works as member(-E,+L) (first argument free)
The general specification is member(?E,+L) (last argument is bound, but the
first is not necessarily bound)
There are no functions in Prolog. What if we want that our program to
compute a value?
We invent a new variable that will be bound to the result by various
unification processes
Membership: member/2
member(X,[X| ]).
member(X,[ |Y]) :- member(X,Y).
What is the answer to the query ?- member(X,[1,2,3,4]).
Both arguments of member/2 may be bound – if so we write member(+E,+L)
In fact, member/2 also works as member(-E,+L) (first argument free)
The general specification is member(?E,+L) (last argument is bound, but the
first is not necessarily bound)
There are no functions in Prolog. What if we want that our program to
compute a value?
We invent a new variable that will be bound to the result by various
unification processes
A predicate for concatenating (”appending”) two lists: append/3
append([],L,L).
append([X|R],L,[X|R1]) :- append(R,L,R1).
Membership: member/2
member(X,[X| ]).
member(X,[ |Y]) :- member(X,Y).
What is the answer to the query ?- member(X,[1,2,3,4]).
Both arguments of member/2 may be bound – if so we write member(+E,+L)
In fact, member/2 also works as member(-E,+L) (first argument free)
The general specification is member(?E,+L) (last argument is bound, but the
first is not necessarily bound)
There are no functions in Prolog. What if we want that our program to
compute a value?
We invent a new variable that will be bound to the result by various
unification processes
A predicate for concatenating (”appending”) two lists: append/3
append([],L,L).
append([X|R],L,[X|R1]) :- append(R,L,R1).
What is the result of the query ?- append(X,Y,[1,2,3,4]).
13 ?- fact(1,X).
X = 1
Yes
13 ?- fact(1,X).
X = 1
Yes
14 ?- fact(2,X).
[WARNING: Arithmetic: ‘fact/2’ is not a function]
Exception: ( 8) G185 is 2*fact(2-1, G274) ?
[WARNING: Unhandled exception]
Given the call fact(5,X), what happens if one requests a new solution
after Prolog answers X=120? Why? How to fix?
fact(1,1).
fact(N,R) :- N1 is N-1, fact(N1,R1), R is N*R1.
?- fact(5,X).
X = 120 ;
???
?- not(member(X,[1,2,3])).
No
?- not(not(member(X,[1,2,3]))).
X = _G332 ;
No
not/1 fails upon resatisfaction (a goal can fail in only one way)
not/1 does not bind variables
sign(X,+) :- positive(X).
sign(X,-) :- negative(X).
sign(X,0).
sign1(X,+) :- positive(X).
sign1(X,-) :- negative(X).
sign1(X,0) :- not(positive(X)), not(negative(X)).
?- sign(1,X).
X = + ;
X = 0 ;
No
?- sign1(1,X).
X = + ;
No
CS 403: Introduction to logic programming Fall 2022 28 / 41
S TATE SPACE SEARCH
The concept of state space search is widely used in AI
Idea: a problem can be solved by examining the steps which might be taken
towards its solution
Each action takes the solver to a new state
The solution to such a problem is a list of steps leading from the initial state
to a goal state
Classical example: A Farmer who needs to transport a Goat, a Wolf and
some Cabbage across a river one at a time. The Wolf will eat the Goat if
left unsupervised. Likewise the Goat will eat the Cabbage. How will they
all cross the river?
A state is described by the positions of the Farmer, Goat, Wolf, and Cabbage
The solver can move between states by making a “legal” move (which does
not result in something being eaten)
General form for a state space search problem:
Input:
1 The start state
2 One (or more) goal states or final states
3 The state transition function, or how to get from one state to another
Output: a list of moves or state transitions that lead from the initial state to
one of the final states
CS 403: Introduction to logic programming Fall 2022 29 / 41
S TATE SPACE SEARCH IN P ROLOG
?- search(a,e,R).
R = [to(a,b),to(b,c),to(c,d),to(d,e)] ;
R = [to(a,d),to(d,e)] ;
No
?- search(e,a,R).
No
?- search(a,e,R).
ERROR: Out of local stack
We can use then a generate and test technique:
We keep track of the previously visited states
Then, we generate a new state (as before), but we also test that we haven’t
been in that state already; we proceed forward only if the test succeeds
search(Initial,Final,Result) :- ?- search(a,e,R).
search(Initial,Final,[Initial],Result). R = [to(a, b), to(b, c),
search(Final,Final,_,[]). to(c, d), to(d, e)] ;
search(Crt,Final,Visited,[M|Result]) :- R = [to(a, d), to(d, e)] ;
move(Crt,AState,M), % generate No
not(member(AState,Visited)), % test
search(AState,Final,[AState|Visited],
Result).
CS 403: Introduction to logic programming Fall 2022 32 / 41
T HE PROBLEM - DEPENDENT DEFINITIONS
% first, attempt to move the Cabbage, then the Goat, then the Wolf:
move_attempt([B,B,G,W],[B1,B1,G,W], moved(cabbage,B,B1)) :- opposite(B,B1).
move_attempt([G,B,G,W],[G1,B,G1,W], moved(goat,G,G1)) :- opposite(G,G1).
move_attempt([W,B,G,W],[W1,B,G,W1], moved(wolf,W,W1)) :- opposite(W,W1).
%... eventually, move the empty boat:
move_attempt([X,C,G,W],[Y,C,G,W], moved(nothing,X,Y)) :- opposite(X,Y).
opposite(south,north). opposite(north,south).
?- search(1-1,3-3,R).
No
3 3
2 2
1 1
1 2 3 1 2 3
?- search_shorter(1-1,4-3,4,R).
Given some integer n and two vertices A and B, is there a path from A to
B of weight smaller than n?
a
distance(a,f,5).
distance(f,g,2). 5 1
distance(a,b,1). f b
distance(a,d,2). 2
2 c 2
distance(b,c,2). 3
g d
distance(c,d,3).
6
distance(d,e,6). e
move(A,B,to(A,B,C)) :- distance(A,B,C).
move(A,B,to(A,B,C)) :- distance(B,A,C).
weight([],0).
weight([to(_,_,C)|P],W) :- weight(P,W1), W is W1+C.
smaller(A,B,N,Result) :- search(A,B,Result),
weight(Result,W), W =< N.
Gregory Mierzwinski
Fall 2022
T HE C OMPILATION P ROCESS
Character stream
Scanner (lexical analysis)
Token stream
Parser (syntax analysis)
Parse tree
Semantic analysis
Abstract syntax tree
Intermediate code optimization
Symbol table
E = M * C ** 2
Token patterns are simple enough so that they can be specified using
regular expressions
Alphabet Σ: a finite set of symbols (e.g. binary digits, ASCII)
Token patterns are simple enough so that they can be specified using
regular expressions
Alphabet Σ: a finite set of symbols (e.g. binary digits, ASCII)
Strings (not sets!) over an alphabet; empty string: ε
Useful operation: concatenation (· or juxtaposition)
ε is the identity for concatenation (εw = wε = w)
Token patterns are simple enough so that they can be specified using
regular expressions
Alphabet Σ: a finite set of symbols (e.g. binary digits, ASCII)
Strings (not sets!) over an alphabet; empty string: ε
Useful operation: concatenation (· or juxtaposition)
ε is the identity for concatenation (εw = wε = w)
Language: a countable set of strings
Abuse of notation: For a ∈ Σ we write a instead of {a}
Useful elementary operations: union (∪, +, |) and concatenation (· or
juxtaposition): L1 L2 = L1 · L2 = {w1 w2 : w1 ∈ L1 ∧ w2 ∈ L2 }
Exponentiation: Ln = {w1 w2 · · · wn : ∀ 1 ≤ i ≤ n : wi ∈ L} (so that L0 = )
Token patterns are simple enough so that they can be specified using
regular expressions
Alphabet Σ: a finite set of symbols (e.g. binary digits, ASCII)
Strings (not sets!) over an alphabet; empty string: ε
Useful operation: concatenation (· or juxtaposition)
ε is the identity for concatenation (εw = wε = w)
Language: a countable set of strings
Abuse of notation: For a ∈ Σ we write a instead of {a}
Useful elementary operations: union (∪, +, |) and concatenation (· or
juxtaposition): L1 L2 = L1 · L2 = {w1 w2 : w1 ∈ L1 ∧ w2 ∈ L2 }
Exponentiation: Ln = {w1 w2 · · · wn : ∀ 1 ≤ i ≤ n : wi ∈ L} (so that L0 = {ε})
Token patterns are simple enough so that they can be specified using
regular expressions
Alphabet Σ: a finite set of symbols (e.g. binary digits, ASCII)
Strings (not sets!) over an alphabet; empty string: ε
Useful operation: concatenation (· or juxtaposition)
ε is the identity for concatenation (εw = wε = w)
Language: a countable set of strings
Abuse of notation: For a ∈ Σ we write a instead of {a}
Useful elementary operations: union (∪, +, |) and concatenation (· or
juxtaposition): L1 L2 = L1 · L2 = {w1 w2 : w1 ∈ L1 ∧ w2 ∈ L2 }
0
Exponentiation: Ln = {wS 1 w2 ·n· · wn : ∀ 1 ≤ i ≤ n : wi ∈ L} (so that L = {ε})
Kleene closure: L = n≥0 L
∗
Token patterns are simple enough so that they can be specified using
regular expressions
Alphabet Σ: a finite set of symbols (e.g. binary digits, ASCII)
Strings (not sets!) over an alphabet; empty string: ε
Useful operation: concatenation (· or juxtaposition)
ε is the identity for concatenation (εw = wε = w)
Language: a countable set of strings
Abuse of notation: For a ∈ Σ we write a instead of {a}
Useful elementary operations: union (∪, +, |) and concatenation (· or
juxtaposition): L1 L2 = L1 · L2 = {w1 w2 : w1 ∈ L1 ∧ w2 ∈ L2 }
0
Exponentiation: Ln = {wS 1 w2 ·n· · wn : ∀ 1 ≤ i ≤ n : wi ∈ L} (so that L = {ε})
Kleene closure: L = n≥0 L
∗
letter = [A − Za − z ]
digit = [0 − 9]
id = letter (letter | digit)∗
digits = digit+
fraction = . digits
exp = E [+−]? digits
number = digits fraction? exp?
if = if
then = then
else = else
rel op = < | > | <= | >= | == | ! =
other
When returning from *-ed states must re-
tract last character
CS 403: Scanning and Parsing Fall 2022 9 / 29
P RACTICAL E XAMPLE : L EX
The L EX language is a programming language particularly suited for
working with regular expressions
Actions can also be specified as fragments of C/C++ code
The L EX compiler compiles the L EX language (e.g., scanner.l) into
C/C++ code (lex.yy.c)
The resulting code is then compiled to produce the actual lexical analyzer
The use of this lexical analyzer is through repeatedly calling the function
yylex() which will return a new token at each invocation
The attribute value (if any) is placed in the global variable yylval
Additional global variable: yytext (the lexeme)
L EX compile the given regular expressions into one big state transition
diagram, which is then repeatedly run on the input
L EX conflict resolution rules:
Always prefer a longer to a shorter lexeme
If the longer lexeme matches more than one pattern then prefer the pattern
that comes first in the L EX program
L EX always reads one character ahead, but then retracts the lookahead
character upon returning the token
Only the lexeme itself in therefore consumed
hbalancedi ::= ε
hbalancedi ::= 0 hbalancedi 1
G = (N, Σ, R, S)
A rewriting rule A ::= v 0 ∈ R is used to rewrite its left-hand side (A) into its
right-hand side (v 0 ):
u⇒v iff ∃ x, y ∈ (N ∪ Σ)∗ : ∃ A ∈ N : u = xAy , v = xv 0 y , A ::= v 0 ∈ R
Rewriting can be chained (⇒∗ , the reflexive and transitive closure of ⇒ =
derivation)
s ⇒∗ s0 iff s = s0 , s ⇒ s0 , or there exist strings s1 , s2 , . . . , sn such that
s ⇒ s1 ⇒ s2 ⇒ · · · ⇒ sn ⇒ s0
hpali ⇒ 0hpali0 ⇒ 01hpali10 ⇒ 010hpali010 ⇒ 0101010
Definition:
1 For every a ∈ N ∪ Σ the following is a parse tree (with yield a): a
2 For every A ::= ε ∈ R the following is a parse tree (with yield ε): A
ε
3 If the following are parse trees (with yields y1 , y2 , . . . , yn , respectively):
A1 A2 An
T1 T2 ... Tn
and A ::= A1 A2 . . . An ∈ R, then the following is a parse tree (w/ yield
y1 y2 . . . yn ):
A
A1 A2 An
T1 T2 ... Tn
Theorem
The following statements are equivalent:
there exists a parse tree with root A and yield w
A ⇒∗ w
L
A ⇒∗ w
R
A ⇒∗ w
void Statement();
void Sequence();
int main() {
t = gettoken();
Statement();
if (t != EOS) printf("String not accepted\n");
return 0; }
void Sequence() {
if (t == CLS_BRACE) /* <empty> */ ;
else { /* <statement> <sequence> */
Statement();
Sequence();
} }
CS 403: Scanning and Parsing Fall 2022 19 / 29
R ECURSIVE D ESCENT E XAMPLE ( CONT ’ D )
void Statement() {
switch(t) {
case SEMICOLON: /* ; */
t = gettoken();
break;
case VAR: /* <var> = <exp> */
t = gettoken();
MustBe(EQ);
Expression();
MustBe(SEMICOLON);
break;
case IF: /* if (<expr>) <statement> else <statement> */
t = gettoken();
MustBe(OPEN_PAREN);
Expression();
MustBe(CLS_PAREN);
Statement();
MustBe(ELSE);
Statement();
break;
CS 403: Scanning and Parsing Fall 2022 20 / 29
R ECURSIVE D ESCENT E XAMPLE ( CONT ’ D )
IF OPN PAREN 〈exp〉 CLS PAREN 〈stmt〉 ELSE 〈stmt〉 〈exp〉 〈stmt〉 〈stmt〉
Node* Sequence() {
Node* current = new Node(SEQ, ...);
if (t == CLS_BRACE) /* <empty> */ ;
else { /* <statement> <sequence> */
current.addChild(Statement());
current.addChild(Sequence());
}
return current;
}
hstmti ::= ε
| VAR := hexpi
| IF hexpi THEN hstmti ELSE hstmti
| WHILE hexpi DO hstmti
| BEGIN hseqi END
hseqi ::= hstmti | hstmti ; hseqi
hstmti ::= ε
| VAR := hexpi
| IF hexpi THEN hstmti ELSE hstmti
| WHILE hexpi DO hstmti
| BEGIN hseqi END
hseqi ::= hstmti | hstmti ; hseqi
hstmti ::= ε
| VAR := hexpi
| IF hexpi THEN hstmti ELSE hstmti
| WHILE hexpi DO hstmti
| BEGIN hseqi END
hseqi ::= hstmti | hstmti ; hseqi
Solution: choose one path and stick to it (e.g., match the else-statement
with the nearest else-less if statement)
case IF:
t = gettoken();
MustBe(OPEN_PAREN);
Expression();
MustBe(CLS_PAREN);
Statement();
if (t == ELSE) {
t = gettoken();
Statement();
}
Not the same language theoretically, but differences not relevant in practice
This being said, some languages are simply not parseable using
recursive descent
hpalindromei ::= ε | 0 | 1 | 0 hpalindromei 0 | 1 hpalindromei 1
Gregory Mierzwinski
Fall 2022
T HE C OMPILATION P ROCESS
Character stream
Scanner (lexical analysis)
Token stream
Parser (syntax analysis)
Parse tree
Semantic analysis
Abstract syntax tree
Intermediate code optimization
Symbol table
Grammar Action(s)
Attributes are computed during the construction of the parse tree and are
typically included in the node objects of that tree
Two general classes of attributes:
Synthesized: passed up in the parse tree
Inherited: passed down the parse tree
Used for passing information about the context to nodes further down the
tree
CS 403: Semantic Analysis Fall 2022 4 / 26
I NHERITED ATTRIBUTES ( CONT ’ D )
http://xkcd.com/303/
struct {
12 struct
char *s;
int n;
int nums[5]; pointer (s) int (n) array (nums)
} arr [12];
char 5 int
Then the comparison will be done recursively based on the tree structure
(very much like Prolog’s unification)
Also needs some way to deal with circular types, such as marking the
visited nodes so that we do not compare them ever again
Static scoping
Method of non local access that works
Getting around restrictions can result in too many globals
C++, Java, Ada, Eiffel, Haskell all use static scoping
Dynamic scoping
Program must be traced to read
Clashes with static typing
Any type error becomes a run-time error!
Access to non local variables takes longer
Used by APL, SNOBOL, LISP (older)
But new LISP (and variants) use static scoping
Static scoping
Method of non local access that works
Getting around restrictions can result in too many globals
C++, Java, Ada, Eiffel, Haskell all use static scoping
Dynamic scoping
Program must be traced to read
Clashes with static typing
Any type error becomes a run-time error!
Access to non local variables takes longer
Used by APL, SNOBOL, LISP (older)
But new LISP (and variants) use static scoping
Overall static scoping is easier to read, is more reliable, and executes
faster
Stefan D. Bruda
Fall 2021
DATA T YPES
Algorithms + data structures = programs
Abstractions of data entities highly desirable
Program semantics embedded in data types
Data types enable semantic checking
Data types can enhance design
Data types can determine memory layout and allocation
Issues:
Extent to which type information is represented in program
How types are constructed
How types are checked (done)
When types are checked (done)
Determining types
Explicit typing → type determined at declaration, usually invariant
throughout execution (Pascal, C, C++, Java)
Implicit typing → type determined by usage (Prolog, Lisp)
Mixed → implicit typing by default, but allows explicit declarations (Miranda,
Haskell, ML)
Determining types
Explicit typing → type determined at declaration, usually invariant
throughout execution (Pascal, C, C++, Java)
Implicit typing → type determined by usage (Prolog, Lisp)
Mixed → implicit typing by default, but allows explicit declarations (Miranda,
Haskell, ML)
Simple types
Pre-defined (int, bool, etc.)
Enumeration
enum colour {red, green, blue};
data Colour = Red | Green | Blue
Ordinal (discrete order on elements i.e., not real)
Determining types
Explicit typing → type determined at declaration, usually invariant
throughout execution (Pascal, C, C++, Java)
Implicit typing → type determined by usage (Prolog, Lisp)
Mixed → implicit typing by default, but allows explicit declarations (Miranda,
Haskell, ML)
Simple types
Pre-defined (int, bool, etc.)
Enumeration
enum colour {red, green, blue};
data Colour = Red | Green | Blue
Ordinal (discrete order on elements i.e., not real)
Type constructors: cartesian product
U × V = {(u, v ) : u ∈ U, v ∈ V }; p1 : U × V → U; p2 : U × V → V
Record implementation struct icr {int i; char c; double r;};
Tuple implementation type Icr = (Int, Char, Double)
Main> aStack
Push 0 (Push 1 (Push 2 Empty))
Main> :type aStack
aStack :: Stack Integer
Main> isEmpty aStack
False
Main> pop aStack
Push 1 (Push 2 Empty)
Main> let aStack = push 0 empty in pop (pop aStack)
Main>
CS 403: Types and Classes (S. D. Bruda) Fall 2021 7 / 25
S IMPLIFIED S TACK IN H ASKELL
-- Stack.hs Main> aStack
module Stack where Push 0 (Push 1 (Push 2 Empty))
data Stack a = Empty | Main> :r
Push a (Stack a) Main> aStack
deriving Show Push 0 (Push 1 (Push 2 Empty))
Main> :type aStack
pop :: Stack a -> Stack a aStack :: Stack Integer
top :: Stack a -> a Main> isEmpty aStack
isEmpty :: Stack a -> Bool False
Main> pop aStack
pop (Push e s) = s Push 1 (Push 2 Empty)
pop (Empty) = error "pop (empty)." Main> let aStack = Push 0 Empty
top (Push e s) = e in pop (pop aStack)
top (Empty) = error "top (empty)"
isEmpty (Push e s) = False Program error: pop (empty).
isEmpty Empty = True
-- main.hs
module Main where
import Stack
type Queue(Elm)
Operators:
create : ∅ → Queue
enqueue : Elm × Queue → Queue
dequeue : Queue → Queue
front : Queue → Elm
isEmpty : Queue → Bool
Axioms:
front(enqueue(e, Q)) = if (isEmpty(Q)) then e else front(Q)
dequeue(enqueue(e, Q)) = if (isEmpty(Q)) then Q
else enqueue(e, dequeue(Q))
isEmpty(create) = true
isEmpty(enqueue(e, Q)) = false
Restrictions:
dequeue(empty)
front(empty)
type Complex
Operators:
create : Real × Real → Complex
real : Complex → Real
imaginary : Complex → Real
+ : Complex × Complex → Complex
× : Complex × Complex → Complex
...
Axioms:
real(create(r , i)) = r
imaginary(create(r , i)) = i
real(x + y ) = real(x) + real(y )
imaginary(x + y ) = imaginary(x) + imaginary(y)
real(x × y ) = real(x) × real(y ) − imaginary(x) × imaginary(y)
imaginary(x × y ) = imaginary(x) × real(y ) + real(x) × imaginary(y )
...
GraphicalObject obj;
Line line;
... // initialization, etc.
Everything is an object
Advantage: elegance, purity, least surprise (everything works in the same
way)
Disadvantage: potential slow operations on simple objects (e.g., float)
However all modern languages where everything is an object have
implementation tricks that minimize the overhead
Notable examples: C++, Haskell
Include an imperative-style typing system for primitive types, but make
everything else objects
Advantage: fast operations on simple objects, relatively small typing system
Disadvantage: confusion caused by the existence of two separate type
systems
Can also have class wrappers for primitive types (such as Integer, Float,
Character, etc. in Java)
Single mainstream example: Java
Cake → ChockolateCake
%
Dessert
&
Scone → ButteredScone
Definitions
1 Feast(Dessert d, Scone s) { ... }
2 Feast(Cake c, Dessert d) { ... }
3 Feast(ChockolateCake cc, Scone s) { ... }
Usage
Feast(dessertref, sconeref)
Feast(chockolatecakeref, dessertref)
Feast(chockolatecakeref, butteredsconeref)
Cake → ChockolateCake
%
Dessert
&
Scone → ButteredScone
Definitions
1 Feast(Dessert d, Scone s) { ... }
2 Feast(Cake c, Dessert d) { ... }
3 Feast(ChockolateCake cc, Scone s) { ... }
Usage
Feast(dessertref, sconeref) → uses definition 1
Feast(chockolatecakeref, dessertref) → uses definition 2
Feast(chockolatecakeref, butteredsconeref) → uses definition 3
class Printable {
void print() { /* default implementation */ } }
class Person extends Printable {
void print() { /* overriding method */ } }
Stefan D. Bruda
Fall 2021
P ROCEDURES AND PARAMETERS
Two fundamental abstraction facilities: data abstraction and process
abstraction
In particular subprograms simplify complex programs though abstraction
Abstraction of actions
Called by name with arguments: Calculate Pay(pay rate, hours worked)
Single entry (caller is suspended for the duration)
Control always returns to the caller when the subprogram terminates
Procedures (subroutines) and functions (or methods in OOP languages)
Design issues for subprograms
Are local variables static or dynamic?
The local reference environment may be static (historical significance only)
The local reference environment may be stack-based (all modern languages)
What are the parameter passing methods?
Are the types of the actual and formal parameters checked?
Can subprograms be passed as parameters? What is the referencing
environment?
Can subprogram definitions be nested?
Can subprograms be overloaded or generic?
Are side effects allowed?
What type of variables can be returned?
CS 403: Subprograms (S. D. Bruda) Fall 2021 1 / 13
PARAMETER PASSING M ECHANISMS
Pass by value
Ada: The arguments are expressions evaluated at the time of the call
Parameters are constant values
All the parameters in the body of the procedure will be replaced by those values
Main> foo 10
22 -- it would be 40 with shallow binding
Return address
Contains pointer back to code segment + offset of the address following the
call
Static link
Implements access to non-local variables for deep/lexical binding
Non-local references could be made by searching down the static chain
However, we cannot search at run time (no name information anymore!)
But scopes are known at compile time so the compuler knows the length of
the static chain
Thus a non-local variable is represented by an ordered pair of integers
(chain offset,local offset)
References to variables beyond static parent are costly
Dynamic link
Represents the history of the execution
Implements access to non-local variables for shallow binding
Parameters
Local variables
Stefan D. Bruda
Fall 2021
A LL A BOUT P OINTERS
http://xkcd.com/138/
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 1 / 27
P OINTERS
What is a pointer?
The index of a book contains pointers
A URL (e.g., http://cs.ubishops.ca/home/cs403) is a pointer
A street address is a pointer
What is then a forwarding address?
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 2 / 27
P OINTERS
What is a pointer?
The index of a book contains pointers
A URL (e.g., http://cs.ubishops.ca/home/cs403) is a pointer
A street address is a pointer
What is then a forwarding address? → a pointer to a pointer!
OK, so what is then a (C/C++) pointer?
Often need to refer to some object without making a copy of the object itself
Computer memory contains data which can be accessed using an address
A pointer is nothing more than such an address
Alternatively, computer memory is like an array holding data
A pointer then is an index in such an array
What are pointers physically?
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 2 / 27
P OINTERS
What is a pointer?
The index of a book contains pointers
A URL (e.g., http://cs.ubishops.ca/home/cs403) is a pointer
A street address is a pointer
What is then a forwarding address? → a pointer to a pointer!
OK, so what is then a (C/C++) pointer?
Often need to refer to some object without making a copy of the object itself
Computer memory contains data which can be accessed using an address
A pointer is nothing more than such an address
Alternatively, computer memory is like an array holding data
A pointer then is an index in such an array
What are pointers physically?
Since a pointer is an address, it is usually represented internally as
unsigned int
Pointers can (just as array indices) be stored in variables.
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 2 / 27
C/C++ P OINTERS
x px ppx
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 3 / 27
P OINTER T YPES
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 4 / 27
P OINTER T YPES
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 4 / 27
P OINTER T YPES
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 4 / 27
P OINTER A RITHMETIC
x−2 x x+3
Memory
0x34791B
0x34791C
0x34791D
0x34791E
0x34791F
0x347920
0x347921
0x347922
0x347923
0x347924
0x437925
0x437926
0x437927
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 5 / 27
P OINTER A RITHMETIC
Memory
0x34791B
0x34791C
0x34791D
0x34791E
0x34791F
0x347920
0x347921
0x347922
0x347923
0x347924
0x437925
0x437926
0x437927
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 5 / 27
P OINTER A RITHMETIC
Memory
0x34791B
0x34791C
0x34791D
0x34791E
0x34791F
0x347920
0x347921
0x347922
0x347923
0x347924
0x437925
0x437926
0x437927
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 5 / 27
C A RRAYS AND P OINTERS
nums[0]
nums[1]
nums[2]
nums[3]
nums[4]
nums[5]
nums
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 6 / 27
C A RRAYS VERSUS P OINTERS
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 7 / 27
A RRAYS , P OINTERS , AND F UNCTIONS
#include <iostream>
using namespace std;
void translate(char a) {
if (a == ’A’) a = ’5’; else a = ’0’;
}
void translate(char* array, int size) {
for (int i = 0; i < size; i++) {
if (array[i] == ’A’) array[i] = ’5’;
else array[i] = ’0’;
}
}
int main () {
char mark = ’A’; char marks[5] = {’A’,’F’,’A’,’F’,’F’};
translate(mark);
translate(marks,5);
cout << mark << "\n";
for (int i = 0; i < 5; i++)
cout << marks[i] << " ";
cout << "\n";
}
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 8 / 27
A RRAYS , P OINTERS , AND F UNCTIONS
#include <iostream>
using namespace std;
void translate(char a) {
if (a == ’A’) a = ’5’; else a = ’0’;
}
void translate(char* array, int size) {
for (int i = 0; i < size; i++) {
if (array[i] == ’A’) array[i] = ’5’;
else array[i] = ’0’;
}
}
int main () {
char mark = ’A’; char marks[5] = {’A’,’F’,’A’,’F’,’F’};
translate(mark);
translate(marks,5);
cout << mark << "\n";
for (int i = 0; i < 5; i++)
cout << marks[i] << " "; Output:
cout << "\n";
} A
5 0 5 0 0
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 8 / 27
A RRAYS , P OINTERS , AND F UNCTIONS ( CONT ’ D )
#include <iostream>
using namespace std;
void translate(char *a) {
if (*a == ’A’) *a = ’5’; else *a = ’0’;
}
void translate(char* array, int size) {
for (int i = 0; i < size; i++) {
if (array[i] == ’A’) array[i] = ’5’;
else array[i] = ’0’;
}
}
int main () {
char mark = ’A’; char marks[5] = {’A’,’F’,’A’,’F’,’F’};
translate(&mark);
translate(marks,5);
cout << mark << "\n";
for (int i = 0; i < 5; i++)
cout << marks[i] << " "; Output:
cout << "\n";
} 5
5 0 5 0 0
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 9 / 27
P OINTERS AND F UNCTIONS
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 10 / 27
C ALL BY R EFERENCE P OINTER
foo.cc
void increment (int* i) {
*i = *i + 1;
}
int main () {
int n = 0;
increment(&n);
increment1(&n);
}
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 11 / 27
C ALL BY R EFERENCE
foo.cc → no more messy syntax!
void increment (int& i) {
i = i + 1;
}
int main () {
int n = 0;
increment(n);
increment1(n);
}
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 12 / 27
R EFERENCES AND C ALLING C ONVENTIONS
A reference is just like a pointer, but with a nicer interface
An alias to an object, but it hides such an indirection from the programmer
Must be typed and can only refer to the declared type (int &r; can only
refer to int, etc)
Must always refer to something in C++ but may refer to the null object in
Java
Explicit in C++ (unary operator &) implicit in Java for all objects and
nonexistent for primitive types
Implicit calling conventions:
What Java C++
Primitive types value value
(int, float, etc.)
Arrays reference value
Objects reference value
In C++ everything is passed by value unless explicitly stated otherwise (by
declaring the respective parameter as a reference)
C arrays are apparently passed by reference, but only because of the array
structure (pointer + content)
In Java there is no other way to pass arguments than the implicit one
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 13 / 27
P OINTERS AND S IMPLE L INKED L ISTS
struct cons_cell {
int car;
cons_cell* cdr; // must use pointers, else the type is infinitely recursive
};
typedef cons_cell* list;
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 14 / 27
DYNAMIC M EMORY M ANAGEMENT
new allocates memory for your data. The following are (somehow)
equivalent:
char message[256]; char* pmessage;
pmessage = new char[256];
Exception:
message takes care of itself (i.e., gets deleted when it is no longer in use),
whereas
pmessage however must be explicitly deleted when it is no longer needed:
delete[] pmessage;
Perrils of not using new:
list cons (int car,
list cdr = nil) {
cons_cell new_cons;
new_cons.car = car; int main () {
new_cons.cdr = cdr; list bad = cons(1);
return &new_cons; cout << car(bad); → Boom!
} }
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 15 / 27
DYNAMIC M EMORY M ANAGEMENT ( CONT ’ D )
Stack
main:
Heap
i int
cons_cell zap
!! foo:
c cons_cell
Memory
Conclusion: foo returns l foo returns c
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 16 / 27
S AY N O TO M EMORY L EAKS
If you create something using new then you must eventually delete it
using delete
list rmth (list cons, int which) {
list place = cons;
for (int i = 0; i < which - 1; i++) {
if (null(place))
break;
place = place -> cdr;
}
if (! null(place) ) {
if (null(cdr(place)))
place -> cdr = nil;
else {
list to delete = cdr(place);
place -> cdr = cdr(place -> cdr);
delete to delete;
}
}
return cons;
}
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 17 / 27
T HE P ERILS OF D ELETE
An object and all the pointers to it (when they are dereferenced) alias the
same location
Assigning a value through one channel will affect all the other channels
Memory management through one channel will affect all the other channels
as well
Thou shall not leak memory, but also:
Thou shall not leave stale (dangling) pointers behind
char* str = new char[128]; → allocate memory for str
strcpy(str,"hello"); → put something in there (“hello”)
char* p = str; → p points to the same thing
delete [] p; → “hello” is gone,
str is a stale pointer!!
Thou shall not dereference deleted pointers
strcpy(str,"hi"); → str already deleted!!
Thou shall not delete a pointer more than once
delete str; → str already deleted!!
You can however delete null pointers as many times as you wish!
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 18 / 27
T HE P ERILS OF D ELETE
An object and all the pointers to it (when they are dereferenced) alias the
same location
Assigning a value through one channel will affect all the other channels
Memory management through one channel will affect all the other channels
as well
Thou shall not leak memory, but also:
Thou shall not leave stale (dangling) pointers behind
char* str = new char[128]; → allocate memory for str
strcpy(str,"hello"); → put something in there (“hello”)
char* p = str; → p points to the same thing
delete [] p; → “hello” is gone,
str is a stale pointer!!
Thou shall not dereference deleted pointers
strcpy(str,"hi"); → str already deleted!!
Thou shall not delete a pointer more than once
delete str; → str already deleted!!
You can however delete null pointers as many times as you wish!
So assign zero to deleted pointers whenever possible (not a panaceum)
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 18 / 27
T HE P ERILS OF D ELETE ( CONT ’ D )
// Copy stefan
struct prof { bruda = new prof;
char* name;
char* dept; // (a) Shallow copying
}; bruda−>name = stefan−>name;
char *csc = new char[30]; bruda−>dept = stefan−>dept;
strcpy (csc,"Computer Science");
prof *stefan, *dimitri, *bruda; // Can we delete stefan now??
stefan = new prof; dimitri = new prof; // (b) Deep copying
stefan−>name = new char[30];
dimitri−>name = new char[30]; bruda−>name = new char[30];
strcpy(stefan−>name,"Stefan Bruda"); bruda−>dept = new char[30];
strcpy(dimitri−>name,"Dimitri Vouliouris"); strcpy(bruda.name,stefan.name);
strcpy(bruda.dept,stefan.dept);
stefan−>dept = csc;
dimitri−>dept = csc; Exogenous data // Can we delete stefan now??
// Delete dimitri
delete dimitri−>name; OK
delete dimitri−>dept;
delete dimitri; ???
Indigenous data
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 19 / 27
P OINTERS AS F IRST-O RDER C++ O BJECTS
They are objects that look and feel like pointers, but are smarter
Look like pointers = have the same interface that pointers do
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 20 / 27
P OINTERS AS F IRST-O RDER C++ O BJECTS
They are objects that look and feel like pointers, but are smarter
Look like pointers = have the same interface that pointers do
They need to support pointer operations like dereferencing (operator*) and
indirection (operator ->)
An object that looks and feels like something else is called a proxy object, or
just proxy
Smarter = do things that regular pointers don’t
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 20 / 27
P OINTERS AS F IRST-O RDER C++ O BJECTS
They are objects that look and feel like pointers, but are smarter
Look like pointers = have the same interface that pointers do
They need to support pointer operations like dereferencing (operator*) and
indirection (operator ->)
An object that looks and feels like something else is called a proxy object, or
just proxy
Smarter = do things that regular pointers don’t
Such as memory management
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 20 / 27
P OINTERS AS F IRST-O RDER C++ O BJECTS
They are objects that look and feel like pointers, but are smarter
Look like pointers = have the same interface that pointers do
They need to support pointer operations like dereferencing (operator*) and
indirection (operator ->)
An object that looks and feels like something else is called a proxy object, or
just proxy
Smarter = do things that regular pointers don’t
Such as memory management
Simplest example: auto_ptr, included in the standard C++ library.
header: <memory>
template <class T> class auto_ptr {
T* ptr;
public:
explicit auto_ptr(T* p = 0) : ptr(p) {}
~auto_ptr() {delete ptr;}
T& operator*() {return *ptr;}
T* operator->() {return ptr;}
// ...
};
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 20 / 27
E XAMPLE OF U SE
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 21 / 27
W HY U SE : L ESS B UGS
Automatic cleanup: They clean after themselves, so there is no chance
you will forget to deallocate
Automatic initialization: You all know what a non-initialized pointer does;
the default constructor now does the initialization to zero for you
Stale pointers: As stated before, stale pointers are evil:
MyClass* p(new MyClass);
MyClass* q = p;
delete p;
// p->DoSomething(); // We don’t do that, p is stale
p = 0; // we do this instead
q->DoSomething(); // Ouch, q is still stale!
Smart pointers can set their content to 0 once copied, e.g.
template <class T>
auto_ptr<T>& auto_ptr<T>::operator=(auto_ptr<T>& rhs) {
if (this != &rhs) {
delete ptr; ptr = rhs.ptr; rhs.ptr = 0;
}
return *this; }
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 22 / 27
S TALE P OINTERS ( CONT ’ D )
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 23 / 27
W HY U SE : E XCEPTION S AFETY
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 24 / 27
W HY U SE : E XCEPTION S AFETY
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 24 / 27
W HY U SE : G ARBAGE C OLLECTION , E FFICIENCY
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 25 / 27
W HY U SE : G ARBAGE C OLLECTION , E FFICIENCY
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 25 / 27
W HY U SE : STL C ONTAINERS
Base b; Derived d;
vector<Base> v;
v.push_back(b); // OK
v.push_back(d); // no cake
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 26 / 27
W HY U SE : STL C ONTAINERS
Base b; Derived d;
vector<Base> v;
v.push_back(b); // OK
v.push_back(d); // no cake
You go around this by using pointers:
vector<Base*> v;
v.push_back(new Base); // OK
v.push_back(new Derived); // OK
// obligatory cleanup, disappears when using smart pointers
for (vector<Base*>::iterator i = v.begin(); i != v.end(); ++i)
delete *i;
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 26 / 27
H OW TO C HOOSE YOUR S MART P OINTER
The simplest smart pointer auto ptr is probable suited for local variables
A copied pointer is usually useful for class members
Think copy constructor and you think deep copy
Due to their nature STL containers require garbage collected pointers
(e.g., reference counting)
Whenever you have big objects you are probably better off using copy on
write pointers
CS 403: Pointers, References, and Memory Management (S. D. Bruda) Fall 2021 27 / 27