简体   繁体   English

如何在Haskell中递归定义无限的二维数组?

[英]How to define an infinite 2D array recursively in Haskell?

I'm new to Haskell, and I like its graceful grammar. 我是Haskell的新手,我喜欢它优雅的语法。 But I haven't found a suitable way to define an infinite 2D array -- for example, the Pascal Triangle: 但我还没有找到一种合适的方法来定义无限的2D数组 - 例如,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  ...
...

I know how to define a simple function: 我知道如何定义一个简单的函数:

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

Since Haskell do not memorize function values, a call to pascal 20 20 will take a long time . 由于Haskell不记忆函数值,因此调用pascal 20 20 需要很长时间 How could I define a fast version (like an infinite 2D array)? 我怎么能定义一个快速版本(如无限的2D数组)?

You can create the pascal triangle as an infinite, lazy, nested list 您可以将pascal三角形创建为无限,惰性,嵌套列表

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

The above definition is a bit terse but what it essentially means is just that each row is an accumulating sum of the previous row, starting from repeat 1 ie an infinite list of ones. 上面的定义有点简洁,但它本质上意味着每一行都是前一行的累加和,从repeat 1开始,即无限的列表。 This has the advantage that we can calculate each value in the triangle directly without doing any O(n) indexing. 这样做的好处是我们可以直接计算三角形中的每个值而无需进行任何O(n)索引。

Now you can index the list to find the value you need, eg 现在,您可以索引列表以查找所需的值,例如

> pascal !! 19 !! 19
35345263800

The list will only get partially evaluated for the values you need. 该列表仅针对您需要的值进行部分评估。

You can also easily output a range of values: 您还可以轻松输出一系列值:

> 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

Another option is to use your original function but memoize it using one of the various memorization libraries available. 另一种选择是使用原始功能,但使用可用的各种记忆库之一进行记忆。 For example, using data-memocombinators : 例如,使用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))

The obvious choice for an infinite 2D "array" would be a nested list, ie an infinite list of infinite lists. 无限2D“数组”的明显选择是嵌套列表,即无限列表的无限列表。 It might thus be 因此可能是

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)

This now has the function calls memoised. 现在这个函数调用了memoised。 It's still not optimal because of list O ( n ) access, but not that bad either. 由于列表On )访问,它仍然不是最优的,但也不是那么糟糕。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM