Here is a screen capture of the SML examples similar to class. Each input is meant to illustrate something. The remaining errors are instructive. - 5-3; val it = 2 : int - 3-5; val it = ~2 : int - -3; stdIn:26.1 Error: expression or pattern begins with infix identifier "-" stdIn:26.1-26.3 Error: operator and operand don't agree [literal] operator domain: 'Z * 'Z operand: int in expression: - 3 - 12/5; stdIn:27.3 Error: overloaded variable not defined at type symbol: / type: int - 12. / 5.; stdIn:27.2-27.5 Error: syntax error: deleting DOT ID - 12.0 / 5.0; val it = 2.4 : real - 12 div 5; val it = 2 : int - 12 mod 2; val it = 0 : int - 12 mod 5; val it = 2 : int - "qwerty" ^ "uiop"; val it = "qwertyuiop" : string - true andalso false; val it = false : bool - true orelse false; val it = true : bool - not(true); val it = false : bool - val x = 7; val x = 7 : int - (1+x)*x; val it = 56 : int - it+1; val it = 57 : int - fun f(x) = 2*x+1; val f = fn : int -> int - f(3); val it = 7 : int - f(f(1+f(1))); val it = 19 : int - fun square(z) = z*z; val square = fn : int -> int - square(9); val it = 81 : int - fun double (n) = 2*n; val double = fn : int -> int - double(7); val it = 14 : int - Math.sqrt(9.0); val it = 3.0 : real - fun g(x) = x+1.0; val g = fn : real -> real - fun dub(s) = s^s; val dub = fn : string -> string - dub(dub(dub("x"))); val it = "xxxxxxxx" : string - if true then 3 else 4; val it = 3 : int - if x>0 then x+1 else x+2; val it = 8 : int - fun max(x,y) = if x>y then x else y; val max = fn : int * int -> int - max(2,3); val it = 3 : int - max(3,2); val it = 3 : int - fun min (x,y) = x + y - max(x,y); val min = fn : int * int -> int - min(2,3); val it = 2 : int - fun min(x,y) = ~(max(~x, ~y)); val min = fn : int * int -> int - min(2,3); val it = 2 : int - fun f(i) = if i=0 then 10 else 10+f(i-1); val f = fn : int -> int - f(0); val it = 10 : int - f(1); val it = 20 : int - f(37); val it = 380 : int - f(100000); val it = 1000010 : int - f(1000000); GC #0.0.0.0.2.50: (10 ms) GC #0.0.0.1.3.52: (120 ms) val it = 10000010 : int - f(10000000); GC #0.0.1.3.5.56: (0 ms) GC #0.0.1.4.6.62: (50 ms) GC #0.0.1.5.7.64: (70 ms) GC #0.1.2.6.8.70: (620 ms) GC #1.2.3.7.9.72: (760 ms) GC #2.3.4.8.10.78: (1050 ms) GC #2.4.5.9.11.80: (140 ms) GC #2.5.6.10.12.86: (180 ms) GC #2.6.7.11.13.88: (160 ms) GC #3.7.8.12.14.94: (1730 ms) GC #3.8.9.13.15.96: (140 ms) GC #3.9.10.14.16.102: (170 ms) GC #3.10.11.15.17.104: (130 ms) GC #3.11.12.16.18.110: (180 ms) GC #3.12.13.17.19.112: (130 ms) val it = 100000010 : int - fun fact(n) = if n =0 then 1 else n*fact(n-1); val fact = fn : int -> int - fact(5); val it = 120 : int - fun sum(n) = if n=0 then 0 else n+ sum(n-1); val sum = fn : int -> int - sum(5); val it = 15 : int - sum(100); val it = 5050 : int - fun sumcube(n) = if n=0 then 0 else n*n*n + sumcube(n-1); val sumcube = fn : int -> int - sumcube(3); val it = 36 : int - fun pow(x,n) = if n=0 then 1 else x*pow(x,n-1); val pow = fn : int * int -> int - pow(10,2); val it = 100 : int - pow(2,10); val it = 1024 : int - sum(10); val it = 55 : int - val z = [0,1,2,3,4,5,6,7,8,9,10]; val z = [0,1,2,3,4,5,6,7,8,9,10] : int list - map square z; val it = [0,1,4,9,16,25,36,49,64,81,100] : int list - map sum z; val it = [0,1,3,6,10,15,21,28,36,45,55] : int list SML notes Recall how we define a function: - fun square(x) = x*x; val square = fn : int -> int ...and test it: - square(7); val it = 49 : int Recall how we define a list of values. And we name it "num": - val num = [0,1,2,3,4,5,6,7,8,9,10]; val num = [0,1,2,3,4,5,6,7,8,9,10] : int list Recall how to "map" a function to each value in the list: - map square num; val it = [0,1,4,9,16,25,36,49,64,81,100] : int list For fun, try squaring each of the squares: - map square (map square num); val it = [0,1,16,81,256,625,1296,2401,4096,6561,10000] : int list Here is an arithmetic sequence, defined NOT recursively: - fun f(x) = 7 + 2*x; val f = fn : int -> int Test it: - map f num; val it = [7,9,11,13,15,17,19,21,23,25,27] : int list Here is the same sequence, defined recursively: - fun g(n) = if n=0 then 7 else 2+g(n-1); val g = fn : int -> int It gives the same values, but calculated in a different manner: - map g num; val it = [7,9,11,13,15,17,19,21,23,25,27] : int list Lets compare f and g for speed. f runs very quickly, since it only requires one multiplication and one addition: - f(1000000); val it = 2000007 : int - f(10000000); val it = 20000007 : int But g takes longer, since it requires n additions and substitutions, here a million: - g(1000000); GC #0.0.0.0.2.22: (10 ms) GC #0.0.0.1.3.24: (130 ms) val it = 2000007 : int - GC #0.0.1.2.4.27: (10 ms) Here it is doing ten million additions and substitutions, but it eventually gets the correct answer: - g(10000000); GC #0.0.1.4.6.30: (30 ms) GC #0.0.1.5.7.36: (290 ms) GC #0.0.1.6.8.38: (70 ms) GC #0.1.2.7.9.44: (640 ms) GC #1.2.3.8.10.46: (780 ms) GC #2.3.4.9.11.52: (1020 ms) GC #2.4.5.10.12.54: (140 ms) GC #2.5.6.11.13.60: (170 ms) GC #2.6.7.12.14.62: (160 ms) GC #3.7.8.13.15.68: (1760 ms) GC #3.8.9.14.16.70: (160 ms) GC #3.9.10.15.17.76: (180 ms) GC #3.10.11.16.18.78: (150 ms) GC #3.11.12.17.19.84: (160 ms) GC #3.12.13.18.20.86: (140 ms) val it = 20000007 : int Here is a recursive three-to-the-nth-power function: - fun t(n) = if n=0 then 1 else 3*t(n-1); val t = fn : int -> int - t(0); val it = 1 : int - t(1); val it = 3 : int - t(2); val it = 9 : int - map t num; val it = [1,3,9,27,81,243,729,2187,6561,19683,59049] : int list Now a factorial function: - fun fact(n) = if n=0 then 1 else n*fact(n-1); val fact = fn : int -> int - map fact num; val it = [1,1,2,6,24,120,720,5040,40320,362880,3628800] : int list We find the sum 1+2+3+...+n in two different ways, first NOT recursive, then recursive. Method one is to use the formula we derived in class: - fun s(n) = n *(n+1) div 2; val s = fn : int -> int - s(10); val it = 55 : int - map s num; val it = [0,1,3,6,10,15,21,28,36,45,55] : int list Method two is to write a recursive function. The sum of the first n integers is just n more than the sum of the first n-1 integers. With a base case of 0 when n is 0, we get the following SML: - fun r(n) = if n=0 then 0 else n+r(n-1); val r = fn : int -> int - map r num; val it = [0,1,3,6,10,15,21,28,36,45,55] : int list The recursive method generalizes to other sums. Here is a way to find square(1)+square(2)+square(3)+...+square(n): - fun s2(n) = if n=0 then 0 else square(n)+s2(n-1); val s2 = fn : int -> int - map s2 num; val it = [0,1,5,14,30,55,91,140,204,285,385] : int list If SML didn't know about multiuplication, but still knew about addition, we could teach it multiplication as follows: - fun mul(a,b) = if a=0 then 0 else b+mul(a-1, b); val mul = fn : int * int -> int - mul(7,10); val it = 70 : int - mul(10,10); val it = 100 : int It works if b<0, but wouldn't work if a<0. (Recall "~" is the minus sign.) - mul(2,~5); val it = ~10 : int The speed of mul(a,b) depends on how big a is. 3*10000000 is fast, but 10000000*3 is slow, though it eventually gives the same answer: - mul(3,10000000); val it = 30000000 : int - mul(10000000,3); GC #4.13.15.21.23.112: (10 ms) GC #4.13.15.21.24.113: (10 ms) GC #4.13.16.22.25.118: (210 ms) GC #4.14.17.23.26.119: (270 ms) GC #4.15.18.24.27.124: (430 ms) GC #5.16.19.25.28.125: (520 ms) GC #6.17.20.26.29.130: (670 ms) GC #6.18.21.27.30.131: (140 ms) GC #6.19.22.28.31.136: (150 ms) GC #6.20.23.29.32.137: (140 ms) GC #7.21.24.30.33.142: (1150 ms) GC #7.22.25.31.34.143: (140 ms) GC #7.23.26.32.35.148: (160 ms) GC #7.24.27.33.36.149: (130 ms) GC #7.25.28.34.37.154: (170 ms) GC #7.26.29.35.38.155: (130 ms) GC #8.27.30.36.39.160: (1800 ms) GC #8.28.31.37.40.161: (110 ms) GC #8.29.32.38.41.166: (150 ms) GC #8.30.33.39.42.167: (120 ms) GC #8.31.34.40.43.172: (170 ms) GC #8.32.35.41.44.173: (140 ms) GC #8.33.36.42.45.178: (160 ms) GC #8.34.37.43.46.179: (120 ms) GC #9.35.38.44.47.184: (2680 ms) GC #9.36.39.45.48.185: (100 ms) val it = 30000000 : int If we wished, we could automatically swap a with b in cases where a is large, so it runs in whichever order is faster: - fun prod(a,b) = if a>b then mul(b,a) else mul(a,b); val prod = fn : int * int -> int - prod(2,10000000); val it = 20000000 : int - prod(10000000,2); val it = 20000000 : int Now a recursive function for x to the nth power: - fun pow(x,n) = if n=0 then 1 else x*pow(x,n-1); val pow = fn : int * int -> int - pow(7,2); val it = 49 : int - pow(5,3); val it = 125 : int - pow(2,10); val it = 1024 : int We can speed up exponentiation with a clever trick when n is even. Recall how to test is n is even: - 5 mod 2; val it = 1 : int - 6 mod 2; val it = 0 : int When n is even, we find x to the n/2 power and square that. There are three clauses to the definition here, so we use an IF inside another IF: - fun pow2(x,n) = if n=0 then 1 else = if (n mod 2) = 0 then square(pow2(x, n div 2)) = else x*pow2(x, n-1); val pow2 = fn : int * int -> int We get the same answers as above, but fewer multiplications are being used: - pow2(7,2); val it = 49 : int - pow2(5,3); val it = 125 : int - pow2(2,10); val it = 1024 : int Now an integer version of the log base 2: - fun log(n) = if n=1 then 0 else 1+log(n div 2); val log = fn : int -> int - log(16); val it = 4 : int - log(1024); val it = 10 : int If the exact log is not an integer, we get the floor of the exact log: - log(10); val it = 3 : int Here is one way to do the fibonacci function, with two base cases: - fun f(n) = if n<2 then = if n=0 then 0 else 1 = else f(n-1)+f(n-2); val f = fn : int -> int - map f [0,1,2,3,4,5,6,7,8]; val it = [0,1,1,2,3,5,8,13,21] : int list Here is another way, where the base cases are collected into one case: - fun fibb(n) = if n<2 then n else f(n-1)+f(n-2); val fibb = fn : int -> int - map fibb [0,1,2,3,4,5,6,7,8]; val it = [0,1,1,2,3,5,8,13,21] : int list Next, we explore "lists". A list is a finite sequence of elements, or a "container" for any number of elements. You can give a list explicitly, using square barckets, with commas as separators: Here is a list of integers: - [1,2,3,4,5,6,7,8]; val it = [1,2,3,4,5,6,7,8] : int list Lets name the above list "z" to use it later: - val z=it; val z = [1,2,3,4,5,6,7,8] : int list - z; val it = [1,2,3,4,5,6,7,8] : int list The joining operator, to combine ("catenate" or "concatenate") two lists into one larger list, is written "@": - [1,2,3] @ [4,5,6]; val it = [1,2,3,4,5,6] : int list Lets make a list with 16 elements. Only the first 12 print out: - z@z; val it = [1,2,3,4,5,6,7,8,1,2,3,4,...] : int list Execute this command (once) so in the future lists will print out with up to 1000 elements: - Compiler.Control.Print.printLength := 1000; val it = () : unit Now we see all 16 elements: - z@z; val it = [1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8] : int list All elements of a list must be the same type. List elements can be real numbers: - [1.2, 4.0, 0.01]; val it = [1.2,4.0,0.01] : real list Here is a list of lists: - [ [1,2], [], [1,2,3], z]; val it = [[1,2],[],[1,2,3],[1,2,3,4,5,6,7,8]] : int list list The "head" of a list is its first element. It is extracted with the built-in method "hd": - hd(z); val it = 1 : int The "tail" of a list is the list of everything after the head. It is extracted with the built-in method "tl": - tl(z); val it = [2,3,4,5,6,7,8] : int list - tl([1,2]); val it = [2] : int list - tl([2]); val it = [] : int list The empty list contains zero elements. - []; val it = [] : 'a list It is also called "nil": - nil; val it = [] : 'a list The empty list does not have a head or a tail. Asking for them will generate an error message: - hd([]); stdIn:48.1-48.7 Warning: type vars not generalized because of value restriction are instantiated to dummy types (X1,X2,...) uncaught exception Empty raised at: boot/list.sml:36.38-36.43 - tl([]); stdIn:49.1-49.7 Warning: type vars not generalized because of value restriction are instantiated to dummy types (X1,X2,...) uncaught exception Empty raised at: boot/list.sml:37.38-37.43 Write a recursive method to find the length of a list. The base case is that the empty list has length 0. The general case is that we add one to the length of the tail, which is calculated in a recursive call to the len function we are defining: - fun len(L) = if L=[] then 0 else 1+len(tl(L)); val len = fn : ''a list -> int recall z is an 8-element list: - z; val it = [1,2,3,4,5,6,7,8] : int list - len(z); val it = 8 : int The empty list has length zero: - len([]); val it = 0 : int - len([1,2]); val it = 2 : int Given a list of integers, we compute the sum of its elements with a recursive method. Add the head to the sum of elements in the tail. The base case is that we output 0 for an empty list, (the identity element for addition). - fun sum(L) = if L=[] then 0 else hd(L)+sum(tl(L)); val sum = fn : int list -> int - sum([1,2,3,10]); val it = 16 : int - sum([]); val it = 0 : int - sum(z); val it = 36 : int