dm1299

[swarthmore cs75] Lab 0 Warmup & Basic OCaml

课程回顾

Swarthmore学院16年开的编译系统课,总共10次大作业。本随笔记录了相关的课堂笔记以及第1次大作业。

什么是编译

编译就是执行Program->Program'转换的过程,如下图所示:

这个过程需要满足两个条件:

  1. The input and output program mean the same thing.
  2. The output is executable in a context we care about.

编译执行过程:

不可变数据结构(persistent data structure)

例1: 下图是两个列表在内存中的表示:

xs = [0, 1, 2]
ys = [3, 4, 5]
执行了连接操作,zs在内存中的表示: ```haskell zs = xs ++ ys ``` 注意到,xs被复制了一份,但是ys被共享了。最终xs和ys都没有被改变。其中xs被复制是因为,xs中最后一个元素2不能够指向ys的起始地址,因为这样的话xs就被改变了。

例2: 假设下面的数据代表下图的二叉搜索树

xs = [a, b, c, d, f, g, h]
在执行插入操作后,内存中的数据结构变为下图: ```haskell ys = insert ("e", xs) ``` 可以看到两个指针xs和ys,其中ys共享了xs中的节点。

编程作业

*OSX安装Ocaml

brew install ocaml opam
opam init
opam install extlib ounit

1.Implement fibonacci as an OCaml function that takes an integer n and returns the nth fibonacci number. Write out the evaluation of (fibonacci 3) in substitution style.

  • 递归计算fibonacci数列
let rec fibonacci (n : int) : int = 
  if n <= 2 then 1
  else (fibonacci (n - 1)) + (fibonacci (n - 2));;
  • The evaluation of (fibonacci 3) in substitution style is :

=> (if 3 <= 2 then 1 else (fibonacci (3 - 1)) + (fibonacci (3 - 2)))
=> (if false then 1 else (fibonacci (3 - 1)) + (fibonacci (3 - 2)))
=> ((fibonacci (3 - 1)) + (fibonacci (3 - 2)))
=> ((fibonacci 2) + (fibonacci (3 - 2)))
=> ...
=> (1 + (fibonacci (3 - 2)))
=> (1 + (fibonacci 1))
=> ...
=> (1 + 1)

=> 2

2.Write tests for max and fibonacci using t_int.

  • 测试用例
(* a helper for testing integers *)
let t_int name value expected = name>::
  (fun _ -> assert_equal expected value ~printer:string_of_int);;

let max_test = t_int "" (max 4 5) 5;;
let fibonacci_test = t_int "fibonacci_test" (fibonacci 10) 55;;

let suite = "suite">:::[max_test; fibonacci_test;];;
run_test_tt_main suite
  • >:: is a function that creates a TestLabel for a TestCase

