我正在阅读John Hughes的用箭头编程。有一段我真的无法理解的代码。代码如下:
import Control.Arrow.Operations
import Control.Arrow
import Control.Category
import Prelude hiding ((.),id)
newtype SF a b = SF {runSF :: [a] -> [b]}
instance Category SF where
id = SF id
(.) (SF f) (SF g) = SF $ \x -> f (g x)
(.*.) :: (a -> b) -> (c -> d) -> (a,c) -> (b,d)
(.*.) f g (a,c) = (f a, g c)
instance Arrow SF where
arr f = SF (map f)
first (SF f) = SF (uncurry zip . (f .*. id) . unzip)
instance ArrowLoop SF where
loop (SF f) = SF $ \as -> let (bs,cs) = unzip (f (zip as (stream cs))) in bs
where stream ~(x:xs) = x:stream xs
instance ArrowChoice SF where
left (SF f) = SF (\xs -> combine xs (f [y | Left y <- xs]))
where combine (Left y: xs) (z:zs) = Left z : combine xs zs
combine (Right y :xs) zs = Right y : combine xs zs
combine [] zs = []
instance ArrowCircuit SF where
delay x = SF (x:)
然后
mapA :: ArrowChoice arr => arr a b -> arr [a] [b]
listcase [] = Left ()
listcase (x:xs) = Right (x,xs)
mapA f = arr listcase >>>
arr (const []) ||| (f *** mapA f >>> arr (uncurry (:)))
我无法理解的是
> runSF (mapA (delay 0)) [[1,2,3],[4,5],[6],[7,8],[9,10,11],[12,13,14,15]]
[[0,0,0],[1,2],[4],[6,5],[7,8,3],[9,10,11,0]]
我认为结果应该只是在每个列表的头部添加0
,因为delay 0
定义为SF (0:)
。
更奇怪的是,
diag :: (ArrowCircuit a , ArrowChoice a) => a [b] [b]
diag = arr listcase >>>
arr (const []) ||| (arr id *** (diag >>> delay []) >>> arr (uncurry (:)))
runSF diag [[1,2,3],[4,5,6],[7,8,9],[10,11,12]]
[[1],[4,2],[7,5,3],[10,8,6]]
我可以看到diag
和mapA (delay 0)
在做什么,但我无法理解使用delay
的计算过程。有人可以帮忙吗?感谢。
答案 0 :(得分:5)
考虑箭头的一种方法是某种工厂图。如果您的SF
只是(->)
,那么您就是对的。继续尝试;你应该得到:
Prelude Control.Arrow> mapA (0 :) [[1,2,3],[4,5],[6],[7,8],[9,10,11],[12,13,14,15]]
[[0,1,2,3],[0,4,5],[0,6],[0,7,8],[0,9,10,11],[0,12,13,14,15]]
然而,工厂的机器可以做的事情比简单地发送转换的输入更复杂,->
的工作方式。您的SF
计算机“接收”a
并“熄灭”b
,但它们由功能[a] -> [b]
支持,可让您向其提供一个a
然后他们可以做一些比->
更复杂的事情。
所以delay 0
机器需要一个数字流,并在其前面添加0,轻松便宜,如果您想以这样的方式考虑它,将原始流“滞后”一个“时间步”。
类似地,a1 &&& a2
机器必须将其输入流馈送到两个子机器,并且当它们都具有值时,它可以将它们“压缩”在一起。毕竟,它使用其子机[b] -> [c]
和[b] -> [d]
生成[b] -> [(c, d)]
。
a1 ||| a2
计算机比较棘手:它需要类似的子计算机[b] -> [d]
和[c] -> [d]
,并使用它们来形成[Either b c] -> [d]
。如果看起来完全不言自明,再看一遍!我们没有Either [b] [c]
,这本来是完全直截了当的(这是->
处理上面的内容),而是[Either b c]
。这里做的明显的解决方案是:
[d]
。什么命令?最简单的方法是返回原始列表,每当看到左侧时,从左侧机器列表中生成d
;无论何时看到右侧,都会从右侧列表中生成d
。
在所有这些背景下,我们来mapA
:
mapA f = arr listcase >>> arr (const []) ||| (f *** mapA f >>> arr (uncurry (:)))
对于SF
,listcase
将获取传入的列表流并生成Either () (x, [x])
的流,具体取决于该流是否为空。在第一次通过列表时,runSF
中的所有条目都不为空,因此每个列表都是Right
分支,发出一个正确的值。
因此我们将[Right (1, [2,3]), Right (4, [5]), Right (6, []), ...]
弄平并分成两个列表:[1, 4, 6, ...]
和[[2,3], [5], [], ...]
。
第一个列表被输入到延迟函数中,因此它以0
为前缀。第二个列表以递归方式馈入mapA f
,因此[2,5]前缀也会延迟;等等。
最后,当您将它们连接在一起时,您会发现列表中的每个嵌套级别都已延迟,因此第一个发出的列表为[0,0,0],第二个为[1,2]。