【问题标题】:How to define an infinite 2D array recursively in Haskell?如何在 Haskell 中递归定义无限二维数组?
【发布时间】:2013-12-30 00:48:56
【问题描述】:

我是 Haskell 的新手,我喜欢它优美的语法。但是我还没有找到合适的方法来定义一个无限的二维数组——例如,帕斯卡三角:

1  1  1  1  1  ...
1  2  3  4  5  ...
1  3  6 10 15  ...
1  4 10 20 35  ...
1  5 15 35 70  ...
...

我知道如何定义一个简单的函数:

pascal :: Int -> Int -> Int
pascal 1 _ = 1
pascal _ 1 = 1
pascal x y = (pascal (x - 1) y) + (pascal x (y - 1))

由于 Haskell 不记住函数值,调用 pascal 20 20需要很长时间。如何定义快速版本(如无限二维数组)?

【问题讨论】:

  • 在这两种情况下,您都应该考虑将Int 更改为Integer 以避免最终的整数溢出。
  • 显然,实际上无法创建无限二维数组。在命令式语言中,您可以拥有一个数据结构(例如 Java 中的 java.util.ArrayList),它由一个按需增长的数组支持,但在 Haskell 中这将是有问题的,因为调整数组的大小会产生副作用。您将能够观察到数组大小的变化。
  • 如果你想记住你对pascal的调用,你可以使用hackage.haskell.org/package/data-memocombinators

标签: haskell


【解决方案1】:

您可以将帕斯卡三角形创建为无限、惰性、嵌套列表

pascal :: [[Integer]]
pascal = repeat 1 : map (scanl1 (+)) pascal

上面的定义有点简洁,但它本质上的意思是每一行都是前一行的累加和,从repeat 1开始,即无限列表。这样做的好处是我们可以直接计算三角形中的每个值,而无需进行任何 O(n) 索引。

现在您可以索引列表以找到您需要的值,例如

> pascal !! 19 !! 19
35345263800

该列表只会针对您需要的值进行部分评估。

您还可以轻松输出一系列值:

> putStrLn $ unlines $ take 5 $ map (unwords . map show . take 5) $ pascal
1 1 1 1 1
1 2 3 4 5
1 3 6 10 15
1 4 10 20 35
1 5 15 35 70

另一种选择是使用您的原始函数,但使用可用的各种记忆库之一来记忆它。例如,使用data-memocombinators:

import Data.MemoCombinators

pascal :: Integer -> Integer -> Integer
pascal = memo2 integral integral pascal'

pascal' :: Integer -> Integer -> Integer
pascal' 1 _ = 1
pascal' _ 1 = 1
pascal' x y = (pascal (x - 1) y) + (pascal x (y - 1))

【讨论】:

  • 很好的答案。只是补充一下,您可以使用 iterate 避免显式递归:pascal = iterate (scanl1 (+)) (repeat 1)
【解决方案2】:

无限二维“数组”的明显选择是嵌套列表,即无限列表的无限列表。因此可能是

pascal' :: [[Integer]]
pascal' = repeat 1 : [ 1 : [ pascalGen x y | y<-[1..] ] | x<-[1..] ]
 where pascalGen x y = pascal' !! (x-1) !! y + pascal' !! x !! (y - 1)

现在已经记住了函数调用。由于列表 O (n) 访问,它仍然不是最佳的,但也不是那么糟糕。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-09-10
    • 2016-09-11
    • 2014-02-17
    • 2011-08-16
    • 2016-07-13
    • 2013-01-01
    • 1970-01-01
    相关资源
    最近更新 更多