I am trying to construct a tree from pre/postoreder traversals . My tree type is below:
data Tree = Emptytree | Node Integer [Tree]
I am new in functional programming. So I come across with some difficulties while tying construct my base cases and recursion.My function will be something like this:
ListToTree :: [Integer] -> [Integer] -> Tree
I construct some algorithms but I can not make it to fit to Language requirements.
My algorithm is quite simple:I take each element of first list (preorder traversal list) then I check it position in the second list. Let me give an example:
1
/ \
2 5
/ \
3 4
Preorder of this Tree traversal is as you know [1,2,3,4,5]
Postorder of this Tree traversal is as you know [3,4,2,5,1]
Firstly I look to the first element of first list it is 1 then I look it is position it in 2nd list it is last so I add this to my tree. Then I check next element of tree it is 2 in the second list it on the left of 1 it means it is child of it. Then 3 is on the left of 2 (in the second list) it means it is also the son of 2 then i look to 4 it is on the left of 2 it is son of 2, and lastly 5 it is on the left of 1 it is child of one (because it is on the right of 2 it is not a child of 2).
I tried to implement it . I write helper function which determines if Node has a child or not. I use also counter in my function So actually my function is like this:
ListToTree :: Integer -> [Integer] -> [Integer] -> Tree
{-First Stands for counter ,2nd preorder, 3rd postorder-}
MY base condition are:
1. is about if list are Emptytree return Emptytree
2. is about if counter== length-1 return Node element [Emptytree]
My main problematic part is in my recursive part:
ListToTree counter a b
| hasChild b counter == 1 = Node ( Element ) [ListToTree (1+counter) a b]
| hasChild b counter == 0 = Node ( Element ) [Emptytree]
{-My problematic part if node has no Child what I must change here-}
{-Or what are your suggestions-}
I need help in improving my algorithm Any kind of help or comments will be highly appreciated.
The beautiful thing about haskell is that you don't usually need a counter. It is usually sufficient to just do patter matching.
I will give the solution for [Tree] since that requires less cases. If you want a solution for a single Tree you can just introduce some cases in a wrapper function.
listToTree :: [Integer] -> [Integer] -> [Tree]
listToTree [] [] = []
listToTree (x:xs) ys = go where
fstSubTreePost = takeWhile (/=x) ys -- all the elems of 1. subtree except x
fstSubTreeLength = length fstSubTreePost
fstSubTreePre = take fstSubTreeLength xs
-- this will recursively compute the first subtree
fstTree = Node x (listToTree fstSubTreePre fstSubTreePost)
-- the next line will recursively compute the rest of the subtrees
rest = listToTree (drop fstSubTreeLength xs) (drop (fstSubTreeLength+1) ys)
go = fstTree : rest
Given the pre-order and post-order are [Integer], there may be zero or one or many trees that returns these traversals. For instances the traversals [1,1,1] and [1,1,1] have two possible trees. With 'mLast' and 'splits' helper function, it is possible to define a short 'listToTrees' which handles possible 'Forest' parsings. Then it is easy to define 'listToTree' as a special case that produces possible single 'Tree' parsings.
module PPT where
import Data.List
data Tree a = Emptytree | Node a (Forest a)
deriving Show
-- | A list of sibling trees, in left to right order
type Forest a = [Tree a]
-- | Returns a list of all valid trees that produce the given pre-order and post-order traversals.
--
-- If the input cannot be parsed into a Tree then results is an empty list.
listToTree :: [Integer] -> [Integer] -> [Tree Integer]
listToTree [] [] = [Emptytree] -- base case
listToTree [] _ = [] -- detect length mismatch
listToTree (x:xs) yAll = case mLast yAll of
Just (ys, y) | x==y -> map (Node x) (listToTrees xs ys) -- pre-order start == post-order end
_ -> []
-- | Given pre-order and post-order traversals of a forest, return a list of possible parsings.
listToTrees :: [Integer] -> [Integer] -> [Forest Integer]
listToTrees [] [] = [ [] ] -- base case
listToTrees [] _ = [] -- detect length mismatch
listToTrees (x:xs) ys = concatMap build (splits x ys) -- for each copy of 'x' in ysAll
where
build (belowX', _x', rightOfX') =
let (belowX, rightOfX) = splitAt (length pre) xs
in [ Node x kids : sibs
| kids <- listToTrees belowX belowX'
, sibs <- listToTrees rightOfX rightOfX' ]
-- | Safely split a non-empty into the initial portion and the last portion
mLast :: [a] -> Maybe ([a], a)
mLast [] = Nothing
mLast ys = Just (init ys, last ys)
-- | At each position for the given element 'x', split the input list 'ys' into (pre, x, post)
-- portions. The output has a tuple for each copy of 'x' in the input list 'ys'.
--
-- This could be better optimized to avoid (++), or changed to a zipper
splits :: Eq a => a -> [a] -> [ ([a], a, [a]) ]
splits x ysIn = unfoldr go ([], ysIn)
where
go (pres, ys) =
case span (x /=) ys of
(_, []) -> Nothing
(pre, x':post) -> Just ((pres ++ pre, x', post), (pres++pre++[x'], post))
-- | test1 has a single possible parsing
test1 :: ([Integer], [Integer])
test1 = ( [1, 2, 3, 4, 5]
, [3, 4, 2, 5, 1] )
-- | test2 has two possible parsings
test2 :: ([Integer], [Integer])
test2 = ( [1, 2, 1, 2]
, [2, 1, 2, 1] )
main :: IO ()
main = do
mapM_ print (uncurry listToTree test1)
mapM_ print (uncurry listToTree test2)
Related
Here is the list of lists: [[1,2,3],[1,2,3,4],[1,2,3]]
How can I increment each element of the second list by the length of the first list, and increment the third list by the length of the first list + second list? The first list should remain unchanged.
Intended output: [[1,2,3],[4,5,6,7],[8,9,10]]
Since the first list has length 3, the second list is generated by [1+3, 2+3, 3+3, 4+3].
Since the first list + second list combined have length 7, the third list is generated by [1+7, 2+7, 3+7].
Ideally it should work with any number of lists.
So far, I've had slight sucess using this:
scanl1 (\xs ys -> [y + length xs | y <- ys]) [[1,2,3],[1,2,3,4],[1,2,3]]
which outputs: [[1,2,3],[4,5,6,7],[5,6,7]]
scanl1 is a good idea, but it's not quite right, because you don't want your accumulator to be a list, but rather to be an integer. So you really want scanl, not scanl1. I'll leave it as an exercise for you to see how to adjust your solution - given that you managed to write something almost-right with scanl1, I don't think you'll find it too hard once you have the right function.
In the comments, jpmariner suggests mapAccumL :: (s -> a -> (s, b)) -> s -> [a] -> (s, [b])). That's perfectly typed for what we want to do, so let's see how it would look.
import Data.Traversable (mapAccumL)
addPreviousLengths :: [[Int]] -> [[Int]]
addPreviousLengths = snd . mapAccumL go 0
where go n xs = (n + length xs, map (+ n) xs)
λ> addPreviousLengths [[1,2,3],[1,2,3,4],[1,2,3]]
[[1,2,3],[4,5,6,7],[8,9,10]]
mapAccumL really is the best tool for this job - there's not much unnecessary complexity involved in using it. But if you're trying to implement this from scratch, you might try the recursive approach Francis King suggested. I'd suggest a lazy algorithm instead of the tail-recursive algorithm, though:
incrLength :: [[Int]] -> [[Int]]
incrLength = go 0
where go _ [] = []
go amount (x:xs) =
map (+ amount) x : go (amount + length x) xs
It works the same as the mapAccumL version. Note that both versions are lazy: they consume only as much of the input list as necessary. This is an advantage not shared by a tail-recursive approach.
λ> take 3 . incrLength $ repeat [1]
[[1],[2],[3]]
λ> take 3 . addPreviousLengths $ repeat [1]
[[1],[2],[3]]
There are many ways to solve this. A simple recursion is one approach:
lst :: [[Int]]
lst = [[1,2,3],[1,2,3,4],[1,2,3]]
incrLength :: [[Int]] -> Int -> [[Int]] -> [[Int]]
incrLength [] _ result = result
incrLength (x:xs) amount result =
incrLength xs (amount + length x) (result ++ [map (+amount) x])
(Edit: it is more efficient to use (:) in this function. See #amalloy comment below. The result then has to be reversed.
incrLength :: [[Int]] -> Int -> [[Int]] -> [[Int]]
incrLength [] _ result = reverse result
incrLength (x:xs) amount result =
incrLength xs (amount + length x) (map (+amount) x : result)
End Edit)
Another approach is to use scanl. We use length to get the length of the inner lists, then accumulate using scanl.
map length lst -- [3,4,3]
scanl (+) 0 $ map length lst -- [0,3,7,10]
init $ scanl (+) 0 $ map length lst -- [0,3,7]
Then we zip the lst and the accumulated value together, and map one over the other.
incrLength' :: [[Int]] -> [[Int]]
incrLength' lst =
[map (+ snd y) (fst y) | y <- zip lst addlst]
where
addlst =init $scanl (+) 0 $ map length lst
main = do
print $ incrLength lst 0 [] -- [[1,2,3],[4,5,6,7],[8,9,10]]
I'm trying to make it so that on a tuple input (n,m) and a list of tuples xs , if the first item in the tuple in xs is in (n,m) then keep it that way in the new list otherwise add the a tuple consisting of some value k from n to m as a first element and as second element it should be 0.My question is:how can i say "repeat 0" using guards ? Since clearly my code won't run since my code says "repeat = 0"
expand :: (Int,Int) -> Profile ->Profile
expand (n,m) [] = zip [n..m] (repeat 0)
expand (n,m) (x:xs) = zip [n..m] (repeat (|(fst (x) `elem` [n..m]) == False = 0
|otherwise = snd (x))
You can use a helper function here that converts a number in the [ n .. m ] range to a 2-tuple. Here we thus try to find an element in the list xs that matches with the first item of that tuple, if we do not find such element, we use 0:
import Data.List(find)
expand :: (Int,Int) -> Profile -> Profile
expand (n,m) xs = map go [n .. m]
where go i | Just l <- find (\(f, _) -> f == i) xs = l
| otherwise = (i, 0)
For a list, find was implemented as [src]:
find :: (a -> Bool) -> [a] -> Maybe a
find p = listToMaybe . filter p
filter thus will make a list that contains the elements that satisfy the predicate p, and listToMaybe :: [a] -> Maybe a will convert an empty list [] to Nothing, and for a non-empty list (x:_) it will wrap the first element x in a Just data constructor. Due to Haskell's laziness, it will thus look for the first element that satisfies the predicate.
this thus gives us:
Prelude Data.List> expand (2,7) [(4, 2.3), (6, 3)]
[(2,0.0),(3,0.0),(4,2.3),(5,0.0),(6,3.0),(7,0.0)]
given a list of list pairs ::[a,a], I would like to return the possible combinations of lists, where the sublists have been merged on the last of one sublit matching head of the next.
for example
-- combine two lists if they front and back match
merge :: Eq a => [[a]] -> [[a]]
merge (x:y:ys) | last x == head y = merge $ (x ++ (drop 1 y)) : ys
| otherwise = []
merge xs = xs
combinations :: Eq a => [[a]] -> [[a]]
combinations = nub . concatMap merge . permutations
λ= merge [1,2] [2,3]
[1,2,3]
-- there should be no duplicate results
λ= combinations [[1,3],[1,3],[1,3],[1,3],[2,1],[2,1],[2,1],[2,2],[3,2],[3,2],[3,2]]
[[1,3,2,2,1,3,2,1,3,2,1,3],[1,3,2,1,3,2,2,1,3,2,1,3],1,3,2,1,3,2,1,3,2,2,1,3]]
-- the result must be a completely merged list or an empty list
λ= combinations [[1,3], [3,1], [2,2]]
[]
λ= combinations [[1,3], [3, 1]]
[[1,3,1],[3,1,3]]
λ= combinations [[1,3],[3,1],[3,1]]
[[3,1,3,1]]
I can't quite wrap my head around the recursion needed to do this efficiently.
I ended with this solution, but it contains duplicates (you can use Data.List(nub) to get rid of them).
import Data.List(partition)
main :: IO ()
main = do
print $ show tmp
input = [[1,3],[1,3],[1,3],[1,3],[2,1],[2,1],[2,1],[2,2],[3,2],[3,2],[3,2]]
tmp = combinations input
-- this function turns list into list of pair, first element is element of the
-- input list, second element is rest of the list
each :: [a] -> [a] -> [(a, [a])]
each h [] = []
each h (x:xs) = (x, h++xs) : each (x:h) xs
combinations :: (Eq a) => [[a]] -> [[a]]
combinations l = concat $ map combine $ each [] l
where
-- take pair ("prefix list", "unused lists")
combine :: (Eq a) => ([a], [[a]]) -> [[a]]
combine (x, []) = [x]
combine (x, xs) = let
l = last x
-- split unused element to good and bad
(g, b) = partition (\e -> l == head e) xs
s = each [] g
-- add on element to prefix and pass rest (bad + good except used element) to recursion. so it eat one element in each recursive call.
combine' (y, ys) = combine (x ++ tail y, ys ++ b)
-- try to append each good element, concat result
in concat $ map combine' s
I'm not sure if I fully understand what you want to do, so here are just a few notes and hints.
given a list of list pairs ::[a,a]
(...) for example
λ= merge [1,2] [2,3]
Firstly those are not lists of pairs, each element of the list is an integer not a pair. They just happen to be lists with two elements. So you can say they are of type [Int] or an instance of type [a].
the sublists have been merged on the last of one sublit matching head of the next.
This suggests that the size of the lists will grow, and that you will constantly need to inspect their first and last elements. Inspecting the last element of a list implies traversing it each time. You want to avoid that.
This suggests a representation of lists with extra information for easy access. You only need the last element, but I'll put first and last for symmetry.
-- lists together with their last element
data CL a = CL [a] a a
cl :: [a] -> CL a
cl [] = error "CL from empty list"
cl xs = CL xs (head xs) (last xs)
clSafe :: [a] -> Maybe (CL a)
clSafe [] = Nothing
clSafe xs = Just (cl xs)
clFirst (CL _ x _) = x
clLast (CL _ _ x) = x
compatible cs ds = clLast cs == clFirst ds
Perhaps better, maybe you should have
data CL a = CL [a] a a | Nil
And to include an empty list that is compatible with all others.
Another point to take into account is that if e.g., you have a list xs and want to find lists ys to combine as ys++xs, then you want it to be very easy to access all ys with a given last element. That suggests you should store them in a suitable structure. Maybe a hash table.
Given a list like:
[1, 2, 2, 6, 7, 8, 10, 11, 12, 15]
Split it into blandly increasing ranges (maybe equal):
[[1, 2, 2], [6, 7, 8], [10, 11, 12], [15]]
I tried using a recursive approach:
splitRanges [] = [[]]
splitRanges (x:y:xs)
| x `elem` [y, y + 1] = [x, y] : splitRanges xs
| otherwise = xs
So if the item is one less or equal to the item after I fuse them. But it says I am trying to build an infinite type:
Occurs check: cannot construct the infinite type: a0 = [a0]
Expected type: [[a0]]
Actual type: [a0]
But what does [the fact that it is monotone] have to do with how the list is split?
That being strictly increasing would give different results.
Or are you really trying to say something else?
I hope I am not.
Will the list always be monotone?
No, splitting a monotone list means making it into just one sub-list.
If not, how should that affect the results?
If it is not monotone, you will have many sublists.
Is it always brown into groups of three?
No, the groups may contain n elements.
More examples would be good
splitRanges [1, 3] == [[1], [3]]
splitRanges [1, 2, 5] == [[1, 2], [3]]
splitRanges [0, 0, 1] == [[0, 0, 1]]
splitRanges [1, 5, 7, 9] == [[1], [5], [7], [9]]
I appreciate hints rather than full answers, as I would like to improve myself, copy-pasting is not improvement.
Try breaking the problem into more manageable parts.
First, how would you split just one blandly increasing range from the start of a list? Lets guess that should be splitOne :: [Integer] -> ([Integer], [Integer]).
Second, how can you repeatedly apply splitOne to the left-over list? Try implementing splitMany :: [Integer] -> [[Integer]] by using splitOne.
For splitOne, what should you be trying to find? The first position to split at. What are "split positions"? Lets make that up.
split 0 1 2 3 4 …
list [ | x1, | x2, | x3, | x4, | x5, …]
So a split at 0 is ([], [x1,x2,x3,x4,x5,…]), and a split at 3 is ([x1,x2,x3],[x4,x5,…]). What relationship can you see between the split position and the split list?
How do you determine the first split position of the list? Lets say that is implemented as firstSplitPos :: [Integer] -> Integer. What is the first split position of an empty list?
Can you now implement splitOne using firstSplitPos?
One Possible Answer
-- What are the adjacencies for:
-- 1) empty lists?
-- 2) lists with one element?
-- 3) lists with more than one element?
--
-- Bonus: rewrite in point-free form using <*>
--
adjacencies :: [a] -> [(a,a)]
adjacencies xxs = zip xxs (drop 1 xxs)
-- Bonus: rewrite in point-free form
--
withIndices :: [a] -> [(Int,a)]
withIndices xxs = zip [0..] xxs
-- This is the most involved part of the answer. Pay close
-- attention to:
-- 1) empty lists
-- 2) lists with one element
-- 3) lists which are a blandly increasing sequence
--
firstSplitPos :: (Eq a, Num a) => [a] -> Int
firstSplitPos xxs = maybe (length xxs) pos (find q searchList)
where q (_,(a,b)) = a /= b && a + 1 /= b
searchList = withIndices (adjacencies xxs)
-- Why is the split position one more than the index?
pos (i,_) = i + 1
--
-- Bonus: rewrite in point-free form using <*>
--
splitOne :: (Eq a, Num a) => [a] -> ([a],[a])
splitOne xxs = splitAt (firstSplitPos xxs) xxs
splitMany :: (Eq a, Num a) => [a] -> [[a]]
-- What happens if we remove the case for []?
splitMany [] = []
splitMany xxs = let (l, r) = splitOne xxs in l : splitMany r
Another Approach
This is my explanation of Carsten's solution. It is already succinct but I have elected for a variation which does not use a 2-tuple.
We know that Haskell lists are defined inductively. To demonstrate this, we can define an equivalent data type.
data List a = Cons a (List a) -- Cons = (:)
| Nil -- Nil = []
Then ask the question: can we use induction on lists for the solution? If so, we only have to solve two cases: Cons and Nil. The type signature of foldr shows us exactly that:
foldr :: (a -> b -> b) -- Cons case
-> b -- Nil case
-> [a] -- The list
-> b -- The result
What if the list is Nil? Then the only blandly increasing sequence is the empty sequence. Therefore:
nilCase = [[]]
We might want nilCase = [] instead, as that also seems reasonable — i.e. there are no blandly increasing sequences.
Now you need some imagination. In the Cons case we only get to look at one new element at a time. With this new element, we could decide whether it belongs to the right-adjacent sequence or if it begins a new sequence.
What do I mean by right-adjacent? In [5,4,1,2,2,7], 1 belongs to the right-adjacent sequence [2,2].
How might this look?
-- The rest of the list is empty
consCase new [] = [new] : []
-- The right-adjacent sequence is empty
consCase new ([]:ss) = [new] : ss
-- The right-adjacent sequence is non-empty
-- Why `new + 1 == x` and not `new == x + 1`?
consCase new sss#(xxs#(x:_):ss)
| new == x || new + 1 == x = (new:xxs):ss
| otherwise = [new]:sss
Now that we solved the Nil case and the Cons case, we are done!
splitRanges = foldr consCase nilCase
It would be useful and idiomatic to write your function to take a predicate, instead of writing your split condition into the function itself:
splitBy2 :: (a -> a -> Bool) -> [a] -> [[a]]
splitBy2 ok xs = snd $ f xs [] []
where f (a:b:xs) acc_list acc_out_lists | ok a b = ...
I hope you don't mind spoiling part of it, but as the comments are discussing what you want (and I hope I've got it) maybe you are interested in another possible solution?
I don't want to spoil it all but I think you can easily work this out:
blandly :: (Ord a, Num a) => [a] -> [[a]]
blandly = g . foldr f ([],[])
where f x ([],xss) = ([x],xss)
f x (y:ys,xss)
| abs (x-y) <= 1 = undefined
| otherwise = undefined
g (ys,xss) = undefined
you just have to fill in the undefined holes
The idea is just to fold the list from the right, accumulating your inner lists in the first item of the tuple, s long as the elements are not to far away; and if they are: to push it to the second item.
If done correctly it will yield:
λ> blandly [1,3]
[[1],[3]]
λ> blandly [1,2,5]
[[1,2],[5]]
λ> blandly [0,0,1]
[[0,0,1]]
λ> blandly [1,5,7,9]
[[1],[5],[7],[9]]
which seems to be what you want
1 hour later - I think I can post my solution - just stop reading if you don't want to get spoiled
blandly :: (Ord a, Num a) => [a] -> [[a]]
blandly = uncurry (:) . foldr f ([],[])
where f x ([],xs) = ([x],xs)
f x (y:ys,xs)
| abs (x-y) <= 1 = (x:y:ys,xs)
| otherwise = ([x],(y:ys):xs)
maybe I have a slight misunderstanding here (the examples did not specify it) - but if you want on only monotonic increasing inner lists you just have to change the abs part:
blandly :: (Ord a, Num a) => [a] -> [[a]]
blandly = uncurry (:) . foldr f ([],[])
where f x ([],xss) = ([x],xss)
f x (y:ys,xss)
| 0 <= y-x
&& y-x <= 1 = (x:y:ys,xss)
| otherwise = ([x],(y:ys):xss)
EDIT: while I'm still interested in an answer on the problems the execution faces in this case, it appears that it was indeed related to strictness since a -O fixes the execution and the program can handle the tree really quickly.
I'm currently working on the 67th problem of Project Euler.
I already solved it using simple lists and dynamic programming.
I'd like to solve it now using a tree datastructure (well, where a Node can have two parents so it's not really a tree). I thought I'd use a simple tree but would take care to craft it so that Nodes are shared when appropriate:
data Tree a = Leaf a | Node a (Tree a) (Tree a) deriving (Show, Eq)
Solving the problem is then just a matter of going through the tree recursively:
calculate :: (Ord a, Num a) => Tree a => a
calculate (Node v l r) = v + (max (calculate l) (calculate r))
calculate (Leaf v) = v
Obviously this has exponential time complexity though. So I tried to memoize the results with :
calculate :: (Ord a, Num a) => Tree a => a
calculate = memo go
where go (Node v l r) = v + (max (calculate l) (calculate r))
go (Leaf v) = v
where memo comes from Stable Memo. Stable Memo is supposed to memoize based on whether or not it has seen the exact same arguments (as in, same in memory).
So I used ghc-vis to see if my tree was correctly sharing nodes to avoid recomputation of things already computed in another branch.
On the sample tree produced by my function as such: lists2tree [[1], [2, 3], [4, 5, 6]], it returns the following correct sharing:
(source: crydee.eu)
Here we can see that the node 5 is shared.
Yet it seems that my tree in the actual Euler Problem isn't getting memoized correctly.
The code is available on github, but I guess that apart from the calculate method above, the only other important method is the one that creates the tree. Here it is:
lists2tree :: [[a]] -> Tree a
lists2tree = head . l2t
l2t :: [[a]] -> [Tree a]
l2t (xs:ys:zss) = l2n xs ts t
where (t:ts) = l2t (ys:zss)
l2t (x:[]) = l2l x
l2t [] = undefined
l2n :: [a] -> [Tree a] -> Tree a -> [Tree a]
l2n (x:xs) (y:ys) p = Node x p y:l2n xs ys y
l2n [] [] _ = []
l2n _ _ _ = undefined
l2l :: [a] -> [Tree a]
l2l = map (\l -> Leaf l)
It basically goes through the list of lists two rows at a time and then creates nodes from bottom to top recursively.
What is wrong with this approach? I thought it might that the program will still produce a complete tree parse in thunks before getting to the leaves and hence before memoizing, avoiding all the benefits of memoization but I'm not sure it's the case. If it is, is there a way to fix it?
This doesn't really address the original question, but I find it is usually easier and more powerful to use explicit memoization.
I chose to store the triangle as a list indexed by a position rather than a tree:
[ ((1,1),3),
((2,1),7), ((2,2), 4),
....
Suppose that part of the result has already been memoized in a list of this format. Then computing the answer at a particular coordinate is trivial:
a # i = let Just v = lookup i a in v
compute tree result (x,y) = tree # (x,y) + max (result # (x+1,y)) (result # (x+1,y+1))
Now we must build result. This is also trivial; all we have to do is map compute over all valid indices.
euler67 :: [((Int, Int), Integer)] -> Integer
euler67 tree = result # (1,1)
where
xMax = maximum $ map (fst . fst) tree
result = [ ((x,y), compute (x,y)) | x <- [1 .. xMax], y <- [1..x] ]
++ [ ((xMax + 1,y),0) | y <- [1..xMax + 1]]
compute (x,y) = tree # (x,y) + max (result # (x+1,y)) (result # (x+1,y+1))
Computing height of the triangle (xMax) is just getting the maximum x-index. Of course we are assuming that the tree is well formed.
The only remotely complicated part is determining which indices are valid for result. Obviously we need 1 row for every row in the original tree. Row x will have x items. We also add an extra row of zeroes at the bottom - we could handle the base case in a special way in compute but it is probably easier this way.
You'll notice that is is quite slow for the hundred row triangle. This is because lookup is traversing three lists per call to compute. To speed it up I used arrays:
euler67' :: Array (Int, Int) Integer -> Integer
euler67' tree = result ! (1,1)
where
((xMin, yMin), (xMax, yMax)) = bounds tree
result = accumArray (+) 0 ((xMin, yMin), (xMax + 1, yMax + 1)) $
[ ((x,y), compute (x,y)) | x <- [xMin .. xMax], y <- [yMin..x] ]
++ [ ((xMax + 1,y),0) | y <- [yMin..xMax + 1]]
compute (x,y) = tree ! (x,y) + max (result ! (x+1,y)) (result ! (x+1,y+1))
Also here is the code I used for reading the files:
readTree' :: String -> IO (Array (Int, Int) Integer)
readTree' path = do
tree <- readTree path
let
xMax = maximum $ map (fst . fst) tree
yMax = maximum $ map (snd . fst) tree
return $ array ((1,1), (xMax,yMax)) tree
readTree :: String -> IO [((Int, Int), Integer)]
readTree path = do
s <- readFile path
return $ map f $ concat $ zipWith (\n xs -> zip (repeat n) xs) [1..] $ map (zip [1..] . map read . words) $ lines s
where
f (a, (b, c)) = ((a,b), c)