Формат вывода списка в Haskell? - PullRequest
7 голосов
/ 08 мая 2011

У меня проблемы с попыткой отформатировать вывод списка моего собственного типа в Haskell.

Я бы хотел что-то вроде этого:

Make  | Model | Years(this is a list)    <- this would be the headers if you like
-------------------
Item1 | Item1 | Item1s,Item1s           
Item2 | Item2 | Item2s,Items2,Items2

^ Это будут данные, загруженные из моего типа String String [Int].

Как бы я это сделал в Хаскеле?

Ответы [ 3 ]

13 голосов
/ 08 мая 2011

Как правило, мы используем библиотеки «симпатичной печати», чтобы сделать хороший форматированный вывод.Стандартный, который вы должны знать, это Text.PrettyPrint .Для данного типа данных вы можете пройти этот тип, создав хорошо отформатированный документ.

Пример:

import Text.PrettyPrint
import Data.List

-- a type for records
data T = T { make  :: String
           , model :: String
           , years :: [Int] }
    deriving Show

-- test data
test =
    [ T "foo" "avenger" [1990, 1992]
    , T "bar" "eagle"   [1980, 1982]
    ]

-- print lists of records: a header, then each row
draw :: [T] -> Doc
draw xs =
    text "Make\t|\tModel\t|\tYear"
   $+$
    vcat (map row xs)
 where
    -- print a row
    row t = foldl1 (<|>) [ text (make t)
                         , text (model t)
                         , foldl1 (<^>) (map int (years t))
                         ]

-- helpers
x <|> y = x <> text "\t|\t" <> y
x <^> y = x <> text "," <+> y

Тестирование:

main = putStrLn (render (draw test))

Результаты в:

Make    |   Model   |   Year
foo     |   avenger |   1990, 1992
bar     |   eagle   |   1980, 1982

Способность быстро писать симпатичные принтеры - невероятно полезный навык.

5 голосов
/ 09 мая 2011

Вот обобщенный генератор таблиц.Он рассчитывает ширину столбца, чтобы соответствовать самой широкой строке.Тип ColDesc позволяет указать для каждого столбца выравнивание заголовка, строку заголовка, выравнивание данных и функцию для форматирования данных.

import Data.List (transpose, intercalate)

-- a type for records
data T = T { make  :: String
           , model :: String
           , years :: [Int] }
    deriving Show

-- a type for fill functions
type Filler = Int -> String -> String

-- a type for describing table columns
data ColDesc t = ColDesc { colTitleFill :: Filler
                         , colTitle     :: String
                         , colValueFill :: Filler
                         , colValue     :: t -> String
                         }

-- test data
test =
    [ T "foo" "avenger" [1990, 1992]
    , T "bar" "eagle"   [1980, 1982, 1983]
    ]

-- functions that fill a string (s) to a given width (n) by adding pad
-- character (c) to align left, right, or center
fillLeft c n s = s ++ replicate (n - length s) c
fillRight c n s = replicate (n - length s) c ++ s
fillCenter c n s = replicate l c ++ s ++ replicate r c
    where x = n - length s
          l = x `div` 2
          r = x - l

-- functions that fill with spaces
left = fillLeft ' '
right = fillRight ' '
center = fillCenter ' '

-- converts a list of items into a table according to a list
-- of column descriptors
showTable :: [ColDesc t] -> [t] -> String
showTable cs ts =
    let header = map colTitle cs
        rows = [[colValue c t | c <- cs] | t <- ts]
        widths = [maximum $ map length col | col <- transpose $ header : rows]
        separator = intercalate "-+-" [replicate width '-' | width <- widths]
        fillCols fill cols = intercalate " | " [fill c width col | (c, width, col) <- zip3 cs widths cols]
    in
        unlines $ fillCols colTitleFill header : separator : map (fillCols colValueFill) rows

Выполнение:

putStrLn $ showTable [ ColDesc center "Make"  left  make
                     , ColDesc center "Model" left  model
                     , ColDesc center "Year"  right (intercalate ", " . map show . years)
                     ] test

Результат:

Make |  Model  |       Year      
-----+---------+-----------------
foo  | avenger |       1990, 1992
bar  | eagle   | 1980, 1982, 1983
4 голосов
/ 08 мая 2011

Как-то так?

import Data.List (intercalate)
data Foo = Foo String String [Int]

fooToLine :: Foo -> String
fooToLine (Foo a b cs) = a ++ " | " ++ b ++ " | " ++ intercalate ", " (map show cs)

Теперь вы можете сделать

>>> fooToLine (Foo "Hello" "World" [1, 2, 3])
"Hello | World | 1, 2, 3"
...