When you are working with functions that take in lists, you will often want to work with recursion and list element functions.
Let's take a look at what you have so far.
On the fourth line, you have build evaluate to the following when it receives a list with three elements:
build [x,y,z] = Node (Node (build [x]) y Leaf) z Leaf
On the third line, you have build evaluates as follows when it receives a list of two elements:
build [x,y] = Node (build [x]) y Leaf
If you look at those two lines, you can notice that there is a bit of repetition. Specifically both contain Node (build [x]) y Leaf
. When there is repetition like this, recursion can help.
Using recursion, you can replace that repetition with a recursive call of the build function with a list of two elements. For example, you could replace line four with:
build [x,y,z] = Node (build [x,y]) z Leaf
That makes it a little bit simpler, but it doesn't yet solve the problem.
build :: [LogMessage] -> MessageTree
build [x] = Node Leaf x Leaf
build [x,y] = Node (build [x]) y Leaf
build [x,y,z] = Node (build [x,y]) z Leaf
If you take a look at the new function, you can see that some of the constants that we are using could be replaced by using list element functions, like head
, tail
, last
, and init
.
In each pattern of the function we only treat the last element of the list separately. The non-last elements are then passed back into build
. Thus we can get rid of some of the pattern matching by using last
to access the last element of the list, and init
to get all of the elements except for the last element.
build :: [LogMessage] -> MessageTree
build [x] = Node Leaf x Leaf
build l = Node (build (init l)) (last l) Leaf
By working with general list elements using init
and last
, instead of working with explicitly specified elements of a list, we can remove the need for infinite pattern matching.