Haskell打印水平线

时间:2016-09-17 15:32:24

标签: haskell printing functional-programming line

我正在写一个函数' printLine' - 给定一个int列表 - 返回一个包含水平线的字符串。此行的长度应为整数列表的最大值。例如:

printLine [1, 3, 4, 0]

应该返回:

+----+----+----+----+

(请注意,长度取决于连字符的数量,而不是' +' -signs。应始终有5' +' -signs。)

我已经写了以下代码:

printLine :: [Int] -> String
printLine widthList = concat $ concat $ foldr (:) [["+"]] boundList
where boundList = replicate 4 ( "+" : hyphenList )
      hyphenList = replicate max "-"
      max = maximum widthList

代码工作正常,但是,我觉得我过度复杂了这个功能。例如,我使用了#concat'两次功能。是否有清洁剂'怎么回事呢?

3 个答案:

答案 0 :(得分:3)

可能会出现部分并发症,因为该功能试图立即执行太多操作。您不太可能很快就会重复使用此功能。更重要的是,数字4甚至可能是字符' +'和' - '是"魔术数字"。

因此,第一步可能是将功能转变为更可重用的功能

-- | renamed from "printLine" because "print" has a connotation of I/O in Haskell
separator :: Int -> String 
separator = repeatedLine 4

repeatedLine :: Int -> Int -> String
repeatedLine reps segmentWidth = ...

即使您从不重复使用该功能,数字4现在也有一个名称(reps)来标识其用途。

关于核心功能。您的折叠既是reverse又是["+"]:。撤消是必要的,因为boundList会在开头附加'+'。这是一个很好的优化,但它使组合更复杂。无论你如何做,似乎你都有这些细节需要处理。但是还有另一种观点:从无穷远处开始。所以你已经有了

  where segment = '+':replicate segmentWidth '-'

现在你要重复一遍。最简单的方法是使用cycle segment,它会产生无限的段列表。现在你所要做的就是采用正确的前缀 - 这会将所有复杂性转移到几位数学中。

repeatedLine :: Int -> Int -> String
repeatedLine reps segmentWidth = take (1+reps*(segmentWidth+1)) $ cycle segment
  where segment = '+':replicate segmentWidth '-'

奖励:您现在拥有最少的迭代次数。

答案 1 :(得分:1)

@David Fletcher的评论带来了一个很好的解决方案:

printLine :: [Int] -> String
printLine xs = intercalate (replicate (maximum xs) '-') (replicate 5 "+")

答案 2 :(得分:0)

一些评论:

  • "-"中:hyphenList是一个字符串,因此'-'将是一个字符串列表。如果您使用hyphenList代替它会更简单。这会使concat成为一个字符串,这可以帮助你摆脱max之一。
  • 我会重命名变量max,因为Prelude中已定义了一个名为Data.List的函数。
  • 您可以使用intercalate中的intercalate功能来简化折叠和其他连接。
  • printLine :: [Int] -> String printLine widthList = "+" ++ intercalate "+" boundList ++ "+" where boundList = replicate 4 hyphenList hyphenList = replicate m '-' m = maximum widthList 不会在生成的字符串的开头和结尾添加分隔符,因此必须手动添加它们。

所以把它们放在一起你得到:

node server.js