【问题标题】:How can I define a filter function for arrows?如何为箭头定义过滤函数?
【发布时间】:2023-03-17 06:10:01
【问题描述】:

我目前正在阅读 John Hughes 的论文 Programming with Arrows 我已经被第 20 页第 2.5 节的第一个练习难住了。

我们拥有ArrowArrowChoice 类型类,以及函数实例、流函数[a] -> [b] 和一元函数a -> m b,通过Kleisli 类型。

给出了一个mapA 的例子:

mapA f = arr listcase >>>
         arr (const []) ||| (f *** mapA >>> arr (uncurry (:)))

这是一个尝试:

listcase :: [a] -> (Either () (a,[a]))
listcase [] = Left ()
listcase (x:xs) = Right (x,xs)

helper :: (Bool,a) -> [a] -> Either (a,[a]) [a]
helper (True,x)  y = Left (x,y)
helper (False,x) y = Right y

test :: Arrow a => (b -> Bool) -> a (b,c) ((Bool,b), c)
test p = first (arr p &&& arr id)

filterA :: Arrow a => (b -> Bool) -> a [b] [c]
filterA p = f >>> (g ||| (h >>> (j ||| (filterA p))))
   where f = arr listcase
         g = arr (const [])
         h = test p >>> (uncurry helper)
         j = (arr id *** (filterA p)) >>> (arr (uncurry (:)))

这种徒劳的尝试背后的(蛮力)理由如下: filterA 有两种选择:listcasemap 类似,以及应用谓词 p 的结果。它像map 一样开始,检查列表并使用listcase 返回一个Either 值。在空列表的情况下应用g,否则||| 右侧的所有内容都应用于(a,[a]) 类型的值,分别包含headtail。首先应用h 函数,它应用谓词同时保留head,返回((Bool, head),tail) 类型的值。这被传递给(uncurry helper),它根据Bool 的值决定是否保留head。它将结果作为Either 值返回,以便我们可以对其应用选择方法(|||)。该值被传递给下一个选择:(j ||| (filterA p)),这样如果谓词保持True,那么j 将应用于包含headtail 的对。 head 使用id 过滤,而filter p 应用于tail。两个结果都成对返回。然后使用arr (uncurry (:)) 协调这对,例如map。否则 tail 将单独传递给 filterA p

我怀疑它是否像我说的那么难,我想我错过了一些非常明显的东西。

【问题讨论】:

    标签: haskell arrows


    【解决方案1】:

    对不起,我没有完全按照你的逻辑,但让我们看看非箭头代码的作用。它

    • 检查列表是否为空,如果是则返回
    • 否则,拉掉列表的头部,调用元素x
    • 递归列表的其余部分,调用结果ys
    • 如果谓词p 在头部为真,那么我们将x 附加到ys
    • 否则,我们返回ys

    listcase 函数 [用于实现前 2 个任务] 看起来不错,但请记住您返回的是一个列表,所以不妨返回它而不是 unit 并通过 const [] 重新映射。

    你有第三个子弹的递归代码埋在最后两个案例中,而我直接公开它,但这没关系。

    对于最后的合并,您使用||| 编写它,但由于您不需要在目标类别中编写任何其他箭头,您不妨只提升一个函数来完成所有工作。在我下面的代码中,这是rejoin

    filterA :: forall arr a. ArrowChoice arr => arr a Bool -> arr [a] [a]
    filterA p = arr lstcase >>> (filterRest ||| id) where
        -- left if has a head, right otherwise
        lstcase :: [a] -> (Either (a, [a]) [a])
        lstcase (x:xs) = Left (x, xs)
        lstcase [] = Right []
    
        -- if we got a head, then
        --     for the head ("first" in code), map this to itself and p's result on it
        --     recurse on the rest of the list ("second" part)
        --     append the element to the recursion result if p's result is true
        filterRest :: arr (a, [a]) [a]
        filterRest = (first (id &&& p) >>> second (filterA p) >>> arr rejoin)
    
        rejoin :: ((a, Bool), [a]) -> [a]
        rejoin ((x, True), rest) = x:rest
        rejoin ((x, False), rest) = rest
    

    当然,用***&&&|||first等来清楚地表达你的想法需要时间。

    一点评论。

    • 确保您正在实现他们要求的功能!如果你只取一个原始函数p,那么你不妨声明filterA = arr filter。你真的很想拿起一支箭p。也就是说,更改将是简单地键入 p 而不是 arr p,因此您的代码有正确的想法。
    • (uncurry helper) 不是箭头空间中的东西,它只是一个原始函数。

    在开发这些东西时,我通常会编写一个骨架,并声明类型。这有助于我弄清楚发生了什么。例如,我从

    filterA :: ArrowChoice arr => arr a Bool -> arr [a] [a]
    filterA p = arr lstcase >>> (filterRest ||| id) where
        -- left if has a head, right otherwise
        lstcase :: [a] -> (Either (a, [a]) [a])
        lstcase = undefined
    
        filterRest :: arr (a, [a]) [a]
        filterRest = undefined
    

    但是,当您将fa 添加到filterRest 声明中时,您需要告诉它arr 用于filterRestfilterA 相同(类型变量范围),所以使用@ 987654349@同上。

    【讨论】:

    • 谢谢,看起来不错。我在发布后就注意到谓词是一个箭头,但由于它并没有太大的改变,正如你所指出的,我没有费心去改变它。不解除 (uncurry helper) 是一个错误。也许最大的错误是没有将filterA p 设为ArrowChoice 实例。尽管我更喜欢您的,但这个简单的更改使我的代码能够按预期工作。
    【解决方案2】:

    这里稍微简化一下:

    test :: Arrow a => (b -> Bool) -> a b ([b] -> [b])
    test p = arr $ \b -> if p b then (b:) else id
             
    filterA :: Arrow a => (b -> Bool) -> a [b] [b]
    filterA p = f >>> (g ||| h)
      where f = arr listcase
            g = arr id
            h = (test p *** filterA p) >>> (arr (uncurry ($)))
    
    filterA' :: Arrow a => a b Bool -> a [b] [b]
    filterA' p = f >>> (g ||| h)
      where f = arr listcase
            g = arr id
            h = (i *** filterA p) >>> (arr (uncurry ($)))
            i = proc x -> do 
                  b <- p -< x
                  returnA -< if b then (x:) else id
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-08-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多