
Hello Patrick,
I renamed your function maxTableColumnWidths to match up with my
naming. It seems to work except that it suffers from the same problem
as my 1st implementation when you feed it a table that has many rows:
*Table.IO> maxTableColumnWidths (replicate 1000000 ["hello", "world"])
*** Exception: stack overflow
Thanks,
Keith
On Sat, Feb 21, 2009 at 5:54 PM, Patrick LeBoutillier
Hi,
I came up with this, which seems to work well if all your rows have the same number of fields:
maxRowFieldWidths :: [[String]] -> [Int] maxRowFieldWidths rows = foldr rowMax zeros rows where rowMax fields maxes = zipWith (\f m -> max (length f) m) fields maxes zeros = 0 : zeros
foldr is the key here, which allows you to "accumulate" the results of computations. In this case the accumulator is the list of all the maximums found so far. The initial value for the accumulator is an (infinite) list made up on only zeros. Haskell will generate only as much as it needs.
Patrick
On Sat, Feb 21, 2009 at 4:35 PM, Keith Sheppard
wrote: Hello,
I'm new to haskell and still getting used to working with lazy evaluation. I created a little function to calculate column widths for a 2D list of strings (a table) by iterating through the list and accumulating a max value for each column. My initial implementation ran out memory for tables with many rows because of lazy evaluation and here is how I dealt with it:
{- | for a table, calculate the max width in characters for each column -} maxTableColumnWidths :: [[String]] -> [Int] maxTableColumnWidths [] = [] maxTableColumnWidths table = maxTableColumnWidthsInternal table []
maxTableColumnWidthsInternal :: [[String]] -> [Int] -> [Int] maxTableColumnWidthsInternal [] prevMaxValues = prevMaxValues maxTableColumnWidthsInternal (row:tableTail) prevMaxValues | seqList prevMaxValues = undefined | otherwise = maxTableColumnWidthsInternal tableTail (maxRowFieldWidths row prevMaxValues)
-- this little function is for making the list strict... otherwise -- we run out of memory seqList [] = False seqList (head:tail) | head `seq` False = undefined | otherwise = seqList tail
maxRowFieldWidths :: [String] -> [Int] -> [Int] maxRowFieldWidths row prevMaxValues = let colLengths = map length row lengthOfRow = length row lengthOfPrevMax = length prevMaxValues maxPrefixList = zipWith max colLengths prevMaxValues in if lengthOfRow == lengthOfPrevMax then maxPrefixList else if lengthOfRow > lengthOfPrevMax then maxPrefixList ++ (drop lengthOfPrevMax colLengths) else maxPrefixList ++ (drop lengthOfRow prevMaxValues)
This works but it isn't very pretty (maybe also inefficient?). Is there a better way to deal with this kind of memory issue?
Thanks! Keith _______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners
-- ===================== Patrick LeBoutillier Rosemère, Québec, Canada