【发布时间】:2019-06-03 10:12:16
【问题描述】:
上下文
前几天我询问了patching a recursively-defined list。我现在正试图通过对 2D 列表(列表列表)进行操作来提升它的水平。
我将以帕斯卡三角形为例,例如this beautiful one:
pascals = repeat 1 : map (scanl1 (+)) pascals
[1,1,1,1,1,1...
[1,2,3,4,5...
[1,3,6,10...
[1,4,10...
[1,5...
[1...
问题
我想这样表达:
我将使用我自己的第一行和第一列(上面的示例假设第一行是
repeat 1,这是可以修复的,第一列是repeat (head (head pascals)),这将更加棘手)每个元素都是上面一个和左边一个的函数。
作为一个整体,它本身就是一个函数,足以在定义中插入一个补丁函数并让它传播补丁。
所以从外面看,我想找到一个f 函数,这样我就可以这样定义pascal:
pascal p = p (f pascal)
...所以pascal id 与示例中的相同,pascal (patch (1,3) to 16) 产生如下内容:
[1,1,1,1, 1,1...
[1,2,3,16,17...
[1,3,6,22...
[1,4,10...
[1,5...
[1...
我在哪里
让我们首先定义并提取第一行和第一列,这样我们就可以让它们可用,而不会试图滥用它们的内容。
element0 = 1
row0 = element0 : repeat 1
col0 = element0 : repeat 1
更新定义以使用row0 非常简单:
pascals = row0 : map (scanl1 (+)) pascals
但第一列仍然是element0。更新以从col0 获取它们:
pascals = row0 : zipWith newRow (tail col0) pascals
where
newRow leftMost prevRow = scanl (+) leftMost (tail prevRow)
现在我们可以满足第一个要求(自定义第一行和第一列)。不打补丁,第二个还是不错的。
我们甚至得到了第三部分的一部分:如果我们修补一个元素,它将向下传播,因为newRow 是根据prevRow 定义的。但它不会向右传播,因为(+) 在scanl 的内部累加器上运行,并且来自leftMost,这在此上下文中是显式的。
我尝试过的
从那里开始,似乎正确的做法是真正分离关注点。我们希望我们的初始化器 row0 和 col0 在定义中尽可能明确,并找到一种方法来独立定义矩阵的其余部分。存根:
pascals = row0 : zipWith (:) (tail col0) remainder
[1,1,1,1,1,1,1,1,1,1...
[1,/-------------------
[1,|
[1,|
[1,|
[1,| remainder
[1,|
[1,|
[1,|
[1,|
然后我们希望直接根据整体定义余数。自然的定义是:
remainder = zipWith genRow pascals (tail pascals)
where genRow prev cur = zipWith (+) (tail prev) cur
[1,1,1,1,1,1,1,1,1,1...
<<loop>>
第一行结果很好。为什么是循环?评估后会有所帮助:pascals 被定义为缺点,他的车很好(并且打印了)。什么是cdr?这是zipWith (:) (tail col0) remainder。该表达式是[] 还是(:)?它是其参数tail col0 和remainder 中最短的一个。 col0 是无限的,它与 remainder 一样为空,即 zipWith genRow pascals (tail pascals)。是[] 还是(:)?好吧,pascals 已经被评估为(:),但是(tail pascals) 还没有找到 WHNF。我们已经在尝试了,所以<<loop>>。
(很抱歉用文字拼出来,但我真的不得不像这样在脑海中追踪它才能理解它)。
出路?
根据我的定义,似乎所有定义都是正确的,数据流明智的。循环现在看起来很简单,因为评估器无法确定生成的结构是否是有限的。我找不到办法让它成为“它是无限的”的承诺。
我觉得我需要一些惰性匹配的逆向:一些惰性返回,我可以告诉评估者这个的 WHNF 是 (:),但你仍然需要稍后调用这个 thunk 来找出里面有什么它。
它仍然感觉像是一个固定点,但我还没有设法以一种有效的方式表达。
【问题讨论】:
-
所以,和上次一样,我的问题更复杂(虽然没有上次那么多),但这是我目前最少的障碍点。我什至没有阻止编写补丁功能,我什至不能编写生成器。我的初始化器既不是 1 也不是可比的;我的操作不是
(+)。 -
"评估者无法确定生成的结构是否是有限的[...]" 评估者不关心;它只会评估您告诉它的内容。如果有一个循环,你已经在某个地方自我引用了。
-
如果您更多地使用多维无限递归结构,您可能希望从列表转向trie,以获得更好的性能和更少的尴尬。
-
我已编辑我的答案以回答您的主要问题。 (以防您错过通知。)
-
@AJFarmar:这既无用也无建设性。我知道为什么会有一个循环。我在您引用的内容上方详细说明了一段。我知道评估员不在乎。或者有什么别的感觉,FTM。我觉得整个前面的部分都应该清楚地表明我对评估的工作方式有一个模糊的概念;因此,如果这里不够明显,则将其拼写出来:该句子既不是没有上下文的答案,也不是没有上下文。