CSE305: Data Types Lukasz Ziarek lziarek@buffalo.edu Announcements • No recitations next week • Instead we will hold office hours during recitation times in case you have any questions prior to the midterm • Request for note takers still open • Study guide to be released on Thursday • Sample questions on Friday and likely more over the weekend as well Compile-Time Descriptors Single-dimensioned array Multidimensional array 3 Array Representation for Predictability • Just like with large strings, we may need to break up arrays into chunks • Use a layered structure (arraylettes) spine content content content 4 content Associative Arrays • • • An associative array is an unordered collection of data elements that are indexed by an equal number of values called keys • User-defined keys must be stored Design issues: • What is the form of references to elements? • Is the size static or dynamic? Built-in type in Perl, Python, Ruby, and Lua • In Lua, they are supported by tables 5 Associative Arrays in Perl • Names begin with %; literals are delimited by parentheses • %hi_temps = ("Mon" => 77, "Tue" => 79, "Wed" => 65, …); • Subscripting is done using braces and keys • $hi_temps{"Wed"} = 83; • Elements can be removed with delete • delete $hi_temps{"Tue"}; 6 Record Types • A record is a possibly heterogeneous aggregate of data elements in which the individual elements are identified by names • Design issues: • What is the syntactic form of references to the field? • Are elliptical references allowed? 7 Records in ML • record expressions are of the form {x1=e1,...,xn=en} where the identifiers x are labels val x = {first = "John", last = "Doe", age = 65, balance = 0.12} • # and the label act as the selector (index) val y = #first x 8 Evaluation and Comparison to Arrays • Records are used when collection of data values is heterogeneous • Static subscripts, ordering does not matter • Dynamic subscripts could be used with record field access, but it would disallow type checking and it would be much slower 9 Implementation of Record Type Offset address relative to the beginning of the records is associated with each field 10 Tuple Types • A tuple is a data type that is similar to a record, except that the elements are not named • Used in Python, ML, and F# to allow functions to return multiple values • Python • Closely related to its lists, but immutable • Create with a tuple literal myTuple = (3, 5.8, ′apple′) Referenced with subscripts (begin at 1) • Catenation with + and deleted with del 11 Tuple Types (continued) ML val myTuple = (3, 5.8, ′apple′); - Access as follows: #1(myTuple) is the first element - A new tuple type can be defined type intReal = int * real; F# let tup = (3, 5, 7) let a, b, c = tup This assigns a tuple to a tuple pattern (a, b, c) 12 Why are tuples important? • Provide a mechanism for heterogeneous data storage • Usually immutable • Combined with pattern matching, provide a clean mechanism for defining functions 13 Functions and Tuples in SML • Functions take only ONE argument in SML • … but wait, what is this then? fun max(r1, r2)= if r1 < r2 then r2 else r1 14 Functions and Tuples in SML • Lets break the function down to its more basic form fun max(r1, r2)= if r1 < r2 then r2 else r1 fun max tuple = let val (r1, r2) = tuple in if r1 < r2 then r2 else r1 end 15 Functions and Tuples in SML • This means I can use max in two ways val max = fn : int * int -> int - val b = (1,2); val b = (1,2) : int * int - max b; val it = 2 : int - max (1,2); val it = 2 : int 16 A few common errors explained - fun f(x) = print(Int.toString(x)); val f = fn : int -> unit - fun g(x) = x+1; val g = fn : int -> int - f g(1); Error: operator and operand don't agree operator domain: int operand: int -> int in expression: f g A few common errors explained • What is the meaning of “f g(1);” • This is actually an expression of the form: <expr> <expr> <expr> f g (1) • Order of evaluation is left to right and <expr> <expr> if function application • Thus we evaluate f g A few common errors explained • What happens if we evaluate f g? operator domain: int operand: int -> int in expression: fg fun f(x) = print(Int.toString(x)); fun g(x) = x+1; f expects an int g is a function A few common errors explained • What is the meaning of “f (g(1));” • This is actually an expression of the form: <expr> (<expr> <expr>) f (g (1)) • Order of evaluation is left to right and <expr> <expr> if function application • Thus we evaluate <expr> applied to (<expr> <expr>) • To do this we must first simplify to (<expr> <expr>) Putting it all together • Having tuples as primitives allows for a simple grammar definition for function application • Functions take one argument return one result • Tuples give the illusion of multi argument functions • Parenthesis specify order of evaluation on NOT syntactic forms f(g 1) f(g(1)) List Types • Lists in Lisp and Scheme are delimited by parentheses and use no commas (A B C D) and (A (B C) D) • Data and code have the same form • As data, (A B C) is a list • As code, (A B C) is the function A applied to the parameters B and C • The interpreter needs to distinguish, so if it is data, we quote it with an apostrophe ′(A B C) is data 22 List Types (continued) List Operations in Scheme • CAR returns the first element of its list parameter (CAR ′(A B C)) returns A • CDR returns the remainder of its list parameter after the first element has been removed (CDR ′(A B C)) returns (B C) - CONS puts its first parameter into its second parameter, a list, to make a new list (CONS ′A ′(B C)) returns (A B C) - LIST returns a new list of its parameters (LIST ′A ′B ′(C D)) returns (A B (C D)) 23 List Types (continued) List Operations in ML • Lists are written in brackets and the elements are separated by commas • List elements must be of the same type • The Scheme CONS function is a binary operator in ML, :: 3 :: [5, 7, 9] evaluates to [3, 5, 7, 9] • The Scheme CAR and CDR functions are named hd and tl, respectively 24 List Types (continued) F# Lists • Like those of ML, except elements are separated by semicolons and hd and tl are methods of the List class Python Lists • The list data type also serves as Python’s arrays • Unlike Scheme, Common Lisp, ML, and F#, Python’s lists are mutable • Elements can be of any type • Create a list with an assignment myList = [3, 5.8, "grape"] 25 List Types (continued) Python Lists (continued) • List elements are referenced with subscripting, with indices beginning at zero x = myList[1] Sets x to 2nd element of myList • List elements can be deleted with del del myList[1] • List Comprehensions – derived from set notation [x * x for x in range(6) if x % 3 == 0] range(12) creates [0, 1, 2, 3, 4, 5, 6] Constructed list: [0, 9, 36] 26 List Types (continued) • Haskell’s List Comprehensions • The original [n * n | n <- [1..10]] • F#’s List Comprehensions let myArray = [|for i in 1 .. 5 -> [i * i] |] • Both C# and Java supports lists through their generic heap-dynamic collection classes, List and ArrayList, respectively 27 Using a list as a stack exception stackError fun push(x, xs) = x::xs fun pop(x::xs) = xs | pop([]) = raise stackError val push = fn : 'a * 'a list -> 'a list val pop = fn : 'a list -> 'a list Fold Left and Fold Right • The fold functions provide an abstract mechanism to traverse a structure (lists and sometimes arrays) and generate an accumulator during the course of the traversal • Two forms, left and right, dictate the order of the traversal • Thus they also dictate the order of the accumulation 29 Fold Left and Fold Right • Fold takes conceptually three arguments • A list over which we will fold (traverse) • An initial value for an accumulator • A function which given a pair (list element and accumulator) produces a new accumulator 30 Fold Left and Fold Right • Assume we call foldl on a list [1,2,3,4,5] with an initial accumulator (z) and a function f 31 Fold Left and Fold Right • Assume we call foldr on a list [1,2,3,4,5] with an initial accumulator (z) and a function f 32 Implementation of foldl Type of foldl :('a*'b->'b)->'b->'a list->'b fun foldl (f:'a*'b->'b)(acc: 'b)(l:'a list):'b = case l of [] => acc | x::xs => foldl f (f(x,acc)) xs Call fold recursively Apply f on x and the accumulator 33 Implementation of foldr Type of foldr :('a*'b->'b)->'b->'a list->'b fun foldr (f:'a*'b->'b) (acc:'b) (l:'a list):'b = case l of [] => acc | x::xs => f(x, (foldr f acc xs)) Apply f on x and the accumulator O get from recursive calls Recursive calls that build up the accumulator 34 What can I do with foldl/r? fun fun fun fun length l = foldl (fn (_,a) => a+1) 0 l rev l = foldl List.:: [] l map f l = foldr (fn (x,a) => (f x)::a) [] l filter f l = foldr (fn(x,a) => if f x then x::a else a) [] l