Skip to content

Latest commit

 

History

History

haskell

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

Ninety-Nine Problems in Haskell

This is an adaptation of the Ninety-Nine Prolog Problems written by Werner Hett at the Berne University of Applied Sciences in Berne, Switzerland.

From the original source:

The purpose of this problem collection is to give you the opportunity to practice your skills in logic programming. Your goal should be to find the most elegant solution of the given problems. Efficiency is important, but logical clarity is even more crucial. Some of the (easy) problems can be trivially solved using built-in predicates. However, in these cases, you learn more if you try to find your own solution.

The problems have different levels of difficulty. Those marked with a single asterisk (*) are easy. If you have successfully solved the preceding problems you should be able to solve them within a few (say 15) minutes. Problems marked with two asterisks (**) are of intermediate difficulty. If you are a skilled Haskell programmer it shouldn't take you more than 30-90 minutes to solve them. Problems marked with three asterisks (***) are more difficult. You may need more time (i.e. a few hours or more) to find a good solution.

Table of Contents

Lists

Lists are recursive type in Haskell.

P01 (*) Find the last element of a list

Prelude> last1 ["a","b","c","d"]
"d"

We are using last1 because Haskell already has last function.

P02 (*) Find the last but one element of a list

Prelude> secondLast [1, 2, 11, 4, 5, 8, 10, 6]
10

When list is empty you should get exception as shown below.

Prelude> secondLast []
*** Exception: Can't find secondLast element from a list with less than 2 elements

When list has one element

Prelude> secondLast [1]
*** Exception: Can't find secondLast element from a list with less than 2 elements

P03 (*) Find the K'th element of a list

Prelude> kth [1,2,3,4,5] 3
4

P04 (*) Find the number of elements of a list

Prelude> length' [1..5]
5

length' is a valid function name in Haskell.

P05 (*) Reverse a list

Prelude> reverse' [1..5]
[5,4,3,2,1]

P06 (*) Find out whether a list is a palindrome

Prelude> isPalindrome ["x","a","m","a","x"]
True

Prelude> isPalindrome [1,2,3,4,5]
False

P07 (**) Flatten a nested list structure

Prelude> flatten (List [Elem 1, List [Elem 2, List [Elem 3, Elem 4], Elem 5]])
[1,2,3,4,5]

P08 (**) Eliminate consecutive duplicates of list elements

If a list contains repeated elements they should be replaced with a single copy of the element. The order of the elements should not be changed

Prelude> compress ["a", "a", "a", "a", "b", "c", "c", "a", "a", "d", "e", "e", "e", "e"]
["a","b","c","a","d","e"]

P09 (**) Pack consecutive duplicates of list elements into sublists

If a list contains repeated elements they should be placed in separate sublists.

Prelude> pack ["a", "a", "a", "a", "b", "c", "c", "a", "a", "d", "e", "e", "e", "e"]
[["a","a","a","a"],["b"],["c","c"],["a","a"],["d"],["e","e","e","e"]]

P10 (*) Run-length encoding of a list

Use the result of problem 1.09 to implement the so-called run-length encoding data compression method. Consecutive duplicates of elements are encoded as terms [N,E] where N is the number of duplicates of the element E.

Prelude> encode ["a", "a", "a", "a", "b", "c", "c", "a", "a", "d", "e", "e", "e", "e"]
[(4,"a"),(1,"b"),(2,"c"),(2,"a"),(1,"d"),(4,"e")]

P11 (*) Modified run-length encoding

Modify the result of problem 1.10 in such a way that if an element has no duplicates it is simply copied into the result list. Only elements with duplicates are transferred as [N,E] terms.

Prelude> encode_modified ["a", "a", "a", "a", "b", "c", "c", "a", "a", "d", "e", "e", "e", "e"]
[Right (4,"a"),Left "b",Right (2,"c"),Right (2,"a"),Left "d",Right (4,"e")]

P12 (*) Decode a run-length encoded list

Prelude> decode [Right (4,"a"),Left "b",Right (2,"c"),Right (2,"a"),Left "d",Right (4,"e")]
["a","a","a","a","b","c","c","a","a","d","e","e","e","e"]

P13 (**) Run-length encoding of a list (direct solution)

Implement the so-called run-length encoding data compression method directly. i.e. don't explicitly create the sublists containing the duplicates, as in problem P09, but only count them.

