if ... then ... else ...
(use if'
instead if imported)[(i,j) | i <- [1,2], j <- [3,4]]
In trace-based programming a program is constructed stepwise from execution traces. The first step is to execute the algorithm to be implemented by hand. This execution is then translated into a table where the columns correspond to the variables of the program and each row describes an execution step. After that, one has to find operations which describe the changes of the values in each row. From that, the control-flow graph of the program can be derived. Finally, one has to come up with a binary decision tree for each operation which describes with what operation the program should continue after executing a particular operation. The structure of programs which are developed using this method coincides with sp-programs.
Starting from part 2, the task is to complete partial sp-programs. Certain information such as execution traces in the form of tables is given. Parts of the method can be applied to fill in the gaps.
type Operation a = (a -> a) type Predicate a = (a -> Bool)
a
.if2 :: (Predicate a) -> (Operation a) -> (Operation a) while :: (Predicate a) -> (Operation a) -> (Operation a) repeat :: Int -> (Operation a) -> (Operation a) foreach :: [b] -> (Operation (a,b)) -> (Operation a)
if2
gets a predicate arg1
and an operation arg2
as input parameters and should return the following operation. If arg1
is true then arg2
should be executed. Otherwise, the state of the program should remain unchanged.while
should return the following operation. As long as the predicate arg1
holds, the operation arg2
is executed repeatedly.
As soon as the predicate arg1
does not hold anymore, the state of the program is returned.repeat
should return the following operation. The operation arg2
is executed arg1
≥ 0 times.foreach
should return the following operation. For every element in the list arg1
the operation arg2
should be executed. In addition to the program state the operation arg2
also gets the element of the list. The list should be traversed in its natural order.
tailRecursion :: (Operation a) -> (Operation a) -> (Predicate a) -> (Operation a)
tailRecursion
using only the functions while
and (.)
(composition (f . g) x = f(g(x))
). Assume that arg1
\(=g\), arg2
\(=h\) and arg3
\(=p\).
primitiveSort lst = (while (not.isEmpty.?) ?) lst
primitiveSort
by replacing '?' in the above code.
bubbleSort :: (Ord a) => (Operation [a]) swapPred :: (Ord a) => (Predicate ([a],Int)) swapOp :: (Ord a) => (Operation ([a],Int))
swapPred
and swapOp
have to be defined adequately.
bubbleSort lst = program ? where n = length lst program = (repeat ? (foreach ? (if2 ? ?) ) )
The previous version of BubbleSort can be extended such that the algorithm terminates sooner in certain cases. The following two observations can be used to achieve this.
If no two elements are swapped during an iteration then the list is sorted.
After the first iteration the largest element becomes the last element of the list. After the second iteration the second-largest element becomes the second-last element of the list and so on. This means after the first iteration one does not have to consider the last element of the list anymore and after the second iteration one does not have to consider the two last elements of the list and so on.
Implement this extension of BubbleSort by replacing the '?' in the file AB3_2/bubbleSort.sp
. The traces produced when calling main
must be identical to the ones in AB3_2/reference
(this is tested when main
is called).
\(a\)=5, \(b\)=16 | ||
---|---|---|
\(i\) | \(c\) | \(r\) |
16 | 5 | 0 |
8 | 10 | |
4 | 20 | |
2 | 40 | |
1 | 80 | 80 |
\(a\)=7, \(b\)=11 | ||
---|---|---|
\(i\) | \(c\) | \(r\) |
11 | 7 | 7 |
5 | 14 | 21 |
2 | 28 | |
1 | 56 | 77 |
The number \(c\) is added to \(r\) if \(i\) is odd. The sequence of values assumed by \(i\) describe the binary representation of \(b\) (even=0, odd=1).
Implement the above algorithm by replacing the '?' in the file AB3_3/egyptMult.sp
. The traces produced when calling main
must be identical to the ones in AB3_3/reference
(this is tested when main
is called). You may assume that \(a, b \geq 0\).
a
and b
are given and it should be decided whether concat a == concat b
. For example, this holds for a = ["ab","cde"]
and b = ["a","bc","de"]
because both equal "abcde"
when concatenated. However, the strings in a
and b
are confidential. Therefore one can only access them indirectly:
coa :: CSMInstance -> Int cob :: CSMInstance -> Int lena :: CSMInstance -> Int -> Int lenb :: CSMInstance -> Int -> Int ssc :: CSMInstance -> Int -> Int -> Int -> Int -> Int -> Bool
coa
(= cardinality of a) returns the number of strings in a
(resp. cob
for b
).lena
(= length of element of a) returns the length of the (arg1
+1)-th string in a
(resp. lenb
for b
).ssc
(= substring call) returns true iff two substrings from a
and b
are equal. More specifically, ssc (CSMInstance a b) ca cb oa ob l
returns true iff the first l
characters from the substring a'
and b'
are equal with a'
being the substring of the (ca
+1)-th string in a
which starts from the (oa
+1)-th character (resp. b'
for cb
and ob
).AB3_4/csmAlg.sp
. The traces produced when calling main
must be identical to the ones in AB3_4/reference
(this is tested when main
is called). Additionally, you have to implement functions in the file AB3_4.hs
which allow you to express the predicates p_eqlen
and p_empty
.
The Josephus problem is defined as follows (see also Numberphile (Video)). There are \(n\) people standing in a circle who are labeled \(1\) to \(n\). In the beginning, person \(1\) removes person \(2\) from the circle. Then the new neighbor of \(1\) (person \(3\)) removes their next neighbor (person \(4\)) from the circle. The next person to remove their neighbor is person \(5\). This is repeated until only one person remains. What is the number of this person?
Implement this algorithm by replacing the '?' in the file AB3_5/josephus.sp
. The traces produced when calling main
must be identical to the ones in AB3_5/reference
(this is tested when main
is called).