我是Haskell的新手,并且对于如何以最惯用和最清晰的方式最好地表达某些操作感到困惑。目前(还会有更多)我对<*>
感到困惑(我甚至不确定该怎么称呼。)
例如,如果我有,请说
f = (^2)
g = (+10)
作为代表性功能(实际上它们更复杂,但关键是它们是不同的和不同的),然后
concatMap ($ [1,2,3,4,10]) [(f <$>), (g <$>) . tail . reverse]
和
concat $ [(f <$>), (g <$>) . tail . reverse] <*> [[1,2,3,4,10]]
完成同样的事情。
这些更为惯用的Haskell之一,是否暗示Haskell的经验丰富的读者,而另一个则不然。也许有更多(更好)的方式来表达完全相同的东西。这两种方法之间是否存在概念差异,像我这样的新手Haskeller可能会丢失?
答案 0 :(得分:10)
您的函数(f <$>)
和(g <$>).tail.reverse
都返回一个monoid类型(本例中为list),因此您可以使用mconcat
将它们转换为单个函数。然后,您可以将此函数直接应用于输入列表,而不是将其包装在另一个列表中并使用concatMap
:
mconcat [(f <$>), (g <$>).tail.reverse] $ [1,2,3,4,10]
要展开此功能,如果a -> b
是幺半群,则函数Monoid
是b
的实例。此类函数的mappend
implementation为:
mappend f g x = f x `mappend` g x
或等效
mappend f g = \x -> (f x) `mappend` (g x)
所以给定两个函数f
和g
返回一个monoid类型b
,f
mappend g
返回一个函数,该函数将其参数应用于{{ 1}}和f
并使用g
的{{1}}实例合并结果。
Monoid
的类型为b
,并使用mconcat
组合输入列表的所有元素。
列表是Monoid a => [a] -> a
== mappend
所以
mappend
返回类似
的函数(++)
答案 1 :(得分:9)
就个人而言,我会写
f = (^2)
g = (+10)
let xs = [1,2,3,4,10]
in (map f xs) ++ (map g . tail $ reverse xs)
在非常适用的情绪&#34;中,我会在in
之后替换
((++) <$> map f <*> map g . tail . reverse) xs
在这种情况下,我实际上不认为更具可读性。如果您不直接了解其含义,请花点时间了解Applicative
((->) a)
Reader
的实例。
我认为这个选择实际上取决于你尝试做什么,即你的输出意味着什么。在您的示例中,任务非常抽象(基本上只是展示Applicative
可以执行的操作),因此使用哪个版本并不明显。
Applicative
[]
实例直观地与组合相关,所以我会在这样的情况下使用它:
-- I want all pair combinations of 1 to 5
(,) <$> [1..5] <*> [1..5]
如果您有许多函数,并且您希望尝试使用多个参数来处理这些函数的所有组合,我确实会使用[]
Applicative
的{{1}}实例。但是,如果你所追求的是不同变换的串联,我会这样写(我在上面做过)。
作为中等经验的Haskeller,我只需2美分。
答案 2 :(得分:1)
我有时会遇到类似的问题。你有单个元素,但有多个功能。
通常我们有多个元素和单个函数:所以我们这样做:
map f xs
但这不是Haskell中的问题。双重就是这么简单:
map ($ x) fs
事实上,您的x
实际上是一个列表,并且您希望在concat
之后map
,所以您可以
concatMap ($ xs) fs
我无法真正理解直接在第二个等式中发生的事情,即使我可以推断它与使用适用法律的第一个方法相同。