OCaml Programming: Polymorphic Types and Parser Optimization, Quizzes of Programming Languages

The implementation of a binary search tree in ocaml and optimization techniques for a recursive descent parser. It covers topics such as ocaml polymorphic types, parser shortcuts, grammar descriptions, and derivations. The document also includes examples of grammar productions and parse trees.

Typology: Quizzes

Pre 2010

Uploaded on 07/29/2009

koofers-user-sz7
koofers-user-sz7 🇺🇸

10 documents

1 / 10

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
CMSC 330, Spring 2009, Quiz 3 Practice Problem Solutions
1. OCaml Polymorphic Types
Consider a OCaml module Bst that implements a binary search tree:
module Bst = struct
type bst =
Empty
| Node of int * bst * bst
let empty = Empty (* empty binary search tree *)
let is_empty = function (* return true for empty bst *)
Empty -> true
| Node (_, _, _) -> false
let rec insert n = function (* insert n into binary search tree *)
Empty -> Node (n, Empty, Empty)
| Node (m, left, right) ->
if m = n then Node (m, left, right)
else if n < m then Node(m, (insert n left), right)
else Node(m, left, (insert n right))
(* Implement the following functions
val min : bst -> int
val remove : int -> bst -> bst
val fold : ('a -> int -> 'a) -> 'a -> bst -> 'a
val size : bst -> int
*)
let rec min = (* return smallest value in bst *)
let rec remove n t = (* tree with n removed *)
let rec fold f a t = (* apply f to nodes of t in inorder *)
let size t = (* # of non-empty nodes in t *)
end
a. Is insert tail recursive? Explain why or why not.
No, since the return value for recursive call to insert cannot be used as
the return value of the original call to insert. The return value is used to
create a Node data type first, and the Node value is returned.
b. Implement min as a tail-recursive function. Raise an exception for an empty bst.
Any reasonable exception is fine.
let rec min = function
Empty -> (raise (Failure "min"))
| Node (m, left, right) ->
if (is_empty left) then m
else min left
pf3
pf4
pf5
pf8
pf9
pfa

Partial preview of the text

Download OCaml Programming: Polymorphic Types and Parser Optimization and more Quizzes Programming Languages in PDF only on Docsity!

CMSC 330, Spring 2009, Quiz 3 Practice Problem Solutions

  1. OCaml Polymorphic Types Consider a OCaml module Bst that implements a binary search tree:

module Bst = struct type bst = Empty | Node of int * bst * bst

let empty = Empty (* empty binary search tree *)

let is_empty = function (* return true for empty bst *) Empty -> true | Node (_, _, _) -> false

let rec insert n = function (* insert n into binary search tree *) Empty -> Node (n, Empty, Empty) | Node (m, left, right) -> if m = n then Node (m, left, right) else if n < m then Node(m, (insert n left), right) else Node(m, left, (insert n right))

(* Implement the following functions val min : bst -> int val remove : int -> bst -> bst val fold : ('a -> int -> 'a) -> 'a -> bst -> 'a val size : bst -> int ) let rec min = ( return smallest value in bst ) let rec remove n t = ( tree with n removed ) let rec fold f a t = ( apply f to nodes of t in inorder ) let size t = ( # of non-empty nodes in t *) end

a. Is insert tail recursive? Explain why or why not. No, since the return value for recursive call to insert cannot be used as the return value of the original call to insert. The return value is used to create a Node data type first, and the Node value is returned. b. Implement min as a tail-recursive function. Raise an exception for an empty bst. Any reasonable exception is fine. let rec min = function Empty -> (raise (Failure "min")) | Node (m, left, right) -> if (is_empty left) then m else min left

c. Implement remove. The result should still be a binary search tree. let rec remove n = function Empty -> Empty | Node (m, left, right) -> if m = n then ( if (is_empty left) then right else if (is_empty right) then left else let x = min right in Node(x, left, remove x right) // OR // else let x = max left in // Node(x, remove x left, right) ) else if n < m then Node(m, (remove n left), right) else Node(m, left, (remove n right)) d. Implement fold as an inorder traversal of the tree so that the code List.rev (fold (fun a m -> m::a) [] t) will produce an (ordered) list of values in the binary search tree. let rec fold f a n = match n with Empty -> a | Node (m, left, right) -> fold f (f (fold f a left) m) right e. Implement size using fold. let size t = fold (fun a m -> a+1) 0 t

  1. Context Free Grammars a. List the 4 components of a context free grammar. Terminals, non-terminals, productions, start symbol b. Describe the relationship between terminals, non-terminals, and productions. Productions are rules for replacing a single non-terminal with a string of terminals and non-terminals c. Define ambiguity. Multiple left-most (or right-most) derivations for the same string d. Describe the difference between scanning & parsing. Scanning matches input to regular expressions to produce terminals, parsing matches terminals to grammars to create parse trees
  2. Describing Grammars a. Describe the language accepted by the following grammar: S  abS | a (ab)a b. Describe the language accepted by the following grammar: S  aSb |  anbn, n  0 c. Describe the language accepted by the following grammar: S  bSb | A A  aA |  bnabn, n  0 d. Describe the language accepted by the following grammar: S  AS | B A  aAc | Aa |  B bBb |  Strings of a & c with same or fewer c’s than a’s and no prefix has more c’s than a’s, followed by an even number of b’s e. Describe the language accepted by the following grammar: S  S and S | S or S | (S) | true | false Boolean expressions of true & false separated by and & or, with some expressions enclosed in parentheses f. Which of the previous grammars are left recursive? 2d, 2e g. Which of the previous grammars are right recursive? 2a, 2c, 2d, 2e h. Which of the previous grammars are ambiguous? Provide proof. Examples of multiple left-most derivations for the same string 2d: S => AS => AaS => aS => aB => a S => AS => S => AS => AaS => aS => aB => a 2e: S => S and S => S and S and S => true and S and S => true and true and S => true and true and true S => S and S => true and S => true and S and S => true and true and S => true and true and true
  1. Creating Grammars a. Write a grammar for axby, where x = y S  aSb |  b. Write a grammar for axby, where x > y S  aL L  aL | aLb |  c. Write a grammar for axby, where x = 2y S  aaSb |  d. Write a grammar for axbyaz, where z = x+y S  aSa | L L  bLa |  e. Write a grammar for axbyaz, where z = x-y S  aSa | L L  aLb |  f. Write a grammar for all strings of a and b that are palindromes. S  aSa | bSb | L L  a | b |  g. Write a grammar for all strings of a and b that include the substring baa. S  LbaaL L  aL | bL |  // L = any h. Write a grammar for all strings of a and b with an odd number of a ’s and b ’s. S  EaEbE | EbEaE E  EaEaE | EbEbE |  | SS // E = even #s i. Write a grammar for the “if” statement in OCaml S  if E then E else E | if E then E E  S | expr j. Write a grammar for all lists in OCaml S  [] | [E] | E::S E  elem | S // Ignores types, allows lists of lists k. Which of your grammars are ambiguous? Can you come up with an unambiguous grammar that accepts the same language? Grammar for 3h is ambiguous. An unambiguous grammar must exist since the language can be recognized by a deterministic finite automaton, and DFA -> RE -> Regular Grammar. Grammar for 3i is ambiguous. Multiple derivations for “if expr then if expr then expr else expr”. It is possible to write an unambiguous grammar by restricting some S so that no unbalanced if statement can be produced.
  2. Derivations, Parse Trees, Precedence and Associativity For the following grammar: S  S and S | true a. List 4 derivations for the string “true and true and true”. i. S => S and S => S and S and S => true and S and S => true and true and S => true and true and true ii. S => S and S => true and S => true and S and S => true and true and S => true and true and true iii. S => S and S => S and true => S and S and true => S and true and true => true and true and true iv. S => S and S => S and S and S => S and S and true => S and true and true => true and true and true v. S => S and S => S and S and S => true and S and S => true and S and true => true and true and true vi. S => S and S => S and S and S => S and true and S => true and true and S => true and true and true

S

S (^) and S

true

true true

S (^) or S

S

S (^) or S

true

true true

S (^) and S

Tree 1 Tree 2 S

S (^) and S

true

true true

S (^) or S

S

S (^) or S

true

true true

S (^) and S

Tree 1 Tree 2

f. What is implied about the precedence/associativity of “and” and “or” for each parse tree? Tree 1 => or has higher precedence than and Tree 2 => and has higher precedence than or g. Rewrite the grammar so that “and” has higher precedence than “or” and is right associative S  S or S | L // op closer to Start = lower precedence op L  true and L | true // right recursive = right associative

  1. Left factoring & eliminating left recursion Rewrite the following grammars so they can be parsed by a predicative parser by eliminating left recursion and applying left factoring where necessary a. S  S + a | b  S  b L L  + a L |  b. S  S + a | S + b | c  S  c L L  + a L | + b L |   S  c L L  + M |  M a L | b L c. S  S + a | S + b |   S  L L  + a L | + b L |   S  L L  + M |  M a L | b L d. S  a b c | a c  S  a L L b c | c

e. S  a b c | a b b  S  a b L L c | b f. S  a b c | a b  S  a b L L c |  g. S  a a | a b | a c  S  a L L a | b | c h. S  a a | a b | a  S  a L L a | b |  i. S  a a | a b |   S  a L |  L a | b j. S  a S c | a S b | b  S  a S L | b L c | b k. S  a S c | a S b | a  S  a L L S c | S b |   S  a L L S M |  M c | b l. S  a S c | a S | a  S  a L L S c | S |   S  a L L S M |  M c | 

  1. Parsing For the problem, assume the term “predictive parser” refers to a top-down, recursive descent, non-backtracking predictive parser. a. Consider the following grammar: S  S and S | S or S | (S) | true | false i. Compute First sets for each production and nonterminal

if (lookahead == “c”) { match(“c”); // S  cL parse_L( ); } else error( ); } parse_L( ) { if (lookahead == “a”) { match(“a”); // L  aL parse_L( ); } else if (lookahead == “c”) { match(“c”); // L  cL parse_L( ); } else ; // L  } d. Describe an abstract syntax tree (AST) Compact representations of parse trees with only essential parts

  1. Automata a. Describe regular grammars. Grammars where all productions are of the form X  a or X  aY b. Describe the relationship between regular grammars and regular expressions. Regular grammars are exactly as powerful as regular expressions (and one can be converted to the other) c. Name features needed by automata to recognize i. Regular languages (i.e., languages recognized by regular grammars) DFA (automaton with finite # of states and transitions) ii. Context-free languages NFA and 1 stack iii. All binary numbers DFA (binary #s can be recognized by RE) iv. All binary numbers divisible by 2 DFA (binary #s ending in 0 can be recognized by RE) v. All prime binary numbers DFA and 1 tape (can write a program to compute prime #s) d. Compare finite automata, pushdown automata, and Turing machines Pushdown automata are finite automata that can use 1 stack, Turing machines are finite automata that can use a tape (or 2 stacks). Turing machines > pushdown automata > finite automata in terms of computing power. e. Describe computability Problem that can be solved by algorithm of finite length f. Describe a Turing test When communicating by text, indistinguishable from human being