Você está na página 1de 6

Haskell Driving Test

Friday 8th November 2013


15-00pm 17-30pm
TWO AND ONE HALF HOURS
(including 15 minutes planning time)

Please make your swipe card visible on your desk.


After the planning time log in using your username
as both your username and password.
Work in the file named Exam.hs.

Full marks can be obtained by answering questions 17.


The maximum total is 25.
Credit will be awarded throughout for the style, clarity
and the appropriate use of list comprehensions, higherorder functions etc.
Please make sure your program compiles. (Comment out
any code which you cannot get to compile.) There will
be a 2 mark penalty for programs which do not
compile.
1

In this exercise you are required to write an interpreter for a simple firstorder functional programming language called FUN, similar in style to a (very)
stripped-down version of Haskell. Rather than work with source code (characters in a text file, for example) the programs you will be manipulating will
be represented by a Haskell data type. A FUN program consists of a sequence
of top-level function definitions, each of which associates a function name (a
String) with a definition. A definition has two components: the names of the
functions arguments (a list of Strings) and the right-hand side, which is an
expression. For example, the function
f x y z = e
in a Haskell-like syntax would be represented as follows:
( "f", ( [ "x", "y", "z" ], rhs ) )
where rhs is the representation of the expression e above. This may refer to
arguments "x", "y" and "z" by name, just as in Haskell. The arity of a function
is the number of arguments it expects. For example, the arity of "f" here is 3.
FUN supports only integer base types, so every expression evaluation generates an integer result. An expression in FUN is one of the following:
1. A constant, i.e. an integer
2. An argument identifier, i.e. a string
3. A conditional, like Haskells if...then...else...
4. A binary operator application
5. A user-defined function application
There are four familiar built-in operators: +, *, ==, >, represented by constructors of an enumerated data type (see below). Each of these operators has
exactly two arguments, which are expressions. In a well-formed program each
user-defined function application has exactly the right number of argument expressions (corresponding to its arity). With the exception of the final question,
you may assume that all programs you will be manipulating are well-formed in
this sense. The following Haskell types can be used to represent FUN programs:
type Identifier = String
type FunctionName = String
data BinOp = EQU | GRT | ADD | MUL
deriving (Eq,Ord,Show)
data Expr = Val Int | Id Identifier | If Expr Expr Expr |
OpApp BinOp Expr Expr | FunApp FunctionName [ Expr ]
2

deriving ( Eq, Ord, Show )


type Definition = ( [ Identifier ], Expr )
type Function = ( FunctionName, Definition )
type Program = ( [ Function ], Expr )
To illustrate this representation the following two examples show FUN functions represented in an imaginary Haskell-like source syntax and internally as
a Haskell object of type Function. The first defines the equivalent of the max
function in Haskell; the second defines the factorial function recursively. Both
of these, and an additional example, are provided in the template provided.
-- Maximum of two numbers
-- Haskell-like equivalent: max2 a b = if a > b then a else b
-max2 :: Function
max2
= ( "max2", ( [ "a", "b" ],
( If ( OpApp GRT ( Id "a" ) ( Id "b" ) )
( Id "a" )
( Id "b" )
)
)
)

-- Factorial
-- Haskell-like equivalent:
-fact :: Function
fact
= ( "fact", ( [ "x" ],
( If ( OpApp
( Val 1
( OpApp

fact x = if x==0 then 1 else x * fact (x-1)

EQU ( Id "x" ) ( Val 0 ) )


)
MUL ( Id "x" )
( FunApp "fact"
[ OpApp ADD ( Id "x" ) ( Val (-1) ) ]
)

)
)
)
)
A program is represented by a list of top-level function definitions, each like the
above, and a top-level expression, analogous to an expression you might type at
3

the GHCi prompt. Here are two examples that use the max2 and fact functions
(note the Program type synonym above):
eg1, eg2 :: Program
-- When evaluated eg1 should give 15 and eg2 should give 120
-eg1
= ( [ max2 ], FunApp "max2" [ Val 12, Val 15 ] )
eg2
= ( [ max2, fact ], FunApp "fact" [ FunApp "max2" [ Val 5, Val 3 ] ] )
These, and some additional examples, are provided in the template that accompanies this exercise.
Spend a moment to ensure that you understand fully these examples.

Program execution
Execution of a FUN program is performed relative to an environment and a list
of top-level function definitions, such as those above. An environment associates
an identifier with an integer value and will be represented in the interpreter by
a list of (Identifier,Int) pairs:
type Environment

= [ ( Identifier, Int ) ]

A reference to an identifier invokes a look-up of the identifier in the environment.


The list of top-level function definitions works similarly, this time mapping a
function name to its definition:
type Defs = [ Function ]
with Function as above.

What to do
1. Define a function search :: Eq a => a -> [ ( a, b ) ] -> b that
looks up the value associated with a key, in a list of (key,value) pairs. Note
that the key can be any type that is a member of Eq, whilst the value can
have any type. For example, search "x" [("a",198),("x",2),("y",12),("x",8)]
should return 2. You may assume that there is always at least one binding
for the given item in the list. If there is more than one binding you should
return the leftmost one, as in the example. Note that this function can be
used to look up both an identifier in an environment and a function name
in a list of function definitions.
[2 marks]
2. Define a function arity :: Function -> ( FunctionName, Int ) that,
given a user-defined function, returns both the name of the function and its
arity, in the form of a pair. For example, arity ("f",(["x","y","z"],Val
1)) should return ("f",3).
[1 mark]
4

3. Define a function arities :: [ Function ] -> [ ( FunctionName,


Int ) ] that returns the arity of each function in a list of functions,
e.g. arities [ ("f",(["x","y","z"],Val 1)), ("g",(["a","b"],Id
"a")) ] should return [("f",3),("g",2)] in that order.
[2 marks]
4. Define a function applyBinOp :: BinOp -> Int -> Int -> Int that
returns the result of applying a given binary operator to two integer arguments. There is no equivalent of type Bool in FUN, so False and True
should be represented by the integers 0 and 1 respectively. thus, for example, applyBinOp MUL 4 7 should return 28 and applyBinOp GRT 5 10
should return 0 (i.e. the representation of False).
[3 marks]
5. Define a function eval :: Expr -> Environment -> Defs -> Int that
evaluates a given expression, given an environment and a list of top-level
function definitions. We need the environment because the expression may
refer to one or more argument identifiers and we need the list of function
definitions because the expression may invoke a user-defined function. If
we call the environment e and the list of function definitions ds, the rules
are as follows:
(a) To evaluate a basic value of the form Val x, return x.
[1 mark]
(b) To evaluate an argument identifier reference of the form Id s use the
search function to look up the binding for s in e.
[1 mark]
(c) To evaluate a conditional of the form If p q r, first evaluate p using
eval, using the same e and ds. If the result is 1 (True), return
the result of evaluating q similarly; otherwise return the result of
evaluating r similarly.
[2 marks]
(d) To evaluate a binary operator application of the form OpApp op e1
e2, first evaluate e1 and e2 using eval. Then use applyBinOp to
invoke the rule for Op.
[1 mark]
(e) To evaluate a user-defined function application of the form FunApp
f args where args is a list of argument expressions:
i. Evaluate each argument in args using eval, giving a list of integer values, vs say.
ii. Look up the definition for f in the function definition list ds
(use search again to do this) giving a pair of the form (as,rhs)
where as is the list of argument names and rhs is the right-hand
side expression.

iii. Return the result of evaluating rhs in the environment e extended with additional bindings that associate the argument
names (as) with their values (vs) pairwise. Remember to add
the new bindings to the front of e.
[5 marks]
Note that the list of function definitions remains unchanged throughout,
but you need to carry it round to implement the rules for eval.
6. Define a function runProgram :: Program -> Int that takes a wellformed program comprising a list of function definitions and an expression
to be computed, and which uses eval to evaluate the expression. The
initial environment in the top-level call to eval should be []. To test
your runProgram function, execute the five programs provided in the
template. For example, when applied to eg2 it should return 120 (the
factorial of the maximum of 5 and 3, i.e. 5! = 5 4 3 2 1).
[2 marks]

7. Using the function arities (and/or arity) above, define a function isValid
:: Program -> Bool that checks whether all user-defined function applications have the correct number of arguments. The function should
return True if and only if (iff) the program is well-formed in this sense.
[5 marks]

Você também pode gostar