是否可以实现此功能?

时间:2012-10-05 14:07:10

标签: list function haskell

我想要一个类型为<{p}>的函数q

q :: ([b] -> b) -> ([(a, b)] -> (a, b))

它接受一个从列表中选择单个元素的函数,并将该函数提升到从一对列表中选择单个对的上下文中(完全忽略对的第一个元素)。

甚至可以编写这样的功能吗?我未能在此方面取得任何进展。

*是'解除'正确的词?


使用示例:如果我有一个功能:

safeMaximum :: b -> [b] -> b

> safeMaximum 18 [] 
18
> safeMaximum 18 [4,5,1,4,3]
5

然后,我想使用safeMaximum从对列表中获取第二个元素最大的对:

liftedSafeMaximum :: (a, b) -> [(a, b)] -> (a, b)
liftedSafeMaximum val = q val safeMaximum

liftedSafeMaximum ("?", 3) []
> ("?", 3)
liftedSafeMaximum ("?", 3) [("xyz", 1), ("123", 3), ("hi", 2)]
> ("123", 3)

5 个答案:

答案 0 :(得分:4)

如果您愿意略微优化选择器函数的定义,那么可以使用此类函数。我们将不是直接从列表中选择元素,而是使用多态函数,给定一个允许它查看它感兴趣的每个元素部分的投影,将从列表中选择一个项目。

所有q必须执行此操作,并将其snd作为要使用的投影。

