Specifications.Assign2
This assignment is due on Thursday 9/18 by 8:00PM. You should put all of your programming solutions in a file called assign2/lib/assign2.ml
. See the file test/test_assign2.ml
for example behavior of each function. You should put all your written solutions in a single pdf file.
These problems come from a list of Exercises on OCaml's webpage. We won't grade these (the solutions are given with the problem statements) but we'll typically include a couple with each assignment for additional practice.
Implement the function drop_leading
so that drop_leading k l
is the result of removing all instances of k
from the beginning of the list l
.
Also implement the function drop_trailing
so that drop_trailing k l
is the result of removing all instances of k
from the end of the list l
. Hint. Don't re-implement the same logic as above, use drop_leading
and another common list function.
Implement the function split_on_char
so that split_on_char sep s
is the list of all (possibly empty) substrings of s
that are delimited by the character sep
. If s
is empty, the result is the singleton list [""]
(this description is taken from the OCaml standard library documentation).
Note. Working with strings in OCaml is different from working with lists (in a sense they might seem more familiar if you're coming from a language with arrays). We cannot destruct strings. Rather we use the functions String.get
and String.sub
to access parts of the string. See the Standard library documentation on strings for more details.
In this problem we're going to implement an interpreter for FRACTRAN. Interpreting a FRACTRAN program means working with very large numbers, and integers in OCaml are not arbitrary-precision. Therefore, we begin by installing a library for arbitrary-precision integer arithmetic. In a terminal, run the following:
eval $(opam env)
opam install zarith
This library will give us access to two new types: Z.t
for arbitrary-precision integers and Q.t
for arbitrary-precision rationals. Remember: There is no operator overloading in OCaml. We cannot use the +
or *
operator on values of these types. We will be clear in each subproblem below which functions you'll want to use in your implementation.
Implement the function parse_fractran
so that parse_fractran s
is a FRACTRAN program (i.e., a list of fractions) according to the following rules:
'/'
character and no spaces.See the example FRACTRAN programs in the given implementations of max_fractran
and fib_fractran
below. For this problem, we will assume the input is well-formed. In particular, we will consider the behavior of the function undefined otherwise. You'll likely want to use the following functions in your implementation.
Z.of_string s
is the arbitrary-precision integer represented by the string s
.Q.make num den
is the arbitrary precision fraction with numerator num
and denominator den
.List.nth l i
is the i
th element of l
assuming 0 <= i
and i < List.length l
.split_on_char
from the previous problem will also naturally be useful.Implement the function eval_fractran
so that eval_fractran p i
evaluates the FRACTRAN program p
on the given input i
. See the previous assignment for what this means, along with the written problem below. You'll likely want to use the following functions in your implementation.
Q.mul a b
is the the result of multiplying a
and b
.Q.num a
is the numerator of a
and is an aribtrary-precision integer (i.e., of type Z.t
)Q.den a
is the denominator of a
and is an arbitrary-precision integer.Z.one
is the number one as an arbitrary-precision integer.Important: Arbitrary-precision rationals are always given in reduced form. This makes it easy to determine if a rational can be converted into an integer.
The remaining functions are implemented for you.
interp_fractran
is parse_fractran
followed by eval_fractran
.
max_fractran i j
is the same as max i j
but implemented in a very round-about way, i.e., by writing the program in FRACTRAN, running our interpreter and extracting the output.
fib_fractran n
is the n
th value of the Fibonacci sequence, implemented using our FRACTRAN interpreter.
\def\nt#1{\textcolor{blue}{\texttt{<}\texttt{#1}\texttt{>}}} \def\tm#1{\textcolor{red}{\texttt{#1}}} \nt{expr} \Coloneqq \nt{expr}\tm{.[} \nt{expr} \tm{]}
Write down an English sentence which expresses the same thing as the above grammar rule (this is actual OCaml syntax).
\def\code#1{\textcolor{purple}{\texttt{#1}}} \{ \code{s} : \code{string}, \code{i} : \code{int} \} \vdash \code{s.[i]} : \code{char}
Write down an English sentence which expresses the same thing as the above typing judgment.
\def\code#1{\textcolor{purple}{\texttt{#1}}} \frac {\Gamma \vdash e_1 : \code{string} \qquad \Gamma \vdash e_2 : \code{int}} {\Gamma \vdash e_1\code{.[}e_2\code{]} : \code{char}} \ (\text{string-index})
Write down an English sentence which expresses the same thing as the above typing rule.
\def\code#1{\textcolor{purple}{\texttt{#1}}} \Gamma \vdash \code{let rec f x = f (x + g (f y)) in h (g (x = y)) = f x} : \tau
Determine the type \tau
and the smallest context \Gamma
such that the above typing judgment holds in OCaml. (Hint. Come up with dummy values for the unknown variables in the above expression so that you can type it into UTop and it will type-check.)
(Another problem about FRACTRAN? Probably the last one...) In this problem, we will look at the semantics rules for FRACTRAN.
The semantic rules for FRACTRAN are given in terms of configurations. A configuation consists of two FRACTRAN programs P
and Q
, and an integer value n
, written \langle \ P \ , \ Q \ , \ n \ \rangle
. We think of P
as the input program, Q
as the part of the program we haven't yet processed, and n
as the input that is updated throughout the evaluation process. An evaluation judgment is of the form
\langle \ P \ , \ Q \ , \ n \ \rangle \longrightarrow \langle \ P' \ , \ Q' \ , \ n' \ \rangle
and it means that the configuration the the left-hand-side evaluates to the configuration on the right-hand-side in one step. There are two rules for evaluating a FRACTRAN program.
\frac {\textcolor{green}{\frac{pn}{q} \text{ is an integer}}} {\left\langle \ P \ , \ \frac{p}{q} :: Q \ , \ n \ \right\rangle \longrightarrow \left\langle \ P \ , \ P \ , \ \frac{pn}{q} \ \right\rangle} \ (\text{int}) \qquad \qquad \frac {\textcolor{green}{\frac{pn}{q} \text{ is not an integer}}} {\qquad\qquad???\qquad\qquad} \ (\text{not-int})
Recall that the green parts of the rules are called side-conditions because they are not evaluation judgments, they are just conditions that need to hold in order for the rule to be applied. The rule (int) says that if the next fraction p / q
in Q
times our value n
is an integer, then we reset Q
to P
and set n
to pn / q
. The rule (not-int) is missing its conclusion, but should express that if the next fraction p / q
in Q
times n
is not an integer, then we should remove it from Q
and keep n
as it is.
The Task. Write (not-int) as a formal inference rule.