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).