我正在阅读列表操作的一些技巧,其中包含以下内容:
zipRev xs ys = foldr f id xs snd (ys,[]) where f x k c = k (\((y:ys),r) -> c (ys,(x,y):r))
我们在这里可以看到的是,我们有两个彼此叠放的延续。发生这种情况时,他们通常可以“取消”,如下所示:
zipRev xs ys = snd (foldr f (ys,[]) xs) where f x (y:ys,r) = (ys,(x,y):r)
我不明白您如何“取消”堆叠的延续以从顶部的代码块到达底部的代码块。您寻找进行此转换的方式是什么,为什么行得通?
函数f :: a -> b
可以“伪装”双延续作为一个函数内f' :: ((a -> r1) -> r2) -> ((b -> r1) -> r2)
。
obfuscate :: (a -> b) -> ((a -> r1) -> r2) -> (b -> r1) -> r2
obfuscate f k2 k1 = k2 (k1 . f)
obfuscate
具有很好的属性,可以保留函数的组成和身份:您可以通过几个步骤证明这obfuscate f . obfuscate g === obfuscate (f . g)
一点obfuscate id === id
。这意味着您可以经常使用此变换来解开组成obfuscate
d函数的双连续计算,方法obfuscate
是将合成中的因子分解出来。这个问题就是这样一个例子。
的f
在顶部的代码块是obfuscate
的d版本f
在底块(更确切地说,顶部f x
是obfuscate
底部的d版本f x
)。您可以通过注意到top如何f
将外部延续应用到转换其输入的函数,然后将整个事物应用到内部延续来看到这一点,就像在主体中一样obfuscate
。
这样我们就可以解开纠结zipRev
:
zipRev xs ys = foldr f id xs snd (ys,[])
where
f x = obfuscate (\(y:ys,r) -> (ys,(x,y):r))
由于foldr
此处的作用是obfuscate
彼此构成一堆d函数(并将其全部应用到id
,我们可以在右边保留),因此我们可以将d函数分解为obfuscate
整个折叠的外部:
zipRev xs ys = obfuscate (\accum -> foldr f accum xs) id snd (ys,[])
where
f x (y:ys,r) = (ys,(x,y):r)
现在应用的定义obfuscate
并简化:
zipRev xs ys = obfuscate (\accum -> foldr f accum xs) id snd (ys,[])
zipRev xs ys = id (snd . (\accum -> foldr f accum xs)) (ys,[])
zipRev xs ys = snd (foldr f (ys,[]) xs)
QED!
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句