Haskell 应用语言?
Haskell Applicative idiom?
我是 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 的 reader 经验,而另一个则没有。也许还有其他(更好)的方式来表达完全相同的事物。像我这样的新手 Haskell 可能会遗漏这两种方法之间的概念差异吗?
就你个人而言,我会写
f = (^2)
g = (+10)
let xs = [1,2,3,4,10]
in (map f xs) ++ (map g . tail $ reverse xs)
在一个非常有应用性的"mood"中,我会将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
的 []
实例。但是如果你想要的是不同转换的串联,我会这样写(我在上面做了)。
我作为中等经验的 Haskeller 的 2 美分。
我有时会遇到类似的问题。你有一个元素,但有多个功能。
通常我们有多个元素和单一功能:所以我们这样做:
map f xs
但这不是 Haskell 中的问题。对偶很简单:
map ($ x) fs
事实上,你的 x
实际上是一个列表,你想 concat
在 map
之后,所以你
concatMap ($ xs) fs
我无法真正直接理解第二个方程式中发生了什么,即使我可以使用适用定律推断它与第一个方程式相同。
您的函数 (f <$>)
和 (g <$>).tail.reverse
return 都是幺半群类型(在本例中为列表),因此您可以使用 mconcat
将它们转换为单个函数。然后您可以将此函数直接应用于输入列表,而不是将其包装在另一个列表中并使用 concatMap
:
mconcat [(f <$>), (g <$>).tail.reverse] $ [1,2,3,4,10]
为了扩展这一点,函数 a -> b
是 Monoid
的一个实例,如果 b
是一个幺半群。此类函数的 implementation of mappend
是:
mappend f g x = f x `mappend` g x
或等同于
mappend f g = \x -> (f x) `mappend` (g x)
所以给定两个函数 f
和 g
其中 return 一个幺半群类型 b
, f
mappendg
return 是一个函数,它将其参数应用于 f
和 g
,并使用 b
的 Monoid
实例组合结果。
mconcat
具有类型 Monoid a => [a] -> a
并使用 mappend
.
组合输入列表的所有元素
列表是幺半群,其中 mappend
== (++)
所以
mconcat [(f <$>), (g <$>).tail.reverse]
return是一个类似于
的函数
\x -> (fmap f x) ++ (((fmap g) . tail . reverse) x)
我是 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 的 reader 经验,而另一个则没有。也许还有其他(更好)的方式来表达完全相同的事物。像我这样的新手 Haskell 可能会遗漏这两种方法之间的概念差异吗?
就你个人而言,我会写
f = (^2)
g = (+10)
let xs = [1,2,3,4,10]
in (map f xs) ++ (map g . tail $ reverse xs)
在一个非常有应用性的"mood"中,我会将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
的 []
实例。但是如果你想要的是不同转换的串联,我会这样写(我在上面做了)。
我作为中等经验的 Haskeller 的 2 美分。
我有时会遇到类似的问题。你有一个元素,但有多个功能。
通常我们有多个元素和单一功能:所以我们这样做:
map f xs
但这不是 Haskell 中的问题。对偶很简单:
map ($ x) fs
事实上,你的 x
实际上是一个列表,你想 concat
在 map
之后,所以你
concatMap ($ xs) fs
我无法真正直接理解第二个方程式中发生了什么,即使我可以使用适用定律推断它与第一个方程式相同。
您的函数 (f <$>)
和 (g <$>).tail.reverse
return 都是幺半群类型(在本例中为列表),因此您可以使用 mconcat
将它们转换为单个函数。然后您可以将此函数直接应用于输入列表,而不是将其包装在另一个列表中并使用 concatMap
:
mconcat [(f <$>), (g <$>).tail.reverse] $ [1,2,3,4,10]
为了扩展这一点,函数 a -> b
是 Monoid
的一个实例,如果 b
是一个幺半群。此类函数的 implementation of mappend
是:
mappend f g x = f x `mappend` g x
或等同于
mappend f g = \x -> (f x) `mappend` (g x)
所以给定两个函数 f
和 g
其中 return 一个幺半群类型 b
, f
mappendg
return 是一个函数,它将其参数应用于 f
和 g
,并使用 b
的 Monoid
实例组合结果。
mconcat
具有类型 Monoid a => [a] -> a
并使用 mappend
.
列表是幺半群,其中 mappend
== (++)
所以
mconcat [(f <$>), (g <$>).tail.reverse]
return是一个类似于
的函数\x -> (fmap f x) ++ (((fmap g) . tail . reverse) x)