6

这是一些代码,以“直接样式”确定列表是否为 n+1 比较中的回文

pal_d1 :: Eq a => [a] -> Bool
pal_d1 l = let (r,_) = walk l l in r
        where walk l           [] = (True,l) 
              walk l       (_:[]) = (True,tail l)
              walk (x:l) (_:_:xs) = let (r, y:ys) = walk l xs
                                    in (r && x == y, ys)      

可以在几个例子上进行测试

-- >>> pal_d1 [1,2,1]
-- True

-- >>> pal_d1 [1,2,2,1]
-- True

-- >>> pal_d1 [1,2,3,4,2,1]
-- False

Danvy 在“ There and back again ”中声称没有控制操作符(在 4.2 之前)没有直接的风格解决方案,因为下面 CPS 风格解决方案中的延续的非线性使用:

pal_cps1 :: Eq a => [a] -> Bool
pal_cps1 l = walk l l (\_ -> trace "called" True) 
    where 
          walk  l          []  k = k l
          walk  l       (_:[]) k = k (tail l)
          walk (x:xs) (_:_:ys) k = walk xs ys (\(r:rs) ->  x == r && k rs)          

第一个代码如何与此断言不矛盾?

(以及如何不线性使用延续?)

4

2 回答 2

4

他没有声称没有控制操作员就没有解决方案。

延续不是线性使用的,因此将此程序映射回直接样式需要控制运算符。

这篇论文的背景是研究直接风格和 CPS 之间的系统转换,而那段的主张是,如果以花哨的方式使用延续,从 CPS 回归是很棘手的。

通过一些努力,您可以将其重新调整为一个不错的形状,但问题仍然存在,编译器如何自动执行此操作?

(以及如何不线性使用延续?)

在本文中,延续在andalso( &&) 的右侧,因此如果左侧操作数是 ,则将其丢弃False

在操作语义中,您可以将延续视为评估上下文,并且在该视图中丢弃延续对应于引发异常。当然可以,但关键是这需要源语言中的额外机制。

于 2021-03-12T16:01:10.857 回答
2

CPS 代码(在问题的原始版本中——因为由 OP 编辑​​)似乎有问题。看起来应该是

      walk (x:xs) (_:_:ys) k  =  walk xs ys (\(z:zs) -> x == z && k zs)

非 CPS 代码从中间开始比较,并n `div` 2针对长度列表进行比较n。即使发现不匹配,它也会继续测试,因此是"linear"

在这种情况下,CPS 代码会立即退出,因为(False && undefined) == False成立;“非线性”也是如此。两者相等,因此第一个没有说明第二个。

正如另一个答案所说,不调用延续相当于在没有延续的代码中抛出异常,该论文的作者显然称之为“直接[即非CPS(?)--wn]风格”。

(我没有读过论文)。

顺便说一句,以“直接”风格对早期退出的解决方案进行编码并不难。我们将只使用相同的海龟和兔子技巧来发现一半,同时反向构建前半部分,然后and $ zipWith (==) first_half_reversed second_half在像 Scheme 这样的严格语言中调用 Haskell 或其等效的短路直接递归变体。

于 2021-03-12T16:55:32.767 回答