val (>:😃 : string -> test_fun -> test

  • >::: is a function that creates a TestLabel for a TestList

val (>::😃 : string -> test list -> test

  • 这里使用printer的作用是,可以打印出expected和value的值。而不是仅仅打印not equal这样的信息。

3.Write a test function t_string that’s like t_int, but tests for equality of strings. Can you write a function that produces a string form of the results like t_int did for integers?

(* a helper for testing strings *)
let t_string name value expected = name>::
  (fun _ -> assert_equal expected value ~printer:(fun x -> x));;
  • 首先看一下参数printer的声明,如果数据是string类型,就不用像t_int那样做int到string的数据转换;但是这里需要传入一个函数,所以可以用f(x)=x来填充。

?printer:('a -> string) ->
printer : value printer, don't print value otherwise

* 因为4~8题会用到二叉树,先定义了一个btnode类型。

type btnode =
  | Leaf
  | Node of string * btnode * btnode;;

4.Write at least five interesting tests for inorder_str.

  • 中序遍历二叉树
let rec inorder_str (bt : btnode) : string = 
  match bt with
        | Leaf -> ""
        | Node(s, left, right) ->
          (inorder_str left) ^ s ^ (inorder_str right);;
  • 测试用例
""              Leaf

inorder_str: ""
"a"             Node("a", Leaf, Leaf)

inorder_str: "a"
    "b"         Node("b", 
   /   \           Node("a", Leaf, Leaf), Node("c", Leaf, Leaf))
"a"     "c"

inorder_str: "abc"
      "a"       Node("a",  
     /             Node("b", 
  "b"               Node("c", Leaf, Leaf), Leaf), Leaf)
  /
"c" 
inorder_str:"cba"

    "1"          Node("1", 
       \           Leaf, Node("3", 
       "3"          Node("2", Leaf, Leaf), Leaf))
       / 
     "2"
inorder_str: "123"

let inorder_str_test = t_string "inorder_str_test1" (inorder_str  填入相应的btnode) 填入中序遍历结果;;
let suite = "suite">:::[inorder_str_test;];;
run_test_tt_main suite

5.Write out the substitution-based evaluation of inorder_str on a tree with at least 3 nodes.

  • The evaluation of (inorder_str (Node("1", Leaf, Node("3", Node("2", Leaf, Leaf), Leaf)))) in substitution style is :

=> match (Node("1", Leaf, Node("3", Node("2", Leaf, Leaf), Leaf))) with
| Leaf -> ""
| Node(s, left, right) -> (inorder_str left) ^ s ^ (inorder_str right);;
=> (inorder_str Leaf) ^ s ^ (inorder_str right)
=> "" ^ s ^ (inorder_str right)
=> "" ^ "1" ^ (inorder_str right)
=> "1" ^ (inorder_str (Node("3", Node("2", Leaf, Leaf), Leaf)))
=> ...
=> "1" ^ (inorder_str (Node("2", Leaf, Leaf))) ^ "3" ^ (inorder_str Leaf)
=> ...
=> "1" ^ "" ^ "2" ^ "" ^ "3" ^ ""
=> "123"

6.Write a function size that takes a btnode and produces an integer that is the number of Nodes in the tree.

  • 递归计算二叉树节点数
let rec size (bt : btnode) : int =
  match bt with 
    | Leaf -> 0
    | Node(s, left, right) -> 1 + (size left) + (size right);;

7.Write a function height that takes a btnode and produces an integer that is the height of the tree.

  • 递归计算二叉树高度
let rec height (bt : btnode) : int =
  match bt with
    | Leaf -> 0
    | Node(s, left, right) ->
      1 + (max (height left) (height right));;

8.Make sure to test the above two functions.

  • 测试用例
let size_test = t_int "size_test" (size (Node("1", Leaf, Node("3", Node("2", Leaf, Leaf), Leaf)))) 3;;
let height_test = t_int "height_test" (height (Node("1", Leaf, Node("3", Node("2", Node("a", Leaf, Leaf), Leaf), Leaf)))) 4;;
 "1"          
    \          
    "3"         
     / 
   "2"
   /
 "a"
let suite = "suite">:::[size_test; height_test;];;
run_test_tt_main suite

*因为9~12题需要分别测试int list,string list以及int list of int list,并且assert_equal中的~printer参数需要传入一个类型为'a->string的函数,也就是说需要把int,string以及int list分别转换为string;所以要分别实现这样的函数。

(* 将一个'a list转换为字符串, 转换方法是f, f因数据类型不同而不同 *)
let string_of_list (f : 'a -> string) (l : 'a list) : string = 
  "[" ^ String.concat ";" (List.map f l) ^ "]";;

(* 测试string list,需要传入的f是我定义的匿名函数 *)
let string_of_string_list = string_of_list (fun x -> "\"" ^ x ^ "\"");;
let t_string_list name value expected = 
  name>::(fun _ -> assert_equal expected value ~printer:string_of_string_list);;

(* 测试int list,需要传入的f是string_of_int *)
let string_of_int_list = string_of_list string_of_int;;
let t_int_list name value expected = 
  name>::(fun _ -> assert_equal expected value ~printer:string_of_int_list);;

(* 测试int list of int list,因为处理的是列表的列表,上一步已经定义了如何处理列表,所以f只需传入string_of_int_list *)
let string_of_int_list_list = string_of_list string_of_int_list;;
let t_int_list_list name value expected = 
  name>::(fun _ -> assert_equal expected value ~printer:string_of_int_list_list);;

9.Write and test a function increment_all that takes an int list and produces a new int list with all the elements increased by 1.

let rec increment_all (l : int list) : int list = List.map (fun x -> x + 1) l;;
  • 测试用例
let increment_all_test = t_int_list "increment_all_test" (increment_all [1; 2; 3]) [2; 3; 4];;
let suite = "suite">:::[increment_all_test;];;
run_test_tt_main suite

10.Write and test a function long_strings that takes a string list and an int and produces a new string list that contains all the strings that had length greater than the given int. You can get the length of a string with the function String.length. Other string functions are documented here.

  • 这里要把字符串长度大于len的舍弃,递归处理即可
let rec long_strings (l : string list) (len : int) : string list = 
  match l with
    | [] -> []
    | first::rest -> 
        if String.length(first) > len then first::(long_strings rest len) 
        else (long_strings rest len);;
  • 测试用例
let long_strings_test = t_string_list "long_strings_test" (long_strings [""; "a"; "ab"; "abc"; "abcd"] 2) ["abc"; "abcd"];; 
let suite = "suite">:::[long_strings_test;];;
run_test_tt_main suite

11.Write and test a function every_other that takes a ’a list (a list with elements of any one type), and produces a new list that contains every other element from the given list, starting with the first element.

  • 递归计算every other element from the given list。比如:[1; 2;] 返回 [1; ], [1;2;3;] 返回 [1;3;];这里注意下只有一个元素的情况,如果没考虑会导致match不exhaustive。
let rec every_other (l : 'a list) : 'a list =
  match l with
    | [] -> []
    | [first] -> [first]
    | first::second::rest -> first::(every_other rest);;
  • 测试用例
let every_other_test1 = t_int_list "every_other_test1" (every_other [1]) [1];;
let every_other_test2 = t_string_list "every_other_test2" (every_other ["a"; "b"; "c"; "d"]) ["a"; "c"];;
let every_other_test3 = t_int_list_list "every_other_test3" (every_other [[]; [0;1;2]]) [[]];;
let suite = "suite">:::[every_other_test1; every_other_test2; every_other_test3;];;
run_test_tt_main suite

12.Write and test a function sum_all that takes an int list list (that is, a list of lists of integers), and returns an int list that contains the sums of the sub-lists.

  • 计算子数组的和。
let sum (l : int list) : int = List.fold_left (+) 0 l;;
let sum_all (ll : int list list) : int list = List.map sum ll;;
  • 左折叠函数的定义

val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a
List.fold_left f a [b1; ...; bn] is f (... (f (f a b1) b2) ...) bn.

  • 测试用例
let sum_all_test = t_int_list "sum_all_test" (sum_all [[]; [0;1]; [0;1;2]; [0;1;2;3]]) [0; 1; 3; 6];;
let suite = "suite">:::[sum_all_test;];;
run_test_tt_main suite

参考资料

Lab 0 — Warmup & Basic OCaml

posted on 2019-02-03 02:23  dm1299  阅读(196)  评论(0编辑  收藏  举报

导航