mapA如何在Haskell中使用Stream Function Arrow?

aft*_*mmy 10 haskell arrows

背景

我一直在阅读约翰·休斯的" 箭头编程",我觉得直到下面的使用mapA的例子我才能直截了当:

>runSF (mapA (delay 0)) [[1,2,3],[4,5,6],[7,8,9]]
[[0,0,0],[1,2,3],[4,5,6]]
Run Code Online (Sandbox Code Playgroud)

其中runSF从StreamFunction箭头中提取流函数,定义为:

newtype SF a b = SF {runSF :: [a]->[b]}
Run Code Online (Sandbox Code Playgroud)

延迟定义为:

delay x = SF (init . (x:))
Run Code Online (Sandbox Code Playgroud)

SF是ArrowChoice的一个实例(声明mapA),因此是Arrow的一个实例.

我的理解

mapA :: arr a b -> arr [a] [b]
delay :: SF a b
Run Code Online (Sandbox Code Playgroud)

这样delay只需要用它的第一个参数预先设定它的第二个参数.

因此,mapA (delay 0)应该返回一个获取[[a]]并返回的SF箭头[[b]]

mapA (delay 0) :: SF [[a]] [[b]]
Run Code Online (Sandbox Code Playgroud)

我希望这会产生的"电路"是:

mapA的控制流程图(延迟0)

数字标记过程的一部分:

  1. 对于任何非空list x,listcase都会发出Right(x, xs).对于空列表,listcase将发出Left(),终端案例.
  2. 标记的值Right将传递到下半部分.标记的值Left将传递给const[],这实质上停止了迭代.
  3. 随着输入(x, xs),x将传递给(delay 0),同时xs将传递回去listcase.
  4. 3的输出将被(z, zs)传递给uncurry (:),它将元组连接回列表.

这是我对流程的理解,输入[[1,2,3],[4,5,6],[7,8,9]]:

  • 第一关

    1. Right ([1,2,3],[[4,5,6],[7,8,9]])
    2. ([1,2,3], [[4,5,6],[7,8,9]]) 传递到下半部分
    3. (delay 0)被召唤[1,2,3],导致[0,1,2].[[4,5,6],[7,8,9]]被传回去listcase
  • 第二关

    1. Right ([4,5,6], [[7,8,9]])
    2. ([4,5,6], [[7,8,9]]) 传递到下半部分
    3. (delay 0)被召唤[4,5,6],导致[0,4,5].[[7,8,9]]被传回去listcase
  • 第三关

    1. Right ([7,8,9], [])
    2. ([7,8,9], []) 传递到下半部分
    3. (delay 0)被召唤[7,8,9],导致[0,7,8].[]被传回去listcase.
  • 第四关

    1. Left (),掉在地上.

在这一点上,我们得到第4部分,它取3的输出并将它们汇总在一起.我们基本上构建了一个操作:

[0,1,2] : [[0,4,5] : [[0,7,8] : []]]

哪会给我们[[0,1,2],[0,4,5],[0,7,8]].

我的困惑

显然,我的上述流程是错误的.

如何调用runSF (mapA (delay 0)) [[1,2,3],[4,5,6],[7,8,9]]结果[[0,0,0],[1,2,3],[4,5,6]]

sab*_*uma 8

我发现这些例子很难理解.在此示例中有两个列表,外部列表是箭头操作的流,而内部列表是mapA映射的内容.考虑一个更简单的例子,所以我们现在可以忽略递归的情况.特定

runSF (mapA (delay 0)) [[1], [2]]
Run Code Online (Sandbox Code Playgroud)

我们看到了第一步

  1. 通过listcase箭头管道输入为我们提供输出 [Right (1, []), Right (2, [])].每对中的第一个元素被馈送到delay 0箭头,而第二个元素被反馈到mapA f.
  2. 所以,我们有这个[1, 2] => delay 0[[], []] => mapA f.饲养[1,2]delay 0给出结果[0, 1],并喂空名单mapA f产生了更多的空列表 [[], []].
  3. 这两个结果被馈送到arr (uncurry (:)),其行为类似zipWith (:),因为这些函数都映射到列表上,因此它以元素方式连接两个输入列表.

    [0,  1]
       |
       v
    arr (uncurry (:)) => [ 0:[], 1:[] ] == [[0], [1]]
       ^
       |
    [[], []]
    
    Run Code Online (Sandbox Code Playgroud)

关键是要认识到构造的所有东西都在arr内部列表集上运行,因此运行初始输入arr listcase 不会产生Right ([1,2,3],[[4,5,6],[7,8,9]]),但是 [Right (1, [2, 3]), Right (4, [5,6]), Right (7, [8,9])].这是我尝试在图表中绘制它.

     [Right (1, [2, 3]), Right (4, [5,6]), Right (7, [8,9])]
     =======================================================
                 |        [1, 4, 7]      +-----------+ [0, 1, 4]
  +----------+   |       +----=--------->|   delay   |-----=------|
  | listcase |---=------>|               +-----------+            |   +-------------------+
  +----------+           |                                        +-->| arr (uncurry (:)) |---> [[0,0,0],[1,2,3],[4,5,6]]
                         |                                        |   +-------------------+
                         |               +-----------+            |
                         +-------=------>|   mapA f  |------=-----|
                                 |       +-----------+      |
                                 |                          |
                          [[2,3],[4,5],[6,7]]         [[0,0], [2,3],[4,5]]
                                                      * what will be
                                                        returned if you
                                                        trace it through
Run Code Online (Sandbox Code Playgroud)

对不起,我不能画得更好.实际上,mapA给出了输入列表的转置视图,因此您可以将其mapA (delay 0)视为操作 transpose . map (init . (0:)) . transpose,因为init . (0:)是定义delay.