笔记
开始搞得有点像编译原理了哦,很神秘。也不是很像。
46. Truth Tables for Logical Expressions (2 Variables)
Let us define a small “language” for boolean expressions containing variables:
# type bool_expr = | Var of string | Not of bool_expr | And of bool_expr * bool_expr | Or of bool_expr * bool_expr;; type bool_expr = Var of string | Not of bool_expr | And of bool_expr * bool_expr | Or of bool_expr * bool_expr
A logical expression in two variables can then be written in prefix notation. For example,
(a ∨ b) ∧ (a ∧ b)
is written:# And (Or (Var "a", Var "b"), And (Var "a", Var "b"));; - : bool_expr = And (Or (Var "a", Var "b"), And (Var "a", Var "b"))
Define a function,
table2
which returns the truth table of a given logical expression in two variables (specified as arguments). The return value must be a list of triples containing(value_of_a, value_of_b, value_of_expr)
.# table2 "a" "b" (And (Var "a", Or (Var "a", Var "b")));; - : (bool * bool * bool) list = [(true, true, true); (true, false, true); (false, true, false); (false, false, false)]
额啊,题好长。还好是固定参数的,好像 OCaml 函数只能固定参数数量。
表驱动,懂?太对劲了是个锤子的表驱动。
let table2 one two exp =
let rec eval exp (fir, sec)= match exp with
| Var e -> if e = one then fir else sec
| Not e -> not (eval e (fir, sec))
| And (e1, e2) -> (eval e1 (fir, sec)) && (eval e2 (fir, sec))
| Or (e1, e2) -> (eval e1 (fir, sec)) || (eval e2 (fir, sec))
in
List.map (fun (a,b) -> (a, b, (eval exp (a, b))) ) [(true, true); (true, false); (false, true); (false, false)];;
val table2 : string -> 'a -> bool_expr -> (bool * bool * bool) list = <fun>
我的函数签名还不太一样,第二个参数没有限制,这是怎么做到的,问题是第一个参数的类型限制又是什么时候加上的呢?有什么是依赖 string 的吗?OCaml 的类型推断,真的很神秘。
# let rec eval2 a val_a b val_b = function
| Var x -> if x = a then val_a
else if x = b then val_b
else failwith "The expression contains an invalid variable"
| Not e -> not (eval2 a val_a b val_b e)
| And(e1, e2) -> eval2 a val_a b val_b e1 && eval2 a val_a b val_b e2
| Or(e1, e2) -> eval2 a val_a b val_b e1 || eval2 a val_a b val_b e2
let table2 a b expr =
[(true, true, eval2 a true b true expr);
(true, false, eval2 a true b false expr);
(false, true, eval2 a false b true expr);
(false, false, eval2 a false b false expr)];;
val eval2 : string -> bool -> string -> bool -> bool_expr -> bool = <fun>
val table2 : string -> string -> bool_expr -> (bool * bool * bool) list =
<fun>
48. Truth Tables for Logical Expressions
Generalize the previous problem in such a way that the logical expression may contain any number of logical variables. Define
table
in a way thattable variables expr
returns the truth table for the expressionexpr
, which contains the logical variables enumerated invariables
.
这下任意数量参数了。得搞个 map 了。
写了我好久,这种过程较为复杂的情况,声明式的语言就优势一点。好吧,可能也没啥优势,就是我不熟 OCaml 的库函数,而且还在用闭包啥的导致情况有点复杂。
说起来,OCaml 的 Map 还是有序的,fold 文档里说 where k1 ... kN
are the keys of all bindings in m
(in increasing order) 这么看底层是什么情况呢,链表吗。
let table vars exp= let module VarTable = Map.Make(String) in
let rec get_input l before= match l with
| [] -> [before]
| h::t -> (get_input t ((h, true)::before)) @ (get_input t ((h, false)::before))
in
let rec into_map l m= match l with
| [] -> m
| (name, value)::t -> into_map t (VarTable.add name value m)
in
let var_maps = List.map (fun e -> into_map e VarTable.empty) (get_input vars []) in
let rec eval inner_exp var_map= match inner_exp with
| Var e -> VarTable.find e var_map
| Not e -> not (eval e var_map)
| And (e1, e2) -> (eval e1 var_map) && (eval e2 var_map)
| Or (e1, e2) -> (eval e1 var_map) || (eval e2 var_map)
in
List.map (fun m -> ((VarTable.fold (fun k v acc-> acc@ [(k,v)]) m []), (eval exp m))) var_maps;;
val table :
String.t list -> bool_expr -> ((String.t * bool) list * bool) list = <fun>
哦,List.assoc 这么牛的吗,当 map 用了,我还老老实实用 map 了,不过 List.assoc 估计是 O(n) 的。
# (* [val_vars] is an associative list containing the truth value of
each variable. For efficiency, a Map or a Hashtlb should be
preferred. *)
let rec eval val_vars = function
| Var x -> List.assoc x val_vars
| Not e -> not (eval val_vars e)
| And(e1, e2) -> eval val_vars e1 && eval val_vars e2
| Or(e1, e2) -> eval val_vars e1 || eval val_vars e2
(* Again, this is an easy and short implementation rather than an
efficient one. *)
let rec table_make val_vars vars expr =
match vars with
| [] -> [(List.rev val_vars, eval val_vars expr)]
| v :: tl ->
table_make ((v, true) :: val_vars) tl expr
@ table_make ((v, false) :: val_vars) tl expr
let table vars expr = table_make [] vars expr;;
val eval : (string * bool) list -> bool_expr -> bool = <fun>
val table_make :
(string * bool) list ->
string list -> bool_expr -> ((string * bool) list * bool) list = <fun>
val table : string list -> bool_expr -> ((string * bool) list * bool) list =
<fun>
49. Gray Code
An n-bit Gray code is a sequence of n-bit strings constructed according to certain rules. For example,
n = 1: C(1) = ['0', '1']. n = 2: C(2) = ['00', '01', '11', '10']. n = 3: C(3) = ['000', '001', '011', '010', '110', '111', '101', '100'].
Find out the construction rules and write a function with the following specification:
gray n
returns then
-bit Gray code.# gray 1;; - : string list = ["0"; "1"] # gray 2;; - : string list = ["00"; "01"; "11"; "10"] # gray 3;; - : string list = ["000"; "001"; "011"; "010"; "110"; "111"; "101"; "100"]
查了下
2.1.1 产生的基本规律原则和标准做法
其实就是3个步骤,
第一步,改变最右边的位元值;
第二步,改变右起第一个为1的位元的左边位元;
第三步,第四步重复第一步和第二步,直到所有的格雷码产生完毕(换句话说,已经走了(2^n) - 1 步)。
我能不能倒过来生成呢,倒着遍历不是很方便。也就是:
第一步,改变最左边的位元值;
第二步,改变左起第一个为1的位元的右边位元;
第三步,第四步重复第一步和第二步,直到所有的格雷码产生完毕(换句话说,已经走了(2^n) - 1 步)。
中途有哪一步把顺序搞反了,头疼。好吧,没有,我又憨憨了。
递归导致的顺序混乱是真的头晕。最后 fold 的时候又有点混乱。
let gray n =
let rec init_it remain = if remain>0 then "0"::init (remain-1) else [] in
let init = init_it n in
let pow a b = int_of_float ((float_of_int a) ** (float_of_int b)) in
let total = pow 2 n -1 in
let rec next step now acc =
if total = List.length acc then acc
else if step = 1 then
match now with
| [] ->raise Not_found
| h::t -> let next_step = ((if h="1" then "0" else "1") :: t) in
next 2 next_step (next_step::acc)
else if step = 2 then
let rec step_two l= match l with
| f::s::t -> if f = "1" then (f::(if s="1" then "0" else "1")::t)
else (f::(step_two (s::t)))
| _ -> raise Not_found
in
let next_step = step_two now in
next 1 next_step (next_step::acc)
else raise Not_found
in
List.map (fun e -> List.fold_right (fun a acc-> acc^a) e "") (init :: List.rev (next 1 init []));;
val gray : int -> string list = <fun>
流汗了,你怎么这么短。
看起来有个比较神奇的生成方法。没太看懂,呃呃。
# let gray n =
let rec gray_next_level k l =
if k < n then
(* This is the core part of the Gray code construction.
* first_half is reversed and has a "0" attached to every element.
* Second part is reversed (it must be reversed for correct gray code).
* Every element has "1" attached to the front.*)
let (first_half,second_half) =
List.fold_left (fun (acc1,acc2) x ->
(("0" ^ x) :: acc1, ("1" ^ x) :: acc2)) ([], []) l
in
(* List.rev_append turns first_half around and attaches it to second_half.
* The result is the modified first_half in correct order attached to
* the second_half modified in reversed order.*)
gray_next_level (k + 1) (List.rev_append first_half second_half)
else l
in
gray_next_level 1 ["0"; "1"];;
val gray : int -> string list = <fun>
50. Huffman Code
First of all, consult a good book on discrete mathematics or algorithms for a detailed description of Huffman codes (you can start with the Wikipedia page)!
We consider a set of symbols with their frequencies. For example, if the alphabet is
"a"
,…,"f"
(represented as the positions 0,…5) and respective frequencies are 45, 13, 12, 16, 9, 5:
瞄了一眼答案,流汗了,有点长。
至少,我需要一个树。癌,好麻烦。
OCaml 有空指针吗?是有 Option 的。
我甚至不会把值简单地从 type 里取出来,一定得 match 一下,我服了。
猜对了,左树 0 右树 1,还行啊。
type 'a tree_node =
| Nil
| Tree_node of 'a * 'a tree_node * 'a tree_node;;
type 'a tree_node = Nil | Tree_node of 'a * 'a tree_node * 'a tree_node
let huffman l =
let init_nodes = List.map (fun (name,fre) -> Tree_node ((name,fre), Nil, Nil) ) l in
let extract_value node = match node with
| Nil -> raise Not_found
| Tree_node ((name,fre),l,r) -> fre
in
let extract_all node = match node with
| Nil -> raise Not_found
| Tree_node ((name,fre),l,r) -> ((name,fre),l,r)
in
let rec stack nodes =
let sorted_nodes = List.sort (fun a b -> (extract_value a) - (extract_value b)) nodes in
match sorted_nodes with
| [] -> raise Not_found
| f::s::t -> stack ((Tree_node (("", (extract_value f) + (extract_value s)), f, s))::t)
| h::t -> h
in
let rec assign node bits =
let ((name,fre),l,r) = extract_all node in if name != "" then [(name, bits)] else (assign l (bits^"0")) @ (assign r (bits^"1"))
in
match (stack init_nodes) with
| root -> assign root ""
| _ -> raise Not_found;;
Line 23, characters 11-12:
Warning 11 [redundant-case]: this match case is unused.
val huffman : (string * int) list -> (string * string) list = <fun>
标准答案整了个优先队列,搞个堆而不是 list 应该就差不多了。
# (* Simple priority queue where the priorities are integers 0..100.
The node with the lowest probability comes first. *)
module Pq = struct
type 'a t = {data: 'a list array; mutable first: int}
let make() = {data = Array.make 101 []; first = 101}
let add q p x =
q.data.(p) <- x :: q.data.(p); q.first <- min p q.first
let get_min q =
if q.first = 101 then None else
match q.data.(q.first) with
| [] -> assert false
| x :: tl ->
let p = q.first in
q.data.(q.first) <- tl;
while q.first < 101 && q.data.(q.first) = [] do
q.first <- q.first + 1
done;
Some(p, x)
end
type tree =
| Leaf of string
| Node of tree * tree
let rec huffman_tree q =
match Pq.get_min q, Pq.get_min q with
| Some(p1, t1), Some(p2, t2) -> Pq.add q (p1 + p2) (Node(t1, t2));
huffman_tree q
| Some(_, t), None | None, Some(_, t) -> t
| None, None -> assert false
(* Build the prefix-free binary code from the tree *)
let rec prefixes_of_tree prefix = function
| Leaf s -> [(s, prefix)]
| Node(t0, t1) -> prefixes_of_tree (prefix ^ "0") t0
@ prefixes_of_tree (prefix ^ "1") t1
let huffman fs =
if List.fold_left (fun s (_, p) -> s + p) 0 fs <> 100 then
failwith "huffman: sum of weights must be 100";
let q = Pq.make () in
List.iter (fun (s, f) -> Pq.add q f (Leaf s)) fs;
prefixes_of_tree "" (huffman_tree q);;
module Pq :
sig
type 'a t = { data : 'a list array; mutable first : int; }
val make : unit -> 'a t
val add : 'a t -> int -> 'a -> unit
val get_min : 'a t -> (int * 'a) option
end
type tree = Leaf of string | Node of tree * tree
val huffman_tree : tree Pq.t -> tree = <fun>
val prefixes_of_tree : string -> tree -> (string * string) list = <fun>
val huffman : (string * int) list -> (string * string) list = <fun>
第一个 advanced 啊,感觉,,,不如 gray code 那道麻烦。
总结
不是,等于说我一整天就写了两道题?是不是有点弱智了。
也不对,下午搞了会事务性的工作,还好,稍微有点弱智。
这几道题都有点强度啊,哈人。
文档信息
- 本文作者:nyaaar
- 本文链接:https://nyaaarlathotep.github.io/2024/09/02/Ocaml-99-%E9%97%AE%E9%A2%98%E7%AC%94%E8%AE%B0-3/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)