Prelude> encode_direct ["a", "a", "a", "a", "b", "c", "c", "a", "a", "d", "e", "e", "e", "e"]
[(4,"a"),(1,"b"),(2,"c"),(2,"a"),(1,"d"),(4,"e")]

P14 (*) Duplicate the elements of a list

Prelude> duplicate ["a","b","c","d"]
["a","a","b","b","c","c","d","d"]

P15 (**) Duplicate the elements of a list a given number of times

Prelude> duplicate ["a","b","c"] 3
["a","a","a","b","b","b","c","c","c"]

P16 (**) Drop every N'th element from a list

Prelude> dropEveryNth ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"] 3
["a","b","d","e","g","h","j","k"]

P17 (*) Split a list into two parts; the length of the first part is given

Prelude> split ["a", "b", "c", "d", "e", "f", "g", "h", "i", "k"] 3
(["a","b","c"],["d","e","f","g","h","i","k"])

P18 (**) Extract a slice from a list

Prelude> slice ["a", "b", "c", "d", "e", "f", "g", "h", "i", "k"] 3 7
["c","d","e","f"]

P19 (**) Rotate a list N places to the left

Prelude> rotate ["a", "b", "c", "d", "e", "f", "g", "h"] 3
["d","e","f","g","h","a","b","c"]

Prelude> rotate ["a", "b", "c", "d", "e", "f", "g", "h"] (-2)
["g","h","a","b","c","d","e","f"]

P20 (*) Remove the K'th element from a list

Prelude> removeAt ["a", "b", "c", "d"] 2
(["a","c","d"],"b")

P21 (*) Insert an element at a given position into a list

Prelude> insertAt ["a", "b", "c", "d"] 2 "alfa"
["a","alfa","b","c","d"]

P22 (*) Create a list containing all integers within a given range

Prelude> range 4 9
[4,5,6,7,8,9]

P23 (**) Extract a given number of randomly selected elements from a list

Prelude> randomSelect ["a", "b", "c", "d", "e", "f", "g", "h"] 3

P24 (*) Lotto: Draw N different random numbers from the set 1..M

Hint: Combine the solutions of problems P22 and P23

Prelude> randomSelect_lotto 6 (1,49)
[48,2,42,42,2,8]

P25 (*) Generate a random permutation of the elements of a list

Hint: Use the solution of problem P23

Prelude> randomPermutation ["a", "b", "c", "d", "e", "f"]
["b","a","b","c","a","a"]

P26 (**) Generate the combinations of K distinct objects chosen from the N elements of a list

Prelude> combinations ["a", "b", "c", "d", "e", "f"] 3

[["a","b","c"],["a","b","d"],["a","c","d"],["b","c","d"],["a","b","e"],["a","c","e"],["b","c","e"],["a","d","e"],["b","d","e"],["c","d","e"],["a","b","f"],["a","c","f"],["b","c","f"],["a","d","f"],["b","d","f"],["c","d","f"],["a","e","f"],["b","e","f"],["c","e","f"],["d","e","f"]]

P27 (**) Group the elements of a set into disjoint subsets

a) In how many ways can a group of 9 people work in 3 disjoint subgroups of 2, 3 and 4 persons? Write a predicate that generates all the possibilities via backtracking

Prelude> length (group3 ["aldo", "beat", "carla", "david", "evi", "flip", "gary", "hugo", "ida"])

1260

b) Generalize the above predicate in a way that we can specify a list of group sizes and the predicate will return a list of groups.

Prelude> length(group' ["aldo", "beat", "carla", "david", "evi", "flip", "gary", "hugo", "ida"] [2,2,5])

756

P28 (**) Sorting a list of lists according to length of sublists

a) We suppose that a list (InList) contains elements that are lists themselves. The objective is to sort the elements of InList according to their length. E.g. short lists first, longer lists later, or vice versa.

Prelude> lsort [["a", "b", "c"], ["d", "e"], ["f", "g", "h"], ["d", "e"], ["i", "j", "k"], ["m", "n"], ["o"]]

[["o"],["d","e"],["d","e"],["m","n"],["a","b","c"],["f","g","h"],["i","j","k"]]

