【发布时间】:2023-03-17 06:10:01
【问题描述】:
我目前正在阅读 John Hughes 的论文 Programming with Arrows 我已经被第 20 页第 2.5 节的第一个练习难住了。
我们拥有Arrow 和ArrowChoice 类型类,以及函数实例、流函数[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 有两种选择:listcase 与 map 类似,以及应用谓词 p 的结果。它像map 一样开始,检查列表并使用listcase 返回一个Either 值。在空列表的情况下应用g,否则||| 右侧的所有内容都应用于(a,[a]) 类型的值,分别包含head 和tail。首先应用h 函数,它应用谓词同时保留head,返回((Bool, head),tail) 类型的值。这被传递给(uncurry helper),它根据Bool 的值决定是否保留head。它将结果作为Either 值返回,以便我们可以对其应用选择方法(|||)。该值被传递给下一个选择:(j ||| (filterA p)),这样如果谓词保持True,那么j 将应用于包含head 和tail 的对。 head 使用id 过滤,而filter p 应用于tail。两个结果都成对返回。然后使用arr (uncurry (:)) 协调这对,例如map。否则 tail 将单独传递给 filterA p。
我怀疑它是否像我说的那么难,我想我错过了一些非常明显的东西。
【问题讨论】: