Prolog
======
1) Note the function append:
append([], X, X).
append([X|Xs], Ys, [X|Zs]) :- append(Xs, Ys, Zs).
Write function mymember(X, Xs) that checks, given an X and a
list Xs, whether Xs contains X.
a) Write such function as a recursively defined predicate.
b) Write such function using a single call to append.
2) Let a graph be given by a binary edge predicate e that
applies to constants.
a) Write a predicate
path(Start, NoNodes, End)
that holds if there is a path from Start to End such that,
excluding Start,End, the path does not pass through any of
the graph nodes in NoNodes list. Assume that the predicate
is called with all variables bound to known values. The
predicate should succeed even if the graph given by e has
cycles. You are allowed to use negation and assume that it
behaves according to its logical meaning.
b) Re-express the previous solution in Scala. Given a Scala type
type Node = Symbol
type Graph = List[(Node,Node)]
and a value declared by
val e : Graph = ...
write a Scala function of signature
def path(start: Node, noNodes : List[Node], end : Node) : Boolean
that corresponds to the Prolog predicate from the previous
part.
Non-determinism, proofs, and monads
===================================
3) We model non-deterministic imperative computation with
state S as the type.
type N[S] = S => Stream[S]
Complete the following worksheet definitions.
Define function that either doubles or triples its argument.
def doubleOrTriple : N[Int] = ???
Define function that either adds a or b to the state.
def addOneOf(a : Int, b : Int) : N[Int] = ???
compose(f,g) composes two non-deterministic computations,
running first f, then g. The function should be associative.
def compose[S](f : N[S], g : N[S]): N[S] = ???
Prove that your compose is associative.
Identity should be a computation that does nothing.
When composed with any function from left or right,
it should give back the same function.
def id[S]: N[S] = ???
We can put non-deterministic computations in a list.
type Actions[S] = List[N[S]]
composeAll should compose all functions in a given list,
applying the functions at the beginning of the list first.
def composeAll[S](as : Actions[S]) : N[S] = ???
runAll should run all actions in a given action list,
starting from the first action.
def runAll[S](as : Actions[S], on : S) : Stream[S] = ???
Here is an example list of actions.
val actions : List[N[Int]] =
List(addOneOf(5, 20), doubleOrTriple, addOneOf(0, 100))
Here is how you can run it and get it all displayed:
runAll(actions, 1).toList