CompSci 220. Programming Methodology 12: Functional Data Structures

Similar documents
Linked lists and the List class

List are immutable Lists have recursive structure Lists are homogeneous

Arne Brüsch Philipp Wille. Pattern Matching Syntax

CSCI-GA Final Exam

Overview. Elements of Programming Languages. Advanced constructs. Motivating inner class example

CS558 Programming Languages

List Functions, and Higher-Order Functions

CSE 130, Fall 2006: Final Examination

INF121: Functional Algorithmic and Programming

CSci 4223 Lecture 12 March 4, 2013 Topics: Lexical scope, dynamic scope, closures

Notes from a Short Introductory Lecture on Scala (Based on Programming in Scala, 2nd Ed.)

CSE341: Programming Languages Lecture 9 Function-Closure Idioms. Dan Grossman Winter 2013

Practically Functional. Daniel Spiewak

Collections and Combinatorial Search

// Body of shortestlists starts here y flatmap { case Nil => Nil case x :: xs => findshortest(list(x), xs)

Exercise 1: Graph coloring (10 points)

Copyright 2018 Tendril, Inc. All rights reserved.

Introduction to Functional Programming

INTRODUCTION TO HASKELL

Haskell An Introduction

CS6202: Advanced Topics in Programming Languages and Systems Lecture 0 : Overview

Don t Drive on the Railroad Tracks. Eugene Wallingford University of Northern Iowa November 17, 2010

Scala. Fernando Medeiros Tomás Paim

The fringe of a binary tree are the values in left-to-right order. For example, the fringe of the following tree:

The Haskell HOP: Higher-order Programming

Scala Style Guide Spring 2018

CS205: Scalable Software Systems

Programming Principles

n n Try tutorial on front page to get started! n spring13/ n Stack Overflow!

Comp 311 Functional Programming. Eric Allen, Two Sigma Investments Robert Corky Cartwright, Rice University Sagnak Tasirlar, Two Sigma Investments

CMSC 330: Organization of Programming Languages. Functional Programming with Lists

CS205: Scalable Software Systems

CS558 Programming Languages

INF 212 ANALYSIS OF PROG. LANGS FUNCTION COMPOSITION. Instructors: Crista Lopes Copyright Instructors.

Programming Paradigms

Bibliography. Analyse et Conception Formelle. Lesson 5. Crash Course on Scala. Scala in a nutshell. Outline

Strings are not collections, but they behave like them in almost every way. For example,

n n Official Scala website n Scala API n

Haskell Overview II (2A) Young Won Lim 8/9/16

Implementing Method Type Specialisation In Dotty

CS558 Programming Languages

Programming with Math and Logic

Summer 2017 Discussion 10: July 25, Introduction. 2 Primitives and Define

Classical Themes of Computer Science

CS 11 Haskell track: lecture 1

CPL 2016, week 10. Clojure functional core. Oleg Batrashev. April 11, Institute of Computer Science, Tartu, Estonia

CSE341: Programming Languages Lecture 11 Type Inference. Dan Grossman Spring 2016

Programming Paradigms

SCHEME 10 COMPUTER SCIENCE 61A. July 26, Warm Up: Conditional Expressions. 1. What does Scheme print? scm> (if (or #t (/ 1 0)) 1 (/ 1 0))

A general introduction to Functional Programming using Haskell

Haskell Overview II (2A) Young Won Lim 8/23/16

JVM ByteCode Interpreter

Title: Recursion and Higher Order Functions

CSci 4223 Principles of Programming Languages

CSE341: Programming Languages Lecture 9 Function-Closure Idioms. Dan Grossman Fall 2011

Functional programming

Lecture 8: Summary of Haskell course + Type Level Programming

# true;; - : bool = true. # false;; - : bool = false 9/10/ // = {s (5, "hi", 3.2), c 4, a 1, b 5} 9/10/2017 4

A Second Look At ML. Chapter Seven Modern Programming Languages, 2nd ed. 1

Introduction to Type Driven Development in Scala

Outline. Collections Arrays vs. Lists Functions using Arrays/Lists Higher order functions using Arrays/Lists

The SCAlable LAnguage

Introduction to Haskell

Chapter 1. Fundamentals of Higher Order Programming

Functional Programming

High Wizardry in the Land of Scala. Daniel Spiewak

CompSci 220. Programming Methodology 16: Understanding FP Error Handling Part 2

CMSC 330: Organization of Programming Languages. Functional Programming with Lists

Life in a Post-Functional World

CSE Lecture 7: Polymorphism and generics 16 September Nate Nystrom UTA

Functional Languages. Hwansoo Han

G Programming Languages - Fall 2012

Using Scala in CS241

Functional Programming. Pure Functional Programming

Introduction to Functional Programming in Racket. CS 550 Programming Languages Jeremy Johnson

PROGRAMMING IN HASKELL. Chapter 5 - List Comprehensions

Booleans (aka Truth Values) Programming Languages and Compilers (CS 421) Booleans and Short-Circuit Evaluation. Tuples as Values.

CSCI-GA Scripting Languages

CMSC 330, Fall 2013, Practice Problems 3

CMSC 330, Fall 2013, Practice Problem 3 Solutions

The type checker will complain that the two branches have different types, one is string and the other is int

Parallel Operations on Collections. Parallel Programming in Scala Viktor Kuncak

Programming Paradigms, Fall 06

A Small Interpreted Language

Haskell Overview II (2A) Young Won Lim 9/26/16

Software System Design and Implementation

INF 102 CONCEPTS OF PROG. LANGS FUNCTIONAL COMPOSITION. Instructors: James Jones Copyright Instructors.

Beyond Blocks: Python Session #1

Advanced Type System Features Tom Schrijvers. Leuven Haskell User Group

Advanced Object-Oriented Languages Scala

Logic - CM0845 Introduction to Haskell

Functional Programming

Scheme. Functional Programming. Lambda Calculus. CSC 4101: Programming Languages 1. Textbook, Sections , 13.7

Repetition Through Recursion

Exercise 8 Parametric polymorphism November 18, 2016

Functional Programming and Haskell

A First Look at ML. Chapter Five Modern Programming Languages, 2nd ed. 1

CSE341: Programming Languages Lecture 7 First-Class Functions. Dan Grossman Winter 2013

Lab 8: Introduction to Scala 12:00 PM, Mar 14, 2018

Lecture 4: Higher Order Functions

Transcription:

CompSci 220 Programming Methodology 12: Functional Data Structures

A Polymorphic Higher- Order Function def findfirst[a](as: Array[A], p: A => Boolean): Int = { def loop(n: Int): Int = if (n >= as.length) -1 else if (p(as(n))) n else loop(n + 1) } loop(0)

A Polymorphic Higher- Order Function def findfirst[a](as: Array[A], p: A => Boolean): Int = { } def loop(n: Int): Int = if (n >= as.length) -1 else if (p(as(n))) n else loop(n + 1) loop(0) The findfirst function is polymorphic in its type parameter A.

A Polymorphic Higher- Order Function def findfirst[a](as: Array[A], p: A => Boolean): Int = { } def loop(n: Int): Int = if (n >= as.length) -1 else if (p(as(n))) n else loop(n + 1) loop(0) The type parameter is used to restrict the type of the elements contained in the Array to A.

A Polymorphic Higher- Order Function def findfirst[a](as: Array[A], p: A => Boolean): Int = { } def loop(n: Int): Int = if (n >= as.length) -1 else if (p(as(n))) n else loop(n + 1) loop(0) And to restrict the type of the argument of the predicate function p to A. We use the function p to abstract the task of determining what we are looking for.

A Polymorphic Higher- Order Function def findfirst[a](as: Array[A], p: A => Boolean): Int = { } def loop(n: Int): Int = if (n >= as.length) -1 else if (p(as(n))) n else loop(n + 1) loop(0) loop performs the iteration over the array of elements of type A. This is a tail- recursive function the Scala compiler will translate this into an efficient loop!

A Polymorphic Higher- Order Function def findfirst[a](as: Array[A], p: A => Boolean): Int = { } def loop(n: Int): Int = if (n >= as.length) -1 else if (p(as(n))) n else loop(n + 1) loop(0) We invoke p on each element in the array to determine if we found the item. Because the types match up this expression is type safe.

Implement a Higher- Order Function def issorted[a](as: Array[A], ord: (A,A) => Boolean): Boolean = { // Implement this on a piece of paper // Hand in this paper at the end of class }

Implement a Higher- Order Function def issorted[a](as: Array[A], ord: (A,A) => Boolean): Boolean = { } def loop(a: Array[A], res: Boolean): Boolean = if (a.isempty) res && true else if (a.length == 1) res && true else loop(a.tail, ord(a(0),a(1))) loop(as, true) Here, we create a tail- recursive loop function that traverses the array. We perform the proper case analysis. We use the ord function to define the ordering we are interested in.

Anonymous Functions def issorted[a](as: Array[A], ord: (A,A) => Boolean): Boolean = { def loop(a: Array[A], res: Boolean): Boolean = if (a.isempty) res && true else if (a.length == 1) res && true else loop(a.tail, ord(a(0),a(1))) } loop(as, true) We use an anonymous function to invoke the issorted function. This is also called a lambda. issorted(array(1,2,3,4), (x: Int,y: Int) => x <= y) See Sorted01.scala

Short Definition, Many Possibilities The issorted function is a short function. It is parameterized by type A to allow for any type of Array[A]. It is parameterized by a function ord to allow for any ordering definition. This allows for many possible uses of the function issorted.

Short Definition, One Possibility Type and function parameterization often allows for many possible uses. It can also be used to constrain the implementation to only one possibility. Consider the higher- order function for partial application. It takes a value and a function of two arguments, and returns a function of one argument as its result: def partial1[a,b,c](a: A, f: (A,B) => C): B => C =???

Short Definition, One Possibility How would you go about implementing this higher- order function? It turns out that there is only one implementation that works and it follows logically from the type signature of the partial1 function. def partial1[a,b,c](a: A, f: (A,B) => C): B => C =???

Short Definition, One Possibility How would you go about implementing this higher- order function? It turns out that there is only one implementation that works and it follows logically from the type signature of the partial1 function. We know that we need to return a function. def partial1[a,b,c](a: A, f: (A,B) => C): B => C =???

Short Definition, One Possibility How would you go about implementing this higher- order function? It turns out that there is only one implementation that works and it follows logically from the type signature of the partial1 function. We know that we need to return a function. We also know that that function takes a parameter of type B. def partial1[a,b,c](a: A, f: (A,B) => C): B => C =???

Short Definition, One Possibility How would you go about implementing this higher- order function? It turns out that there is only one implementation that works and it follows logically from the type signature of the partial1 function. We know that we need to return a function. We also know that that function takes a parameter of type B. So, we can easily start off the implementation. def partial1[a,b,c](a: A, f: (A,B) => C): B => C = (b: B) =>???

Short Definition, One Possibility How would you go about implementing this higher- order function? It turns out that there is only one implementation that works and it follows logically from the type signature of the partial1 function. We know that we need to return a function. We also know that that function takes a parameter of type B. So, we can easily start off the implementation. What is the implementation? def partial1[a,b,c](a: A, f: (A,B) => C): B => C = (b: B) =>???

Short Definition, One Possibility How would you go about implementing this higher- order function? It turns out that there is only one implementation that works and it follows logically from the type signature of the partial1 function. We know that we need to return a function. We also know that that function takes a parameter of type B. So, we can easily start off the implementation. What is the implementation? def partial1[a,b,c](a: A, f: (A,B) => C): B => C = (b: B) => f(a, b) See Partial02.scala

How about this function? The curry function converts a function f of two arguments into a function of one argument that partially applies f. Again, there is only one implementation of this. def curry[a,b,c](f: (A,B) => C): A => (B => C) =???

How about this function? The curry function converts a function f of two arguments into a function of one argument that partially applies f. Again, there is only one implementation of this. def curry[a,b,c](f: (A,B) => C): A => (B => C) = (a: A) => (b: B) => f(a,b) See Curry03.scala

The real world? These functions are interesting, but how do they relate to real- world programming in the large?

The real world? These functions are interesting, but how do they relate to real- world programming in the large? It turns out by using functions such as partial and curry, as well as many others (e.g., compose, map, fold, reduce, zip, ) you are able to construct sophisticated programs using functional composition.

The real world? These functions are interesting, but how do they relate to real- world programming in the large? It turns out by using functions such as partial and curry, as well as many others (e.g., compose, map, fold, reduce, zip, ) you are able to construct sophisticated programs using functional composition. Polymorphic, higher- order functions often end up being extremely widely applicable, precisely because they say nothing about any particular domain and are simply abstracting over common patterns that occur in many contexts.

What about data structures? Using pure functions lends itself naturally to immutable data structures. Pure functions do not change state and immutable data structures can t be modified. How might we define a List data structure? See List04.scala

List Abstract Data Type How do we represent a List such that it is immutable?

List Abstract Data Type How do we represent a List such that it is immutable? We need something that represents the empty list.

List Abstract Data Type How do we represent a List such that it is immutable? We need something that represents the empty list. We need something that represents a non- empty list

List Abstract Data Type How do we represent a List such that it is immutable? We need something that represents the empty list. We need something that represents a non- empty list sealed trait List[+A]

List Abstract Data Type How do we represent a List such that it is immutable? We need something that represents the empty list. We need something that represents a non- empty list sealed trait List[+A] A trait that is sealed simply means that anything that extends it must be defined in the same file.

List Abstract Data Type How do we represent a List such that it is immutable? We need something that represents the empty list. We need something that represents a non- empty list sealed trait List[+A] The + in front of the type parameter A is a variance annotation. In particular, the +A indicates that the type parameter A is covariant. That is, if X is a subtype of A then List[X] is a subtype of List[A].

List Abstract Data Type How do we represent a List such that it is immutable? We need something that represents the empty list. We need something that represents a non- empty list sealed trait List[+A] case object Nil extends List[Nothing] Nil represents the empty list. Nil is an object there is only one of these. Why?

List Abstract Data Type How do we represent a List such that it is immutable? We need something that represents the empty list. We need something that represents a non- empty list sealed trait List[+A] case object Nil extends List[Nothing] Nothing is a subtype of everything. This means that Nil can be used in the context of any type of list: List[Int], List[String], etc.

List Abstract Data Type How do we represent a List such that it is immutable? We need something that represents the empty list. We need something that represents a non- empty list sealed trait List[+A] case object Nil extends List[Nothing] case class Cons[+A](head: A, tail: List[A]) extends List[A] Cons is a case class. We will represent a List as a head element (the data) and the rest of the list (the tail).

Constructing Lists So, lists are represented by a cons cell. How do we create lists then? val xs = Cons(1, Cons(2, Cons(3, Nil)))

Constructing Lists So, lists are represented by a cons cell. How do we create lists then? val xs = Cons(1, Cons(2, Cons(3, Nil))) Isn t this rather ugly? I would think so How might we have our implementation support this: List(1,2,3)?

Constructing Lists So, lists are represented by a cons cell. How do we create lists then? a) Create a function called List b) Create a companion object with an apply method? c) Create an apply method in the List class? d) Use Scala s List implementation to do this. e) None of these. val xs = Cons(1, Cons(2, Cons(3, Nil))) Isn t this rather ugly? I would think so How might we have our implementation support this: List(1,2,3)?

