Here are the key points about functional programming in Scala covered in the document:- Immutable objects - fields of functional objects are immutable- Pattern matching - Inductive (algebraic) data types- Recursive functions- First-class functions (mentioned but not covered)Some examples covered:- Rational number class with immutable fields and functional methods like add- Checking pre-conditions with require- Overriding toString- Auxiliary constructors- Private fields and methods- Self references with this- Using functions as infix operatorsThe document provides examples of functional programming concepts in Scala like immutable data structures, pattern matching, recursion, and functional methods. It compares
Ähnlich wie Here are the key points about functional programming in Scala covered in the document:- Immutable objects - fields of functional objects are immutable- Pattern matching - Inductive (algebraic) data types- Recursive functions- First-class functions (mentioned but not covered)Some examples covered:- Rational number class with immutable fields and functional methods like add- Checking pre-conditions with require- Overriding toString- Auxiliary constructors- Private fields and methods- Self references with this- Using functions as infix operatorsThe document provides examples of functional programming concepts in Scala like immutable data structures, pattern matching, recursion, and functional methods. It compares
Ähnlich wie Here are the key points about functional programming in Scala covered in the document:- Immutable objects - fields of functional objects are immutable- Pattern matching - Inductive (algebraic) data types- Recursive functions- First-class functions (mentioned but not covered)Some examples covered:- Rational number class with immutable fields and functional methods like add- Checking pre-conditions with require- Overriding toString- Auxiliary constructors- Private fields and methods- Self references with this- Using functions as infix operatorsThe document provides examples of functional programming concepts in Scala like immutable data structures, pattern matching, recursion, and functional methods. It compares (20)
Compiler Construction | Lecture 5 | Transformation by Term Rewriting
Here are the key points about functional programming in Scala covered in the document:- Immutable objects - fields of functional objects are immutable- Pattern matching - Inductive (algebraic) data types- Recursive functions- First-class functions (mentioned but not covered)Some examples covered:- Rational number class with immutable fields and functional methods like add- Checking pre-conditions with require- Overriding toString- Auxiliary constructors- Private fields and methods- Self references with this- Using functions as infix operatorsThe document provides examples of functional programming concepts in Scala like immutable data structures, pattern matching, recursion, and functional methods. It compares
1. Lecture 5: Functional Programming
TI1220 2012-2013
Concepts of Programming Languages
Eelco Visser / TU Delft
2.
3. John McCarthy (September 4, 1927 –
October 24, 2011) was an American
computer scientist and cognitive scientist.
He coined the term "artificial
intelligence" (AI), developed the Lisp
programming language family, significantly
influenced the design of the ALGOL
programming language, popularized
timesharing, and was very influential in the
early development of AI.
Around 1959, he invented so-called "garbage collection" methods to
solve problems in Lisp. Based on the lambda calculus, Lisp soon
became the programming language of choice for AI applications after
its publication in 1960.
http://en.wikipedia.org/wiki/John_McCarthy_(computer_scientist)
4. Outline
From the lab:
Unit testing
Graded assignment 1
Functional objects in Scala
Pattern matching
Recursion and induction
Algebraic data types
Binary trees
Algebraic data types in C
6. Tests
• check that your code is correct
• regression testing: don’t make the same mistake twice
Coverage
• a test for each representative case
Test-driven development
• (1) define tests for representative cases
• (2) write code
• (3) test
7. Unit Testing in Scala
import org.scalatest.Suite
class <NameOfTestClass> extends Suite {
import <ClassUnderTest>._
def <testNameOfTest> {
expect(<expected result>) {
<computation>
}
}
}
8. /* import test framework .h */
#include "solution.c"
#include "CuTest.h"
#include "CuJoin.h"
Unit Testing in C
/* your imported libraries */
#include <string.h>
/* signatures of all functions being tested */
char* wc(char* data);
/* defined tests */
void test_1(CuTest *tc) {
char* wcout = wc("hellon world");
char* expected = "2 2 10 12";
CuAssertTrue(tc, !strcmp(wcout,expected));
}
/* hook all your tests into the harness */
void testHooker(CuSuite* intoSuite){
SUITE_ADD_TEST(intoSuite, test_1);
}
9. test("Changing properties", function() {
var obj = {x : 3};
expect(5);
ok(changeProp, "function exists");
equal(obj.x, 3);
equal(obj.y, undefined);
changeProp(obj);
equal(obj.x, 42);
equal(obj.y, 9);
});
Unit Testing in JavaScript
10. Graded Assignment 1
Algebraic datatypes in C
Dynamic dispatch in C
Important dates
Deadline: April 2, 2013 23:59
Extension: April 5, 2013 23:59
Submitting after extension date is not possible
Maximum penalty for submitting after deadline: 6 points
Minimum grade needed: 4
Grade: 70% unit tests, 30% check lists
Grade for GAs: average of four assignments
11. Algebraic Datatypes in C
abstract class XML
case class Text(t: String) extends XML
case class Elem(tag: String, elems: List[XML]) extends XML
object Solution {
def text(elems1: List[XML]): List[XML] =
elems1.flatMap(_ match {
case t@Text(_) => List[XML](t)
case Elem(_, elems2) => text(elems2)
})
}
translate this Scala program to an equivalent C program
12. // values
abstract class Value {
def value: Int Dynamic Dispatch in C
def isFailure: Boolean
def +(that: Value): Value
def *(that: Value): Value
}
object LookupFailure extends Value {
def value: Int = 0
translate this Scala program
def isFailure: Boolean = true to an equivalent C program
def +(that: Value) = LookupFailure
def *(that: Value) = LookupFailure
}
class IntValue(v : Int) extends Value {
val value = v
def isFailure: Boolean = false
def +(that: Value) = that match {
case v: IntValue => new IntValue(value + v.value)
case _ => LookupFailure
}
def *(that: Value) = that match {
case v: IntValue => new IntValue(value * v.value)
case _ => LookupFailure
}
}
13. // environments
abstract class Env { Dynamic Dispatch in C
def lookup(x: String): Value
}
class MtEnv extends Env {
def lookup(x: String): Value = LookupFailure
}
class Bind(key: String, value: Int, env: Env) extends Env {
def lookup(x: String): Value =
if(x == key) new IntValue(value) else env.lookup(x);
}
20. Constructing a Rational
class parameters
class Rational(n: Int, d: Int) {
println("Created " + n + "/" + d)
}
scala> new Rational(1, 2)
Created 1/2
res0: Rational = Rational@2d83e895
21. Immutable object trade-offs
Advantages
• easier reasoning
• pass around freely (no risk of undesired mutations)
• cannot be changed concurrently in two threads
• immutable object make safe hash table keys
Disadvantages
• copying large object graphs vs in-place update
22. Overriding Methods
class Rational(n: Int, d: Int) {
override def toString = n + "/" + d
}
scala> val half = new
Rational(1, 2)
half: Rational = 1/2
23. Checking Pre-conditions
class Rational(n: Int, d: Int) {
require(d != 0)
override def toString = n + "/" + d
}
scala> val half = new Rational(1, 0)
java.lang.IllegalArgumentException: requirement failed
24. class Rational(n: Int, d: Int) {
require(d != 0)
override def toString = n + "/" + d
def add(that: Rational): Rational =
new Rational(n * that.d + that.n * d, d * that.d)
}
Adding Rationals Functionally
25. Visibility of Class Parameters
class Rational(n: Int, d: Int) {
require(d != 0)
override def toString = n + "/" + d
def add(that: Rational): Rational =
new Rational(n * that.d + that.n * d, d * that.d)
}
$ fsc Rational.scala
Rational.scala:5: error: value d is not a member of Rational
new Rational(n * that.d + that.n * d, d * that.d)
^
Rational.scala:5: error: value d is not a member of Rational
new Rational(n * that.d + that.n * d, d * that.d)
^
two errors found
26. Functional Fields
class Rational(n: Int, d: Int) {
require(d != 0)
val numer: Int = n
val denom: Int = d
override def toString = numer + "/" + denom
def add(that: Rational): Rational =
new Rational(
numer * that.denom + that.numer * denom,
denom * that.denom)
}
scala> new Rational(1,2) add new Rational(2,3)
res0: Rational = 7/6
27. Non-functional Objects
class ImperativeRational(n: Int, d: Int) {
require(d != 0)
var numer: Int = n
var denom: Int = d
override def toString = numer + "/" + denom
def add(that: ImperativeRational) {
numer = numer * that.denom + that.numer * denom;
denom = denom * that.denom;
}
}
scala> val half = new ImperativeRational(1, 2)
Destructive Update half: ImperativeRational = 1/2
scala> val twothirds = new ImperativeRational(2,3)
twothirds: ImperativeRational = 2/3
scala> half.add(twothirds)
scala> half
res1: ImperativeRational = 7/6
28. Self References
def lessThan(that: Rational) =
this.numer * that.denom < that.numer * this.denom
def max(that: Rational) =
if (this.lessThan(that)) that else this
this: optional when referring to fields
29. Auxiliary Constructors
class Rational(n: Int, d: Int) {
require(d != 0)
val numer = n
val denom = d
def this(n: Int) = this(n, 1)
// auxiliary constructor
...
}
scala> new Rational(6)
res1: Rational = 6/1
30. Private Fields and Methods
class Rational(n: Int, d: Int) {
require(d != 0)
private val g = gcd(n.abs, d.abs)
val numer = n / g
val denom = d / g
...
private def gcd(a: Int, b: Int): Int =
if (b == 0) a else gcd(b, a % b)
}
scala> new Rational(6,42)
res1: Rational = 1/7
31. Using Functions as Infix Operators
def add(that: Rational): Rational =
new Rational(numer * that.denom + that.numer * denom,
denom * that.denom)
scala> new Rational(1,2).add(new Rational(2,3))
res0: Rational = 7/6
scala> new Rational(1,2) add new Rational(2,3)
res0: Rational = 7/6
32. def +(that: Rational): Rational =
new Rational(numer * that.denom + that.numer * denom,
denom * that.denom)
def *(that: Rational): Rational =
new Rational(numer * that.numer, denom * that.denom)
scala> val d = a + b * c
d: Rational = 11/14
Operator Identifiers
scala> val d = a.+(b.*(c))
d: Rational = 11/14
scala> val d = a * b + c
d: Rational = 16/21
scala> val d = (a.*(b)).+(c)
d: Rational = 16/21
Invoking Operators
33. How many Rational objects are created while executing:
class Rational(n: Int, d: Int) {
require(d != 0)
val numer: Int = n
val denom: Int = d
override def toString = numer + "/" + denom
def +(that: Rational): Rational =
new Rational(
numer * that.denom + that.numer * denom,
denom * that.denom)
}
var half = new Rational(1,2)
half = half + half + half
(a) 1
(b) 2
(c) 3
(d) 4 Quiz
34. Alphanumeric identifier
• identifier: [$A-Za-z_][$A-Za-z_0-9]* ($ reserved for Scala compiler)
• camel-case convention: toString, HashSet
Operator identifier
• Unicode set of mathematical symbols(Sm) or other symbols(So), or
to the 7-bit ASCII characters that are not letters, digits,
parentheses, square brackets, curly braces, single or double quote,
or an underscore, period,semi-colon, comma, or back tick
character.
Literal Identifier
Identifier Syntax
• arbitrary string enclosed in back ticks (` . . . `).
35. Method Overloading
def *(that: Rational): Rational =
new Rational(numer * that.numer, denom * that.denom)
def *(i: Int): Rational =
new Rational(numer * i, denom)
scala> val c = new Rational(3,7)
c: Rational = 3/7
In a method call, the
scala> c * 2
compiler picks the version
res1: Rational = 6/7
of an overloaded method
that correctly matches the
types of the arguments.
36. Method Overloading
does not apply to this
def *(that: Rational): Rational =
new Rational(numer * that.numer, denom * that.denom)
def *(i: Int): Rational =
new Rational(numer * i, denom)
scala> 2 * c
<console>:7: error: overloaded method value * with alternatives:
(Double)Double <and> (Float)Float <and> (Long)Long <and> (Int)Int <and>
(Char)Int <and> (Short)Int <and> (Byte)Int cannot be applied to (Rational)
2 * c
^
37. Implicit Conversions
def *(that: Rational): Rational =
new Rational(numer * that.numer, denom * that.denom)
def *(i: Int): Rational =
new Rational(numer * i, denom)
implicit def intToRational(x: Int) = new Rational(x)
scala> 2 * c
res4: Rational = 6/7
38. Functional Objects - Summary
Immutable objects
• class parameters
• immutable fields (val)
• methods don’t change object, but return value
Natural, concise notation
• methods as infix operators, operator identifiers
• method overloading
• implicit conversion
40. e0 match {
case 1 => e1;
case 2 => e2; Scala’s Match
case _ => e3
}
Match is similar to Java switch. Differences:
• Match is expression: returns a value
• Alternatives never fall through
• MatchError when no pattern matches
41. val firstArg = if (args.length > 0) args(0) else ""
firstArg match {
case "salt" => println("pepper")
case "chips" => println("salsa")
case "eggs" => println("bacon")
Choosing between Actions
case _ => println("huh?")
}
val firstArg = if (!args.isEmpty) args(0) else ""
val friend =
firstArg match {
case "salt" => "pepper"
case "chips" => "salsa"
case "eggs" => "bacon"
case _ => "huh?" Choosing between Values
}
println(friend)
42. def describe(x: Any) = x match {
case 5 => "five"
case true => "truth"
case "hello" => "hi!" Constant Patterns
case Nil => "the empty list"
case _ => "something else"
}
expr match {
case 0 => "zero"
case somethingElse => "not zero: " + somethingElse
}
Pattern Variables
44. Inductive Definitions
Natural number
• 0 is a number
• if n is a number then n + 1 is a number
• nothing else is a natural number
45. def property(n: Int): Boolean =
n match {
case 0 => // base case
case m => ... property (m - 1) ...
// recursive case for n + 1
}
def f(n: Int): Int =
n match {
case 0 => // base case
case m => ... f(m - 1) ...
// recursive case for n + 1
}
Induction Principle
46. def isEven(n: Int): Boolean =
n match {
case 0 => ?
case m => ?
}
def isOdd(n: Int): Boolean =
n match {
case 0 => ?
case m => ?
}
47. def isEven(n: Int): Boolean =
n match {
case 0 => true
case m => isOdd(m - 1)
}
def isOdd(n: Int): Boolean =
n match {
case 0 => false
case m => isEven(m - 1)
}
48. def power(n: Int, exp: Int): Int =
exp match {
case 0 => ?
case m =>
?
}
49. def power(n: Int, exp: Int): Int =
exp match {
case 0 => 1
case m =>
if(exp % 2 == 1)
n * power(n, m - 1)
else
power(n * n, m / 2)
}
51. Inductive Data Structures
Natural number
• 0 is a number
• if n is a number then n + 1 is a number
List
• Empty list is a list
• If L is a list then adding an element in
front of L produces a list
52. abstract class IntList
case class Nil() extends IntList
// Nil() is a list (the empty list)
case class Cons(hd: Int, tail: IntList) extends IntList
// if hd is an Int and tail is an IntList
// then Cons(hd, tl) is an IntList
Nil()
Cons(1, Nil()) Scala: Case Classes
Cons(2, Cons(1, Nil()))
Cons(1, Cons(2, Cons(3, Nil())))
...
53. abstract class IntList
case class Nil() extends IntList
case class Cons(hd: Int, tail: IntList) extends IntList
def f(xs: IntList): T =
xs match {
case Nil() => // base case
case Cons(x, ys) => ... f(ys) ...
// recursive case
}
Induction Principle for IntList
54. length of list xs
def length(xs: IntList): Int =
xs match {
case Nil() => ?
case Cons(x, ys) => ?
}
55. length of list xs
def length(xs: IntList): Int =
xs match {
case Nil() => 0
case Cons(x, ys) => 1 + length(ys)
}
56. sum of the integers in xs
def sum(xs: IntList): Int =
xs match {
case Nil() => ?
case Cons(x, ys) => ?
}
57. sum of the integers in xs
def sum(xs: IntList): Int =
xs match {
case Nil() => 0
case Cons(x, ys) => x + sum(ys)
}
58. def product(xs: IntList): Int =
xs match {
case Nil() => ?
case Cons(x, ys) => ?
}
product of the integers in xs
59. def product(xs: IntList): Int =
xs match {
case Nil() => 1
case Cons(x, ys) => x * product(ys)
}
product of the integers in xs
60. def append(xs: IntList, ys: IntList): IntList =
xs match {
case Nil() => ?
case Cons(x, zs) => ?
}
append elements of ys to xs
61. def append(xs: IntList, ys: IntList): IntList =
xs match {
case Nil() => ys
case Cons(x, zs) => Cons(x, append(zs, ys))
}
append elements of ys to xs
62. elements of xs in reverse
def reverse(xs: IntList): IntList =
xs match {
case Nil() => ?
case Cons(y, ys)
=> ?
}
63. elements of xs in reverse
def reverse(xs: IntList): IntList =
xs match {
case Nil() => Nil()
case Cons(y, ys)
=> append(reverse(ys), Cons(y, Nil()))
}
64. def reverse(xs: IntList): IntList =
xs match {
case Nil() => Nil()
case Cons(y, ys)
=> append(reverse(ys), Cons(y, Nil()))
}
def reverse(xs: IntList): IntList =
reverseAcc(xs, Nil())
def reverseAcc(xs: IntList, rest: IntList): IntList =
xs match {
case Nil() => rest
case Cons(y, ys)
=> reverseAcc(ys, Cons(y, rest))
}
reverse in linear time using accumulator
65. the list of elements of xs that are even
def filterEven(xs: IntList): IntList =
xs match {
case Nil() => Nil()
case Cons(y, ys)
=> if(y % 2 == 0) Cons(y, filterEven(ys))
else filterEven(ys)
}
66. def insert(x: Int, xs: IntList): IntList =
xs match {
case Nil() => Cons(x, Nil())
case Cons(y, ys) => if(x < y) Cons(x, Cons(y, ys))
else Cons(y, insert(x, ys))
}
def isort(xs: IntList): IntList =
xs match {
case Nil() => Nil()
case Cons(y, ys) => insert(y, isort(ys))
}
sort elements in ascending order
67. def msort(xs: IntList): IntList = { Merge Sort
val n = lenght(xs) / 2
n match {
case 0 => xs
case _ => val (as, bs) = splitAt(xs, n)
merge(msort(as), msort(bs))
}
}
def splitAt(xs: IntList, n: Int): (IntList, IntList) =
xs match {
case Nil() => (Nil(), Nil())
case Cons(y, ys) => n match {
case 0 => (Nil(), xs)
case _ =>
val (as, bs) = splitAt(ys, n - 1)
(Cons(y, as), bs)
}
}
68. Merge Sort
define merge(xs: IntList, ys: IntList): IntList =
xs match {
case Nil() => ys
case Cons(a, as) =>
ys match {
case Nil() => xs
case Cons(b, bs) =>
if(a < b) Cons(a, merge(as, ys))
else Cons(b, merge(xs, bs))
}
}
69. abstract class IntList
case class Nil() extends IntList
case class Cons(hd: Int, tail: IntList) extends IntList
def f(xs: IntList): T =
xs match {
case Nil() => // base case
case Cons(x, ys) => ... f(ys) ...
// recursive case
}
Induction Principle for IntList
71. abstract class BinTree
case class Empty() extends BinTree
case class Node(left: BinTree,
value: Int,
right: BinTree) extends BinTree
Empty()
Node(Empty(), 42, Empty())
Node(Empty(), 5,
Node(Empty(), 42, Empty()))
def f(t: BinTree): A =
t match { Node(Node(Empty(), 2, Empty()),
5, Node(Empty(), 42, Empty()))
case Empty() => ...
case Node(t1, i, t2) =>
... f(t1) ... f(t2) ...
}
72. def replace(x: Int, y: Int, t: BinTree): BinTree =
t match {
case Empty() => ?
case Node(l, n, r) =>
?
}
replace occurrence of x by y
73. def replace(x: Int, y: Int, t: BinTree): BinTree =
t match {
case Empty() => Empty()
case Node(l, n, r) =>
Node(
replace(x, y, l),
if(n == x) y else n,
replace(x, y, r)
)
}
replace occurrence of x by y
74. def toList(t: BinTree): IntList = t match {
case Empty() => ?
case Node(l, n, r) =>
?
}
transform binary tree to list
75. def toList(t: BinTree): IntList = t match {
case Empty() => List()
case Node(l, n, r) =>
append(toList(l), Cons(n, toList(r)))
}
transform binary tree to list
76. Binary Search Trees
Invariant:
Node(t1, n, t2)
- all numbers in t1 are smaller than n
- all numbers in t2 are larger than n
Functions:
def insert(x: Int, t: BinTree): BinTree
def lookup(x: Int, t: BinTree): Boolean
Correctness
lookup(x, insert(x, t)) == true
77. Lookup in Binary
Search Tree
def lookup(x: Int, t: BinTree): Boolean = {
t match {
case Empty() => ?
case Node(left, value, right) =>
?
}
}
78. Lookup in Binary
Search Tree
def lookup(x: Int, t: BinTree): Boolean = {
t match {
case Empty() => false
case Node(left, value, right) =>
if(x < value)
lookup(x, left)
else if (x > value)
lookup(x, right)
else true
}
}
79. Insert in Binary
Search Tree
def insert(x: Int, t: BinTree): BinTree = {
t match {
case Empty() => ?
case Node(left, value, right) =>
?
}
}
80. Insert in Binary
Search Tree
def insert(x: Int, t: BinTree): BinTree = {
t match {
case Empty() => Node(Empty(), x, Empty())
case Node(left, value, right) =>
if(x < value)
Node(insert(x, left), value, right)
else if(x > value)
Node(left, value, insert(x, right))
else t
}
}
81. Insert in Binary
Search Tree
def toBinTree(xs: IntList): BinTree = xs match {
case Nil() => Empty()
case Cons(y, ys) => insert(y, toBinTree(ys))
}
def listSort(xs: IntList): IntList =
toList(toBinTree(xs))
82. Algebraic Datatypes in C
Based on: Simple algebraic data types for C by Pieter
Hartel and Henk Muller. Version 8, 2nd September, 2010
83. Algebraic Data Types in Scala
abstract class Tree
case class Leaf(v: Int)
case class Branch(v: Int, left: Tree, right: Tree)
def sum(t: Tree): Int = t match {
case Leaf(v) => v
case Branch(v, left, right) => v + sum(left) + sum(right)
}
84. ADT K&R Style
typedef struct tree_struct {
int val;
struct tree_struct *left;
struct tree_struct *right;
} tree;
tree *mkBRANCH(int val, tree *left, tree *right) {
tree *result = calloc(1, sizeof(struct tree_struct));
if (result == NULL) {
printf("panicn");
}
result->val = val;
result->left = left;
result->right = right;
return result;
}
86. ADT K&R Style
No explicit cases for Leaf and Branch
• distinction by use of NULL values for branches
• Does not cater for more alternatives
Confusion about NULL
• uninitialized value
• end-of-list, leaf-of-tree
• => errors
Explicit allocation of tree nodes
87. Algebraic Data Type with Union
typedef enum {
LEAF = 0, BRANCH = 1
} tree_tag;
typedef struct tree_struct {
tree_tag tag;
char *filename;
union {
struct {
int _val;
} _LEAF;
struct {
int _val;
struct tree_struct *_left;
struct tree_struct *_right;
} _BRANCH;
} data;
} tree;
88. Algebraic Data Type with Union
tree *mkLEAF(int _val) {
tree *result = calloc(1, sizeof(struct tree_struct));
if (result == NULL) {
printf("panicn");
}
result->tag = LEAF;
result->data._LEAF._val = val;
return result;
}
tree *mkBRANCH(int val, tree *left, tree *right) {
tree *result = calloc(1, sizeof(struct tree_struct));
if (result == NULL) {
printf("panicn");
}
result->tag = BRANCH;
result->data._BRANCH._val = val;
result->data._BRANCH._left = left;
result->data._BRANCH._right = right;
return result;
}
89. Recursive Functions on ADT
int sum(tree *t) {
if (t == NULL) {
return 0;
} else if (t->tag == LEAF) {
return t->data._LEAF._val;
} else { // t->tag == BRANCH
return t->data._BRANCH._val
+ sum(t->data._BRANCH._left)
+ sum(t->data._BRANCH._right);
}
}
90. Reading & Programming in Week 5
Reading
Sebesta Chapter 6:
Scala Chapter 6: Functional Objects
Scala Chapter 15: Case Classes and Pattern Matching
WebLab:
C, JavaScript, Scala tutorials
Graded Assignment 1: Dynamic Dispatch in C
(deadline 2 April 2013, 23:59)
Week 6: First-Class Functions