我想要一个类型为<{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)
答案 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)
假设f
是Functor
- 在激励示例中,我们可以将[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]->b
为foldr (.) 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.List
和Data.Function
进行
safePairMaximum :: Ord b => (a, b) -> [(a, b)] -> (a, b)
safePairMaximum m bs = maximumBy (compare `on` snd) $ m:bs
这是hammar's solution部分的一个更基本,不太酷的版本。
这与我认为合理的类型签名非常接近,同时仍然可以解决您所说的问题:
如果使用选择函数::[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)]
列表,这似乎效率低下。
虽然我相信你指定的类型没有任何有用的功能, 那里有 解决你说的问题的各种方法。这是一个有趣的问题,谢谢。