9

I want to do something fairly simple; I am using the operator (++) with Data.Map insertWith, and it works fine, but I want to eliminate duplicates in the value created, so want to compose it with nub.

I tried (nub (++)), (nub $ (++)), (nub . (++)), all to no avail, in that the type of (++) does not match the expected type of nub ( [a] ).

I could of course define an auxiliary function or a lambda, but I think that probably there is a composition which would be clearer.

Hints please!

4

5 回答 5

11

你可以这样写

((nub .) .) (++)

例子:

Prelude Data.List> ((nub .) .) (++) [1,2,3] [3,4,5]
[1,2,3,4,5]

一般来说,你有

(f . ) g x = f (g x) 
((f . ) . ) g x y = f (g x y) 
(((f . ) . ) . ) g x y z = f (g x y z) 
((((f . ) . ) . ) . ) g x y z v = f (g x y z v)
...

这是这个身份的推导((nub .) .)

(f . g) x = f (g x)

(nub .) :: Eq a1 => (a -> [a1]) -> a -> [a1] 
(nub .) = \g x -> (nub (g x))

((nub .) .) :: Eq a2 => (a -> a1 -> [a2]) -> a -> a1 -> [a2]
((nub .) .) = ((\g x -> (nub (g x))) .) = (\g' x' -> (\g x -> (nub (g x))) (g' x'))
            = \g' x' x -> (nub ((g' x') x))

有一篇关于这个(和相关)习语的好文章,但它是俄语的:-(

于 2011-07-06T16:02:08.737 回答
6

您想要的似乎是二元和一元函数的组合,如下所示:

compose :: (c -> d) -> (a -> b -> c) -> (a -> b -> d)
compose unary binary a b = unary (binary a b)

你要求一个无点版本(没有提到ab变量)。让我们尝试将它们一一消除。我们将从 开始b,使用以下事实f (g x) = f . g

compose unary binary a = unary . binary a

a接下来是。让我们首先对表达式进行脱糖:

compose unary binary a = ((.) unary) (binary a)

并再次应用相同的组合规则:

compose unary binary = ((.) unary) . binary

这可以进一步写成:

compose unary = (.) ((.) unary)

甚至作为

compose = (.) . (.)

在这里,每个(.)“剥离”二进制函数的一个参数,您需要其中两个,因为该函数是二进制的。当泛化到任何函子时,这个习语非常有用:(fmap . fmap请注意,fmap.函数被视为函子时)。这允许您“剥离”任何函子,例如您可以编写:

incrementResultsOfEveryFunctionInTwoDimentionalList :: [[String -> Integer]] -> [[String -> Integer]]
incrementResultsOfEveryFunctionInTwoDimentionalList = fmap . fmap . fmap $ (+1)

所以,你的结果变成:

(fmap . fmap) nub (++)

编辑:

我想我找到了我的大脑试图重现的答案:(c→d) → (a→b→c) → (a→b→d) 类型的 Haskell 函数组合运算符

于 2011-07-06T15:56:33.307 回答
3

语义编辑器组合器以一种特别简单和美观的方式解决了这个问题。授予:

您的最终构图将如下所示:

(result.result) nub (++)
于 2011-07-06T17:09:35.390 回答
1

您可以使用看起来有点滑稽的(.).(.)组合器:

Prelude> :set -XNoMonomorphismRestriction
Prelude> :m + Data.List
Prelude Data.List> let f = ((.).(.)) nub (++)
Prelude Data.List> :t f
f :: Eq a => [a] -> [a] -> [a]
Prelude Data.List> f "ab" "ac"
"abc"

不过,在 - 子句中使用 lambda 或辅助函数可能会更具可读性where

于 2011-07-06T15:55:40.270 回答
1

我认为您想要的组合运算符在任何标准库中都不会作为单个函数存在。写它的最短方法可能是((.).(.)). 使用 的Functor定义((->) t),您也可以将其写为fmap . fmapor,如果您愿意的话fmap fmap fmap

以上所有内容都非常神秘,但这个成语很常见,以至于很多人都会认出你在做什么。

顺便说一句,你可能希望避免在 Haskell 中调用两个参数的函数“二元”,因为如果你将该术语扩展到一个参数的函数,你会真正混淆人们。

另请参阅此问题以获取一些相关讨论。

您还可以在这个库中找到许多名称非常直观的组合器。

于 2011-07-06T15:57:01.467 回答