{-# LANGUAGE Rank2Types #-}

import Data.Ord (comparing)
import Data.List (maximumBy)

q :: (forall c. (c -> b) -> c -> [c] -> c) -> (a, b) -> [(a, b)] -> (a, b)
q select = select snd

safeMaximumOn :: Ord b => (a -> b) -> a -> [a] -> a
safeMaximumOn proj x xs = maximumBy (comparing proj) (x:xs)

liftedSafeMaximum :: Ord b => (a, b) -> [(a, b)] -> (a, b)
liftedSafeMaximum = q safeMaximumOn

这与a previous answer of mine基本相同。

答案 1 :(得分:3)

您将此描述为“想要将某个功能提升到一个上下文中”,所以让我们看看这意味着什么。从所需类型开始:

q :: (a, b) -> ([b] -> b) -> ([(a, b)] -> (a, b))

......我们可以乐观地抽象出所需的背景:

q :: f b -> ([b] -> b) -> ([f b] -> f b)

假设fFunctor - 在激励示例中,我们可以将[b] -> b提升为f [b] -> f b。如果我们有[f b] -> f [b]类型的函数,我们可以从那里得到所需的类型,它看起来很像sequence

考虑f((->) a)的情况:给定函数[b] -> b和列表[a -> b],我们可以返回应用其的a -> b函数类型a的参数对列表中的每个函数使用选择器函数,然后返回类型b的结果。这听起来像你正在追求的!

不幸的是,它不适用于您的具体示例 - 涉及的Monad是作者monad,它会在Monoid上添加a约束并始终返回monoid总和列表中的每个a值。

它失败的原因是我们只有b值的不透明选择函数,必须在没有任何上下文的情况下使用它,这需要使用sequence之类的东西来提取(并且在过程合并)所有个别情境。要编写所需的函数,您需要一种方法来合并上下文,而不会丢失将上下文与每个元素相关联的信息。

读者monad可以在其他人没有的地方工作,因为所涉及的“合并”过程是唯一的 - 将单个参数应用于多个函数是基于\x -> ()和{{\x -> (x, x)给出的规范comonoid的逆向使用1}},其中每个结果元素唯一地确定原始输入。

要在协变位置获得相同的属性,我们需要一个monoid,其中每个输入元素唯一地确定结果总和,这意味着每个输入必须相同,这意味着它们必须是只有一个值的类型。基于此,我们确实可以使用稍微受限制的类型编写函数版本:

q' :: ([b] -> b) -> ([((), b)] -> ((), b))

但我认为这不是很令人满意。 :

答案 2 :(得分:2)

现在,使用新的签名/界面,这不是一件小事吗?

import Data.List (elemIndex)

q v f [] = v
q v f xs = let ys = map snd xs; x = f (snd v) ys in
           case elemIndex x ys of Just i -> xs !! i ; _ -> v 
                                               -- or (fst v, x) if you prefer

没有safeMaximum约束就没有Ord,对吗?这是一个遗漏,还是设计?

答案 3 :(得分:1)

我认为这不可能以有意义的方式实现。由于这只是直觉,我无法证明,我只是给出一个反例:

假设我有函数sum :: Num a => [a] -> a。我可以将它应用到列表中:

sum [1 .. 4]
> 10

但是,假设我想应用我的'提升'sum

liftedSum [("abc", 1), ("def", 2), ("ghi", 3), ("jkl", 4)]
> (??WTF??, 10)

??WTF??应该有什么有意义的价值?我想不出一个。

问题是我将[b] -> b解释为“选择一个”,实际上它也可能意味着“聚合”。并且没有任何有意义的方法可以将聚合函数“提升”为元组,正如我试图做的那样。

但另一个问题是,即使[b] -> b表示“选择一个”,如果存在重复的[(a, b)]值,也不能使用b来唯一选择。例如:

liftedMax [("abc", 1), ("def", 2), ("ghi", 2)]
> (>> is this "def" or "ghi"? <<, 2)

所以我认为我的功能不能合理地实现,而且我认为Haskell的类型系统让我很难用脚射击自己很酷。

答案 4 :(得分:1)

没有那种确切的类型签名,没有。 例如,如果您选择type b = Double->Double,而您的函数[b]->bfoldr (.) id, 然后你的多态函数q 不能使用那里产生的值从对中选择, 但我认为这会误解你的问题,就是寻求特定类型的信号,而不是提升/提升 从元素到对的选择方法。

解决原始选择问题

如果您的原始函数仅用于从列表中选择元素, 你可以告诉Haskell如何在两者之间进行选择。

这个解决方案是安全的,因为它强制使用原始列表或后备元素中的选择, 通过使用辅助函数b -> b -> Bool,其中True表示您更喜欢第一个参数。

我们可以用它来选择一对:

selectPair :: (a -> a -> Bool) -> (a,c) -> (a,c) -> (a,c)
selectPair f (a,c) (a',c') 
    | f a a'    = (a,c)
    | otherwise = (a',c')

然后折叠以从列表中选择:

selectList :: (a -> a -> Bool) -> (a,c) -> [(a,c)] -> (a,c)
selectList f = foldr (selectPair f)

请注意,这不需要a类型上的任何实例,因此可能是您在常规设置中所需的实例。

解决最大问题

当然(b -> b -> Bool)感觉非常像>实例中的Ord,并使用了您的示例 一个函数建议最大值,但是如果你有一个Ord实例, 使用导入Data.ListData.Function进行

最简单
safePairMaximum :: Ord b => (a, b) -> [(a, b)] -> (a, b)
safePairMaximum m bs = maximumBy (compare `on` snd) $ m:bs

这是hammar's solution部分的一个更基本,不太酷的版本。

也许你被困[b] - &gt; b,但确实在b

这与我认为合理的类型签名非常接近,同时仍然可以解决您所说的问题: 如果使用选择函数::[b]->b至关重要,那么您至少需要一个Eq上下文:

chooseLike :: Eq b => (a, b) -> ([b] -> b) -> ([(a, b)] -> (a, b))
chooseLike m selectb pairs = let wanted = selectb $ map snd pairs in
    case filter ((==wanted).snd) pairs of
      [] -> m
      (p:_) -> p

(您当然可以用Eq参数替换(b -> b -> Bool)上下文, 这次表示平等。)

这并不理想,因为您将[b]列表单独遍历到[(a,b)]列表,这似乎效率低下。

结论

虽然我相信你指定的类型没有任何有用的功能, 那里有 解决你说的问题的各种方法。这是一个有趣的问题,谢谢。