问题
I have type person = (String, Float, Float, [Int]) ** name, height, weight, miles walked past week**
The [Int] will contain data for how many miles the person walked on each day for the past 7 days e.g. [5,8,12,7,12,6,9]
My testData consists of multiple peoples data and I want to return all names and the 7 daily figures of the miles they walked as a single string, neatly into separate rows for each person and have the miles walked data lined up in columns.
e.g. 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])]
I want this returned like:
Return result
what is the best way to do this in haskell? Thanks
My Code --
personToString :: [Person] -> String
personToString [] = []
personToString ((name,height,weight,miles):person)=
name ++ take (9 - length name) (cycle " ") ++ intercalate ", " (map show miles) ++ "\n \n" ++ personToString person
This returns what I want but the miles digits don't line up as there is a mix of single and double digit figures
回答1:
OK, somebody took the trouble of putting together module Text.Printf.
Seems just a job cut for it here, so let's give it a try:
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
Program output:
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
Sole hitch in the deal I could find, the module is a bit hard to use under ghci: any looseness in the typing around printf
seems to derail the interactive compiler.
回答2:
Your main issue is properly padding columns with spaces. Going from this other answer, we can define our own left-padding and right-padding functions that take the desired length, the padding character, the string to pad, and return a padded string of the correct length (or more, if the string was already longer than the required length):
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
The name of your personToList
function is a bit confusing, since it takes a list of Person
. It would be more readable as personsToList
. To show our padding functions in action, we can look into formatting a single Person
without worrying for now about recursion:
personToString :: Person -> String
personToString (name, _, _, miles) = rPadWithChar 10 ' ' name
++ intercalate "," (map (lPadWithChar 3 ' ' . show) miles)
Notice you also don't need to bind the second and third elements of the Person
, since you're not using them. This is standard practice in Haskell and makes it easier, just by looking at the first few characters of a function, that it only uses the name
and miles
elements.
Finally, we deal with the recursion and multi-line formatting in a separate function which accepts a [Person]
.
personsToString :: [Person] -> String
personsToString = concat . intersperse "\n" . map personToString
--You only need one \n, right?
Here, I used the higher-order functions map
and intersperse
to abstract the recursion away, but you can easily turn this into explicit recursion. Here it is in action:
>>> 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
From the formatting of your test data, I assume that this is from a course. Just note that using tuples to carry around complex groups of real-world information is very error-prone and makes for hard-to-maintain code. For example, consider what would happen if you had a requirement to make the miles the first element of the tuple, you would end up with a completely broken API. The standard way to fix this is by using record syntax.
来源:https://stackoverflow.com/questions/61293535/haskell-display-string-into-8-columns