【问题标题】:Lifting function `a → b → c` to `[a] → [b] → [[c]]`提升函数`a→b→c`到`[a]→[b]→[[c]]`
【发布时间】:2015-11-24 11:31:52
【问题描述】:

我想要一个函数

foo :: (a → b → c) → [a] → [b] → [[c]]

接受一个函数 f :: a → b → c 和两个列表 xsys 并返回一个网格(即列表列表),其中包含应用于 xs 和 @ 的每个值组合的 f 的值987654327@.

示例:foo [1..3] [4..6] 应该返回

[[f 1 4,f 1 5,f 1 6],
 [f 2 4,f 2 5,f 2 6],
 [f 3 4,f 3 5,f 3 6]]

我目前的做法是

foo = traverse . flip . traverse . flip

这可行,但我想知道是否有其他方法或预定义的组合器可以更好地完成(或者甚至可以组合,以便它可以轻松扩展到三元或 n 元函数)

例如:如果我不想要一个结果网格而只是一个结果列表,我可以写f <$> xs <*> ys,它很简洁,使用预定义的组合子,并且显而易见地推广到 n 元函数方式。是否有类似简洁的方式来编写我的组合器?

【问题讨论】:

    标签: haskell nested-lists applicative traversable


    【解决方案1】:

    这是列表推导的工作!

    foo f xs ys = [ [ f x y | y <- ys ] | x <- xs]
    

    测试用例:

    foo (\x y -> show x ++ " " ++ show y) [1..3] [4..6]
    

    产量:

    [["1 4","1 5","1 6"],["2 4","2 5","2 6"],["3 4","3 5","3 6"]
    

    【讨论】:

    • 当然可以,但它比我想象的要冗长得多。正如我所说,我希望有一些类似于f &lt;$&gt; xs &lt;*&gt; ys 的简单形状。此外,这个解决方案不能推广到任意可遍历对象,不是吗?
    • IMO 完美的可读性是对泛化/在这种情况下无意义的巨大的加分
    • 它泛化为任意 monad。
    【解决方案2】:

    此外,这个解决方案不能推广到任意可遍历对象,不是吗?

    确实(甚至更多):两个列表推导都可以替换为fmap,产生

    foo :: (Functor f, Functor g) => (a -> b -> c) -> f a -> g b -> f (g c)
    foo f xs ys = fmap (\x -> fmap (\y -> f x y) ys) xs
    

    现在,进行一些简化:

    \y -> f x y === f x
    fmap (f x) ys === flip fmap ys (f x) === flip fmap ys . f $ x
    \x -> flip fmap ys . f $ x === flip fmap ys . f
    

    所以

    foo f xs ys = fmap (flip fmap ys . f) xs
    

    【讨论】:

    • 任何Traversable 都是Functor。所以这个解决方案可以推广到任意的Traversable
    • 我无法进行类型检查。 ghci 分配这种类型:foo :: (Functor f, Functor f1) =&gt; (a -&gt; a1 -&gt; b) -&gt; f a -&gt; f (f1 a1 -&gt; f1 b)。任何分配所需类型的尝试都会导致错误部分如下Expected type: f a -&gt; g b -&gt; f (g c) Actual type: f a -&gt; f (g b -&gt; g c)
    • @MichaelWelch 哎呀,对不起。不得不完全重写它。
    • @svenningsson 是的,我的意思是它更通用(除了我弄错了,必须修复)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-01
    • 2015-06-12
    • 2020-12-25
    • 1970-01-01
    • 1970-01-01
    • 2017-04-19
    相关资源
    最近更新 更多