我是Haskell的新手,我喜欢它优雅的语法。但我还没有找到一种合适的方法来定义无限的2D数组 - 例如,Pascal Triangle:
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
的调用将花费很长时间。我怎么能定义一个快速版本(如无限的2D数组)?
答案 0 :(得分:9)
您可以将pascal三角形创建为无限,惰性,嵌套列表
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))
答案 1 :(得分:3)
无限2D“数组”的明显选择是嵌套列表,即无限列表的无限列表。因此可能是
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)
现在这个函数调用了memoised。由于列表 O ( n )访问,它仍然不是最佳的,但也不是那么糟糕。