The List Companion Object Companion objects are perfect for this sort of thing. Can anyone suggest on how to implement this? Go ahead, do not be shy J

The List Companion Object Companion objects are perfect for this sort of thing. object List { def apply[a](as: A*): List[A] = if (as.isempty) Nil else Cons(as.head, apply(as.tail: _*)) }

Let us create a sum method in List How would you write a function List.sum that takes a List[Int] and returns the sum of each Int in the list? sealed trait List[+A] case object Nil extends List[Nothing] case class Cons[+A](head: A, tail: List[A]) extends List[A] object List { def sum(xs: List[Int]): Int =??? }

Let us create a sum method in List How would you write a function List.sum that takes a List[Int] and returns the sum of each Int in the list? sealed trait List[+A] case object Nil extends List[Nothing] case class Cons[+A](head: A, tail: List[A]) extends List[A] object List { def sum(xs: List[Int]): Int = if (xs == Nil) 0 else xs.head + sum(xs.tail) } Here is one way of doing it. But, it turns out there is a more elegant way of expressing this in Scala

Let us create a sum method in List How would you write a function List.sum that takes a List[Int] and returns the sum of each Int in the list? sealed trait List[+A] case object Nil extends List[Nothing] case class Cons[+A](head: A, tail: List[A]) extends List[A] object List { def sum(xs: List[Int]): Int = xs match { case Nil => 0 case Cons(x,xs) => x + sum(xs) } } Pattern matching is the preferred approach to match over data structures.