b) Again, we suppose that a list (InList) contains elements that are lists themselves. But this time the objective is to sort the elements of InList according to their length frequency; i.e. in the default, where sorting is done in ascending order, lists with rare lengths are placed first, others with a more frequent length come later.

Prelude> lfsort [["a", "b", "c"], ["d", "e"], ["f", "g", "h"], ["d", "e"], ["i", "j", "k","l"], ["m", "n"], ["o"]]

[["o"],["i","j","k","l"],["a","b","c"],["f","g","h"],["d","e"],["d","e"],["m","n"]]

Arithmetic

P31 (**) Determine whether a given integer number is prime.

Prelude> isPrime 7
True

P32 (**) Determine the prime factors of a given positive integer.

Prelude> primeFactors 315
[3,3,5,7]

Prelude> primeFactors 33
[3,11]

P33 (**) Determine the prime factors of a given positive integer (2).

Prelude> primeFactorsMult 315
[[3,2],[5,1],[7,1]]

P34 (*) A list of prime numbers

Prelude> primeNumbers [7..31]
[7,11,13,17,19,23,29,31]

P35 (**) Goldbach's conjecture

Prelude> goldbach 28
[5,23]

P36 (**) A list of Goldbach compositions.

Given a range of integers by its lower and upper limit, print a list of all even numbers and their Goldbach composition.

Prelude> goldbach_list [9..20]
[(10,[3,7]),(12,[5,7]),(14,[3,11]),(16,[3,13]),(18,[5,13]),(20,[3,17])]

In most cases, if an even number is written as the sum of two prime numbers, one of them is very small. Very rarely, the primes are both bigger than say 50. Try to find out how many such cases there are in the range 2..3000.

Prelude> goldbach_list1 50 [1..2000]
[(992,[73,919]),(1382,[61,1321]),(1856,[67,1789]),(1928,[61,1867])]

P37 (**) Determine the greatest common divisor of two positive integer numbers.

Prelude> gcd' 36 63
9

P38 (*) Determine whether two positive integer numbers are coprime.

Prelude> coprime 25 64
True

P39 (**) Calculate Euler's totient function phi(m).

Prelude> totient_phi 10
4

P40 (**) Calculate Euler's totient function phi(m) (2).

See problem P39 for the definition of Euler's totient function. If the list of the prime factors of a number m is known in the form of problem 2.03 then the function phi(m) can be efficiently calculated as follows: Let [[p1,m1],[p2,m2],[p3,m3],...] be the list of prime factors (and their multiplicities) of a given number m. Then phi(m) can be calculated with the following formula:

phi(m) = (p1 - 1) * p1**(m1 - 1) * (p2 - 1) * p2**(m2 - 1) * (p3 - 1) * p3**(m3 - 1) * ...

Note that a**b stands for the b'th power of a.

Prelude> phi 10
4
Prelude> phi 99
60

Also you can import modules defined in 01-lists by starting ghci using the command ghci -i../01-lists

P41 (*) Compare the two methods of calculating Euler's totient function.

Use the solutions of problems P39 and P40 to compare the algorithms. Take the number of logical inferences as a measure for efficiency. Try to calculate phi(10090) as an example.

P46 (**) Truth tables for logical expressions.

Define predicates and/2, or/2, nand/2, nor/2, xor/2, impl/2 and equ/2 (for logical equivalence) which succeed or fail according to the result of their respective operations; e.g. and(A,B) will succeed, if and only if both A and B succeed. Note that A and B can be Prolog goals (not only the constants true and fail).

A logical expression in two variables can then be written in prefix notation, as in the following example: and(or(A,B),nand(A,B)).

Now, write a predicate table/3 which prints the truth table of a given logical expression in two variables.

table (\a b -> (and' a (or' a b)))
[(True,True,True),(True,False,True),(False,True,False),(False,False,False)]

P47 (*) Truth tables for logical expressions(2).

Skipping this problem for now.

P48 (**) Truth tables for logical expressions(3).

Skipping this problem for now.

P49 (**) Gray code.

An n-bit Gray code is a sequence of n-bit strings constructed according to certain rules. For example,

n = 1: C(1) = ['0','1'].
n = 2: C(2) = ['00','01','11','10'].
n = 3: C(3) = ['000','001','011','010','110','111','101','100'].
Prelude> gray 3
["000","001","011","010","110","111","101","100"]