[英]How do I apply the first partial function that works in Haskell?
Suppose I have a list fns
of partial functions from a
to b
, which I represent as functions from a
to Maybe b
, and I have an object x
of type a
. 假设我有一个从a
到b
的部分函数的列表fns
,我将其表示为从a
到Maybe b
函数,并且我有一个类型为a
的对象x
。 Suppose now that I want to define another function from a
to Maybe b
which takes the value of the first fx
that is not Nothing
, if such an f
exists in fns
, or the value Nothing
if no such f
exists. 现在假设我想要定义另一个函数,从a
到Maybe b
,它取第一个fx
的值不是Nothing
,如果这样的f
存在于fns
,或者值Nothing
如果不存在这样的f
。 So basically it outputs fx
for the first f
that works, or Nothing
if no f
works. 所以基本上它输出fx
的第f
这样的作品,或者Nothing
,如果没有f
工作。
It isn't hard to come up with some code that does the job. 提出一些能够完成这项工作的代码并不难。 For example, one can create a list [fx| f <- fns]
例如,可以创建一个列表[fx| f <- fns]
[fx| f <- fns]
remove all the Nothing
s from it, and then take the head of the resulting list, or Nothing
if that list is empty. [fx| f <- fns]
从中删除所有Nothing
,然后取结果列表的头部,如果该列表为空,则为Nothing
。 But that feels clumsy, and this seems like the kind of general situation for which there is a much more stylish implementation using some built-in function in Haskell. 但这感觉很笨拙,这似乎是一种普遍的情况,在Haskell中使用一些内置函数可以实现更加时尚的实现。 If so, then I'd be interested to know what it is. 如果是这样,那么我有兴趣知道它是什么。
This is what Alternative
is for. 这就是Alternative
的用途。 It represents choice and here you are choosing between Maybe
values. 它代表选择,在这里你选择Maybe
值。 You can do something like 你可以做点什么
foldr (<|>) empty [f x | f <- fns]
In Data.Foldable
you also have asum :: (Foldable t, Alternative f) => t (fa) -> fa
which does what you want even more directly: 在Data.Foldable
你也有asum :: (Foldable t, Alternative f) => t (fa) -> fa
它可以更直接地做你想要的:
asum [f x | f <- fns]
As a side remark, I should note that MonadPlus
also does what you want for its Maybe
instance. 作为旁注,我应该注意到MonadPlus
也为它的Maybe
实例做了你想要的。 As in the above, you can have 如上所述,你可以拥有
foldr mplus mempty [f x | f <- fns]
And 和
msum [f x | f <- fns]
But IMO you should use Alternative
here since that more accurately conveys your meaning of "choice". 但IMO你应该在这里使用Alternative
,因为它更准确地传达了你的“选择”的含义。
In Data.Monoid
, a newtype
copy of Maybe
, called First
, has the "take the first Just
" behaviour. 在Data.Monoid
,一个newtype
的副本Maybe
,所谓的First
,有“拿第一Just
”行为。
If you were looking for a function of type 如果您正在寻找类型的功能
[a -> First b] -> a -> First b
with the behaviour you describe, it would simply be 根据你描述的行为,它就是这样
fold
from Data.Foldable
, because the monoid behaviour for a ->
does the pointwise lifting needed: the Monoid
for a -> First b
is exactly picking the first application outcome whic works. 来自Data.Foldable
,因为a ->
的幺半群行为需要逐点提升:a的Monoid
a -> First b
正好选择了第一个应用结果。 Sadly (for my tears over this have been many), to get Maybe
instead of First
takes a little more work. 可悲的是(因为我的眼泪已经很多了),为了得到Maybe
而不是First
需要多做一些工作。
Note that the pointwise lifting, yanking a ->
out through []
, is just the sort of job for sequenceA
, so 注意,逐点提升,yanking a ->
out through []
,就是sequenceA
的那种工作,所以
(asum .) . sequenceA
will do the job. 会做的。
It's good to get the monoid structure you need cued from the type: in this case, accessing the Alternative
behaviour with asum
will have to do. 从类型中获取需要提示的monoid结构是很好的:在这种情况下,使用asum
访问Alternative
行为必须这样做。
Warning: This is a really non-standard solution. 警告:这是一个非常非标准的解决方案。 But I personally really like it for its elegance - and the underlying mind-bending. 但我个人非常喜欢它的优雅 - 以及潜在的心灵弯曲。
On https://wiki.haskell.org/Pointfree you can find a function named swing
. 在https://wiki.haskell.org/Pointfree上,您可以找到名为swing
的函数。 Its implementation and type are confusing at first: 它的实现和类型最初令人困惑:
swing :: (((a -> b) -> b) -> c -> d) -> c -> a -> d
swing = flip . (. flip id)
You can get a first idea of what it does when you see its fully applied form: 当您看到完全应用的表单时,您可以首先了解它的作用:
swing f c a = f ($ a) c
In conjunction with other higher-order functions it does things that almost look like magic. 结合其他高阶函数,它可以完成看起来像魔术的事情。 Examples from the link: 链接示例:
swing map :: [a -> b] -> a -> [b]
swing any :: [a -> Bool] -> a -> Bool
swing foldr :: b -> a -> [a -> b -> b] -> b
swing zipWith :: [a -> b -> c] -> a -> [b] -> [c]
swing find :: [a -> Bool] -> a -> Maybe (a -> Bool)
swing partition :: [a -> Bool] -> a -> ([a -> Bool], [a -> Bool])
All these functions do exactly what you would assume from the types. 所有这些功能都完全按照您所假设的类型执行。 (But note that the wiki is a little out of date. By now, most of these functions automatically work for any Foldable
type.) (但请注意,wiki有点过时。到目前为止,大多数这些功能都可以自动适用于任何Foldable
类型。)
The function that you search could start from 您搜索的功能可以从中开始
swing mapMaybe :: [a -> Maybe b] -> a -> [b]
and then apply listToMaybe
. 然后应用listToMaybe
。 (Both functions are from Data.Maybe
) (这两个函数都来自Data.Maybe
)
A more general form would be 更一般的形式是
swing mapM :: (Monad m, Traversable t) => t (a -> m b) -> a -> m (t b)
so for example (using the ClassyPrelude
for full generality at the cost of some constraint noise, with headMay
as a more general form of listToMaybe
): 因此,例如(使用ClassyPrelude
完全一般性的一些约束噪声的成本,与headMay
作为一个更一般的形式listToMaybe
):
f :: (Traversable t, MonoFoldable (t b), Element (t b) ~ b)
=> t (a -> Maybe b) -> a -> Maybe b
f functions x = join $ headMay <$> swing mapM functions x
Yes, it might turn your head into mush - but it's like bending your head to see a smiley, only with your whole mind. 是的,它可能会让你的头变得糊涂 - 但这就像弯曲你的头看到一个笑脸,只有你的整个头脑。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.