Let us create a sum method in List How would you implement product? sealed trait List[+A] case object Nil extends List[Nothing] case class Cons[+A](head: A, tail: List[A]) extends List[A] object List { def product(xs: List[Double]): Double =??? }

Let us create a sum method in List How would you implement product? sealed trait List[+A] case object Nil extends List[Nothing] case class Cons[+A](head: A, tail: List[A]) extends List[A] object List { def product(xs: List[Double]): Double = xs match { } } case Nil => 1.0 case Cons(0.0,_) => 0.0 case Cons(x,xs) => x * product(xs) The _ pattern matches anything. In this case if we see a 0.0, then the rest of the list does not matter.

Data Sharing When data is immutable, how do we write functions that add or remove elements from a list? object List { def add[a](xs: List[A], e: A): List[A] = Cons(e, xs) } We need not copy xs, we simply reuse it. Sharing of immutable data simplifies code (copying unnecessary) Functional data structures are persistent meaning that existing references are never changed by operations on the data structure.

Data Structures are Persistent a b c d Nil List( a, b, c, d ).tail à List( b, c, d )

Data Structures are Persistent a b c d Nil List( a, b, c, d ).tail à List( b, c, d ) How would you implement the function List.tail? See List04.scala

Generalizing to Higher- Order Functions Remember the List.sumand List.productmethods? Is there a common pattern that we could abstract from both?

