Haskell Показать строку в 8 столбцов - PullRequest
0 голосов
/ 18 апреля 2020

У меня есть тип person = (String, Float, Float, [Int]) ** имя, рост, вес, мили, пройденные за последнюю неделю **

[Int] будет содержать данные о количестве миль человек ходил каждый день в течение последних 7 дней, например, [5,8,12,7,12,6,9]

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

например, testData = [(Джон, 1,76, 63, [5,8,12,7,12,6,9]), (Ханна, 1,64, 56, [6,9,10,9, 5,13,13]), (Льюис, 1,80, 73, [4,6,2,6,8,4,6])]

Я хочу, чтобы это возвращалось как:

Возвращаемый результат

Каков наилучший способ сделать это в haskell? Спасибо

Мой код -

personToString :: [Person] -> String   
personToString [] = []   
personToString ((name,height,weight,miles):person)=   
   name ++ take (9 - length name) (cycle " ") ++ intercalate ", " (map show miles) ++ "\n \n" ++ personToString person  

Это возвращает то, что я хочу, но цифры миль не совпадают, так как существует сочетание одинарных и двойных цифр git

Ответы [ 2 ]

1 голос
/ 18 апреля 2020

ОК, кто-то взял на себя труд собрать модуль Text.Printf .

Кажется, что здесь просто урезана работа, поэтому давайте попробуем:

import  Data.List (intercalate)
import  Text.Printf

type Person = (String, Float, Float, [Int]) -- ** name, height, weight, miles...

testData  :: [Person]
testData = [("John",   1.76, 63, [5,8,12,7,12,6,9]),
            ("Hannah", 1.64, 56, [6,9,10,9,5,13,13]),
            ("Lewis",  1.80, 73, [4,6,2,6,8,4,6])]

showMileList :: [Int] -> String
showMileList mileList = intercalate ","  $
                            map (printf "%3d") mileList

personToString :: Person -> String
personToString (name, h, w, miles) =
    (printf "%-9s" name) ++ " "                    ++
       (printf "%5.2f " h) ++ (printf "%3.0f " w)  ++
       (showMileList miles)

printAsLines :: [String] -> IO ()
printAsLines xs = mapM_ putStrLn  xs  -- a loop in the IO monad


main = do
   let strings = map  personToString  testData
   printAsLines strings

Вывод программы:

John       1.76  63   5,  8, 12,  7, 12,  6,  9
Hannah     1.64  56   6,  9, 10,  9,  5, 13, 13
Lewis      1.80  73   4,  6,  2,  6,  8,  4,  6

Единственная проблема в сделке, которую я мог найти, модуль немного сложен в использовании ghci: любая слабость при наборе текста вокруг printf, кажется, нарушает интерактивность компилятор.

0 голосов
/ 18 апреля 2020

Ваша главная проблема - правильно заполнить столбцы пробелами. Исходя из этого другого ответа , мы можем определить наши собственные функции заполнения слева и справа, которые принимают желаемую длину, символ заполнения, строку для заполнения и возвращают дополненную строку правильной длины ( или больше, если строка уже длиннее требуемой длины):

lPadWithChar, rPadWithChar :: Int -> Char -> String -> String
lPadWithChar l c s = replicate (l - length s) c ++ s
rPadWithChar l c s = s ++ replicate (l - length s) c

Название вашей функции personToList немного сбивает с толку, так как занимает список Person. Это было бы более читабельным как personsToList. Чтобы показать наши функции заполнения в действии, мы можем рассмотреть форматирование одного Person, не беспокоясь о рекурсии:

personToString :: Person -> String
personToString (name, _, _, miles) = rPadWithChar 10 ' ' name
  ++ intercalate "," (map (lPadWithChar 3 ' ' . show) miles)

Обратите внимание, что вам также не нужно связывать второй и третий элементы Person, так как вы их не используете. Это стандартная практика в Haskell и упрощает, просто посмотрев на первые несколько символов функции, что она использует только элементы name и miles.

Наконец, мы имеем дело с рекурсией и многострочным форматированием в отдельной функции, которая принимает [Person].

personsToString :: [Person] -> String
personsToString = concat . intersperse "\n" . map personToString
                  --You only need one \n, right?

Здесь я использовал функции высшего порядка map и intersperse для отвода рекурсии, но вы можете легко превратить это в явную рекурсию. Вот оно в действии:

>>> let testData = [("John", 1.76, 63, [5,8,12,7,12,6,9]), ("Hannah", 1.64, 56, [6,9,10,9,5,13,13]), ("Lewis", 1.80, 73, [4,6,2,6,8,4,6])]
>>> putStrLn $ personsToString testData
John        5,  8, 12,  7, 12,  6,  9
Hannah      6,  9, 10,  9,  5, 13, 13
Lewis       4,  6,  2,  6,  8,  4,  6

Исходя из форматирования ваших тестовых данных, я предполагаю, что это из курса. Просто отметьте, что использование кортежей для переноса сложных групп реальной информации очень подвержено ошибкам и затрудняет обслуживание кода. Например, подумайте, что произойдет, если у вас будет требование сделать мили первым элементом кортежа, в результате вы получите полностью сломанный API. Стандартный способ исправить это - использовать синтаксис записи .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...