haskell 基础题解(38)

格子内输出

【题目】从控制台输入若干行。每行若干项,由逗号分开。输出时,要求把这些内容放到表格中,并且要居中对齐。
比如:从键盘上输入了:
a,bb,ccc
aaaa,ccccc
a,b,c,ddd
aa,ccccccccccccccccc,ee

(注意,连续的逗号表示中间的项是空的)
(注意,连续输入了两个回车就表示输入线束了)
这时,程序应该输出:
在这里插入图片描述
显然,程序需要计算出每个列的宽度。并把每个格子中的内容安排在合适的位置(通过加入空格)。
通用 haskell 解决这个问题时,要注意把信息本身,和它的表达形式区分开,这样各个击破不仅易于编写,也易于将来的维护。

上代码:


import Data.List (delete)

---- 按字符c把串分开为若干词
split :: Char -> String -> [String]
split _ [] = []
split c s = takeWhile (/=c) s : (split c . delete c . dropWhile (/=c)) s

----若干行词 --> 求出每列的最大宽度
getWidths :: [[String]] -> [Int]
getWidths [] = []
getWidths (ss:sss) = f (map length ss) (getWidths sss) 
    where
    f [] [] = []
    f [] y  = y
    f x  [] = x
    f (x:xs) (y:ys) = max x y : f xs ys

----生成在格子中显示效果的串列表
gridShow :: [Int] -> [[String]] -> [String]
gridShow l列宽 l多行 
    | null l多行   = [f l列宽]
    | otherwise = f l列宽 : g l列宽 (head l多行) : gridShow l列宽 (tail l多行)
    where 
    ---- f 列宽 ---> 生成边线
    f [] = "+"
    f (x:xs) = "+" ++ replicate x '-' ++ f xs
    ---- g 列宽 内空 ---> 内容行(无内容的列要补空格) 
    g [] _ = "|"
    g ws [] = g ws [""]
    g (w:ws) (x:xs) = "|" ++ fill w x ++ g ws xs
    fill w x = 
        let n = (w - length x) `div` 2
        in replicate n ' ' ++ x ++ replicate (w-n-length x) ' '  

ok :: [String] -> [String]
ok ss = let da = map (split ',') ss
            w  = getWidths da
        in gridShow w da   

----------------------------------------
---- 项间逗号分割,空行则结束输入
getSomeLines :: [String] -> IO [String]
getSomeLines xs = do
    s <- getLine
    if null s then 
        return $ reverse xs
    else
        getSomeLines (s:xs)

main :: IO ()
main = do 
    ss <- getSomeLines []
    putStr $ unlines (ok ss)

程序是由虚线分开的两个部分。虚线以下是实现 IO 交互的部分。它的主要工作是识别用户连续两个回车而结束程序,然后把读入的每个行变成 [String] 列表的一部分,放入到 ss 中,等待一个纯函数去处理它。
split 实现按给定的字符对串进行分割。

getWidths 扫描所有的行,得出每个项应该具有的宽度(该列所有元素的宽度的最大值,只是内容,不含格子)

gridShow 显示每一行。每一行的显示,先输出它的上边线,再输出多个项的内容。
当遇到空时,只输出一行边线,这就是整个表格的下边线。

内部的函数 g 是输出内容的。需要注意的是,当内容列表为空,而列宽列表不空时,意味着这行的后面的格子没有内容。这相当于内容为空串(因为空格和格子线还是要画出来的)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值