Generalizing to Higher- Order Functions Remember the List.sumand List.productmethods? Is there a common pattern that we could abstract from both? Right, they both traverse a list to produce a single value. What if we could write a function that traverses a list to produce a single value and use that to implement sum and product? This is the hallmark of function programming.

foldright The foldright function does exactly that it traverses a list and produces a final value by combining elements in the list. object List { def foldright[a,b](xs: List[A], z: B)(f: (A,B) => B): B = xs match { case Nil => z case Cons(x, xs) => f(x, foldright(xs, z)(f)) } } Now, implement sum and product in terms of that

foldright The foldright function does exactly that it traverses a list and produces a final value by combining elements in the list. object List { def foldright[a,b](xs: List[A], z: B)(f: (A,B) => B): B = xs match { case Nil => z case Cons(x, xs) => f(x, foldright(xs, z)(f)) } } def sum(xs: List[Int]) = foldright(xs, 0)((x,y) => x + y)

foldright unfolded foldright(cons(1, Cons(2, Cons(3, Nil))), 0)((x,y) => x + y)

foldright unfolded foldright(cons(1, Cons(2, Cons(3, Nil))), 0)((x,y) => x + y) 1 + foldright(cons(2, Cons(3, Nil)), 0)((x,y) => x + y)

foldright unfolded foldright(cons(1, Cons(2, Cons(3, Nil))), 0)((x,y) => x + y) 1 + foldright(cons(2, Cons(3, Nil)), 0)((x,y) => x + y) 1 + (2 + foldright(cons(3, Nil), 0)((x,y) => x + y)

foldright unfolded foldright(cons(1, Cons(2, Cons(3, Nil))), 0)((x,y) => x + y) 1 + foldright(cons(2, Cons(3, Nil)), 0)((x,y) => x + y) 1 + (2 + foldright(cons(3, Nil), 0)((x,y) => x + y) 1 + (2 + (3 + foldright(nil, 0)((x,y) => x + y)

foldright unfolded foldright(cons(1, Cons(2, Cons(3, Nil))), 0)((x,y) => x + y) 1 + foldright(cons(2, Cons(3, Nil)), 0)((x,y) => x + y) 1 + (2 + foldright(cons(3, Nil), 0)((x,y) => x + y) 1 + (2 + (3 + foldright(nil, 0)((x,y) => x + y) 1 + (2 + (3 + (0))

foldright unfolded foldright(cons(1, Cons(2, Cons(3, Nil))), 0)((x,y) => x + y) 1 + foldright(cons(2, Cons(3, Nil)), 0)((x,y) => x + y) 1 + (2 + foldright(cons(3, Nil), 0)((x,y) => x + y) 1 + (2 + (3 + foldright(nil, 0)((x,y) => x + y) 1 + (2 + (3 + (0)) 6