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

时间:2014-04-13 20:32:27

标签: haskell arrows

背景

我一直在经历John Hughes' Programming with Arrows,我觉得直到以下使用mapA的例子我才能直言不讳:

>runSF (mapA (delay 0)) [[1,2,3],[4,5,6],[7,8,9]]
[[0,0,0],[1,2,3],[4,5,6]]

其中runSF从定义为:

的StreamFunction箭头中提取流函数
newtype SF a b = SF {runSF :: [a]->[b]}

延迟定义为:

delay x = SF (init . (x:))

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

我的理解

mapA :: arr a b -> arr [a] [b]
delay :: SF a b

这样delay只是将第二个参数加上第一个参数。

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

mapA (delay 0) :: SF [[a]] [[b]]

我希望"电路"这将导致:

Diagram of Control Flow of mapA (delay 0)

数字标记过程的部分内容:

  1. 对于任何非空list xlistcase将发出Right(x, xs)。对于空列表,listcase将发出Left(),终端案例。
  2. 标记为Right的值将传递到下半部分。标记为Left的值将传递给const[],这基本上会停止迭代。
  3. 使用输入(x, xs)x将传递给(delay 0),而xs将传递回listcase
  4. 3的输出为(z, zs),传递给uncurry (:),将元组连接回列表。
  5. 这是我对流程的理解,输入[[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)上调用
      4. [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. [4,5,6],结果为[0,4,5][[7,8,9]]被传递回listcase
    • 第三次传递

      1. Right ([7,8,9], [])
      2. ([7,8,9], [])传递到下半部分
      3. (delay 0)上调用
      4. [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]]

1 个答案:

答案 0 :(得分:8)

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

runSF (mapA (delay 0)) [[1], [2]]

我们看到了第一步

  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]]
       ^
       |
    [[], []]
    
  4. 关键是要认识到使用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
    

    很抱歉,我不能把它画得更好。实际上,mapA给出了转置视图 输入列表,因此您可以将mapA (delay 0)视为操作 transpose . map (init . (0:)) . transpose,因为init . (0:)是。{1}} delay的定义。