制作部分应用的功能列表(优雅或惯用)

Joh*_*ler 7 haskell currying

我几乎可以通过我的Haskell问题绊倒,但我没有找到更好的解决方案来解决我的问题.

假设我有一个带有f5个参数的函数,我想创建一个部分应用的函数列表,它们应用了前3个参数,但在列表的每个元素中都有所不同.

例如,让我们说,f :: Num a => a -> a -> a -> b -> b -> c我想最终[b -> b -> c]得到结果的类型.其中一个功能可能是f 1 3 5,另一个可能是f 6 4 2.

有一个论点,我可以做一些像

map f [1..4] 
Run Code Online (Sandbox Code Playgroud)

得到f 1,f 2等等,我可以做2个args

map (uncurry f) $ zip [1..3] [6..8].
Run Code Online (Sandbox Code Playgroud)

现在我可以做3个args

map (uncurry $ uncurry f) $ zip (zip [1..3] [6..8]) [3..5]
Run Code Online (Sandbox Code Playgroud)

但是这种速度非常快.是否有更优雅(或惯用)的方式(除了使我自己的"uncurry3"功能配对zip3)?我总是遇到一个优雅的Haskell解决方案,这看起来非常笨拙.

对不起,如果这是一个新手问题或之前已经回答过.谢谢.

GS *_*ica 12

您可以使用以下命令缩短2参数代码zipWith:

zipWith f [1..3] [6..8]
Run Code Online (Sandbox Code Playgroud)

而且方便的是zipWith3,标准库中实际上有一个(以及最多7个).

也有平行的列表内涵-XParallelListComp这似乎去到任何数量:

[f a b c | a <- [1..3] | b <- [6..8] | c <- [3..5]]
Run Code Online (Sandbox Code Playgroud)


Jus*_* L. 9

这实际上是为列表定义Applicative实例的一种方法.

回想一下,Applicative的定义围绕以下定义(<*>):

(<*>) :: Applicative f => f (a -> b) -> f a -> f b
Run Code Online (Sandbox Code Playgroud)

如果你专注[],你可以得到:

(<*>) :: [a -> b] -> [a] -> [b]
Run Code Online (Sandbox Code Playgroud)

也许这开始看起来像你可以做到这一点的方式?您有一个函数列表,您可以将它们应用于值列表.也许我们可以通过(<*>)某种方式开展工作,以便将函数列表应用于值列表,如zip:

fs <*> xs = zipWith ($) fs xs
Run Code Online (Sandbox Code Playgroud)

回想一下($),函数应用程序运算符:

($) :: (a -> b) -> a -> b
f $ x = f x
Run Code Online (Sandbox Code Playgroud)

因此,zipWith"压缩"函数列表和值列表,并返回将每个函数应用于相应值的结果.

我想你应该可以从这里拿走它.让我们将两个列表加在一起:

(fmap (+) [1,2,3]) <*> [4,5,6]
Run Code Online (Sandbox Code Playgroud)

变成了

[(1+), (2+), (3+)] <*> [4,5,6]
Run Code Online (Sandbox Code Playgroud)

变成了

[1+4, 2+5, 3+6]
Run Code Online (Sandbox Code Playgroud)

[5, 7, 9]
Run Code Online (Sandbox Code Playgroud)

三个参数函数怎么样?

f x y z = x * y + z

((fmap f [1,2,3]) <*> [4,5,6]) <*> [7,8,9]
([(\y z -> 1*y+z), (\y z > 2*y+z), (\y z -> 3*y+z)] <*> [4,5,6]) <*> [7,8,9]
[(4+), (10+), (18+)] <*> [7,8,9]
[11, 18, 27]
Run Code Online (Sandbox Code Playgroud)

整齐!

不难看出你可以通过接受另一个来扩展它到任意性功能(<*>).

此外,我们可以为fmap正确的固定定义一个方便的别名并调用它(<$>),并定义(<*>)为具有不需要括号的正确固定,我们可以做类似的事情

f <$> [1,2,3] <*> [4,5,6] <*> [7,8,9]
Run Code Online (Sandbox Code Playgroud)

哪个很整洁,对吧?现在你可以基本上做一个zipWithN...... zipWith你想要的参数多了!

不幸的是,默认的Applicative实例[]没有这种行为; 它的行为方式与Monad实例一致.因此,为了解决这个问题,我们通常使用newtype包装器让我们为同一类型定义不同的实例.在标准库中Control.Applicative,newtype包装器是ZipList:

data ZipList a = ZipList { getZipList :: [a] }

instance Applicative ZipList where
    (ZipList fs) <*> (ZipList xs) = ZipList (zipWith ($) fs xs)
    pure x                        = -- left as exercise, it might surprise you :)
Run Code Online (Sandbox Code Playgroud)

所以我们可以在真正的Haskell中完成上述操作:

f <$> ZipList [1,2,3] <*> ZipList [4,5,6] <*> ZipList [7,8,9]
Run Code Online (Sandbox Code Playgroud)

不幸的是,这比原始版本稍微冗长一点 - 而且比...更冗长

zipWith3 f [1,2,3] [4,5,6] [7,8,9]
Run Code Online (Sandbox Code Playgroud)

但"优势"是你可以做任何基本上任意固定"提升":)

在这里带走的真实情况是,这是"Applicative"发明要解决的"完全一种模式"; 这是一个非常常见的模式/领域,Applicative特别茁壮成长,并且开始建立直觉以便能够发现可能适合于Applicative解决方案的问题的迹象可能是很好的.