如何解决如何正确解决Haskell中的帕斯卡三角形?
我正在Haskell中实现Pascal Triangle,但是代码无法正确运行。该代码又给了1行。另外,我试图像树一样打印结果,但是这对我来说既困难又困惑,所以我没有添加打印代码。
这些是我得到的结果:
*Main> pascal 1
[[1],[1,1]]
*Main> pascal 2
[[1],1],2,1]]
预期输出:
*Main> pascal 2
[[1],1]]
理想的输出:
*Main> pascal 3
1
1 1
1 2 1
这是代码:
choose n 0 = 1
choose 0 k = 0
choose n k = choose (n-1) (k-1)* n `div` k
pascal :: Integer -> [[Integer]]
pascal 0 = [[1]]
pascal m = pascal (m - 1) ++ [[choose m k | k <- [0,1..m]]]
解决方法
首先让我注意您的方法有点倒退。 Pascal三角形的全部要点在于,它提供了一种有效的方法来制表choose
函数而无需单独计算每个值。这并不意味着您的处理方式错误,但这肯定不是很好。请尝试在没有choose
函数的情况下解决问题!你知道,
1
╱ ╲
1 1
╱ ╲+╱ ╲
1 2 1
╱ ╲+╱ ╲+╱ ╲
1 3 3 1
╱ ╲+╱ ╲+╱ ╲+╱ ╲
1 4 6 4 1
... ... ...
提示:首先不要编写一个计算整个三角形或单个元素的函数,而要编写一个占用三角形行并给您下一行的函数。然后剩下要做的就是iterate
对该功能进行操作。
关于如何使用当前方法解决它–显然,如果您希望pascal 1
产生[[1]]
,那么pascal 0 = [[1]]
并不是一个非常明智的基本情况。而是以
pascal 1 = [[1]]
或
pascal 0 = []
(这会更好一点,因为该函数不会为零定义...但是仍然是负数–我们希望避免这种情况,或者至少在这种情况下给出明确的错误消息。)
然后,对于第m
行,您应该只计算choose (m-1) k
系列。易于修复。记住还要选择k
的正确范围。
关于如何以等腰形状漂亮地打印输出:编写一个辅助函数
centerAlign :: [String] -> [String]
在每行前面添加空白,相当于length
-length
中空白的一半。
然后,您只需在Pascal的三角形上做putStrLn . unlines . centerAlign . map show
。
以下是获取“理想输出”的代码:
printPascal :: [[Integer]] -> IO ()
printPascal pasc =
let
process row =
let
striped = (foldr1 (\ x y -> x ++ " " ++ y) . map show) row
spaces = replicate ((n - length striped) `div` 2) ' '
in spaces ++ striped ++ "\n"
n = length . (foldr1 (\ x y -> x ++ " " ++ y) . map show) . last $ pasc
in mapM_ (putStr . process) pasc
首先,这里是一些示例(实际上,后者看起来不太好-告诉我们,编辑以获得更好的版本)
GHCi> printPascal (pascal 1)
1
GHCi> printPascal (pascal 4)
1
1 1
1 2 1
1 3 3 1
GHCi> printPascal (pascal 8)
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
那么,这里发生的事情:
- 我们采用一个已经建立的三角形
pasc
。我们将最后一行转换为字符串,将所有元素放入字符串中,并用空格分隔。那就是我们最后一个三角形中每一行的长度。 - 我们采用每一行并用
process
处理。它做的很简单:将所有元素放在字符串中,并用空格分隔。如果其长度小于n
,则会添加缺少的空格(左右两边相等;实际上,右边的空格可能会被删除)。最后,我们换了一行。 - 我们在列表上映射
putStr . process
,然后对效果进行排序。mapM
(或更通用的版本-traverse
)用于此类目的。结果类型将为IO [()]
-我们不需要单位列表,因此我们将IO ()
替换为mapM
(或更通用的版本-使其成为mapM_
traverse_
。
您可能想结合使用pascal
和printPascal
:
buildAndPrintPascal :: Integer -> IO ()
buildAndPrintPascal = printPascal . pascal
编辑:为了使树对于更大的数字看起来更好看,我们可能需要更改步长(保持其为奇数)。这是代码的编辑版本,唯一的编辑是step
:
printPascal :: [[Integer]] -> IO ()
printPascal pasc =
let
process row =
let
striped = (foldr1 (\ x y -> x ++ step ++ y) . map show) row
spaces = replicate ((n - length striped) `div` 2) ' '
in spaces ++ striped ++ "\n"
n = length . (foldr1 (\ x y -> x ++ step ++ y) . map show) . last $ pasc
step =
let
times = (floor . logBase 10 . fromInteger . maximum . last $ pasc) * 2 + 1
in replicate times ' '
in mapM_ (putStr . process) pasc
以下是一些示例:
GHCi> buildAndPrintPascal 4
1
1 1
1 2 1
1 3 3 1
GHCi> buildAndPrintPascal 8
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
GHCi> buildAndPrintPascal 10
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
1 8 28 56 70 56 28 8 1
1 9 36 84 126 126 84 36 9 1
我认为它们看起来要好一些。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。