Haskell中的元素数量(以点自由方式表示)

10

我希望定义一个函数,该函数计算列表中满足给定谓词的元素数量:

  number_of_elements :: (a -> Bool) -> [a] -> Int
  number_of_elements f xs = length (filter f xs)
例如:
  number_of_elements (==2) [2,1,54,1,2]

应该返回2

我们可以写得更短:

  number_of_elements f = length . filter f

有没有可能不使用f参数来编写它?


3
你寻找的是“无点风格”(Pointfree style)。这里有一个关于它的维基页面:http://www.haskell.org/haskellwiki/Pointfree。它会教你所有的技巧,比如猫头鹰式:`((.)$(.))` 和点式:((.).(.))。尽管如此,我个人不太建议采用这种风格。 - Thomas Ahle
6
我建议你试着操作一下,看看它是如何工作的,但使用部分无点风格number_of_elements f = length . filter f。通常这样最易读懂。 - Daniel Fischer
6
我很少费心定义这个函数,因为 length (filter f xs) 更容易阅读,而 number_of_elements f xs 需要我查找你的函数定义、文档或通过类型推断来弄清楚它的作用;而前者则是我已经理解的两个函数的简单结合使用——而且写起来更短!我只会在 where 绑定中将其定义为辅助函数,或作为未导出的模块函数——即使这样,也只有在它将成为其他函数的参数时才会定义。 - Luis Casillas
1
sacundim 是完全正确的。如果你真的想要比 length (filter f xs) 更多的东西,我建议在 is7s 的答案中定义 (.:) 组合子,并根据需要使用 length .: filter,但即使这样也有点傻。 - C. A. McCann
3个回答

17
当然可以:
number_of_elements = (length .) . filter

谢谢,你能解释一下为什么(length . filter)(length $ filter)不能工作吗? - vikingsteve
1
@vikingsteve,好的,length . filter 扩展为 \x -> length (filter x)。这里 filter x 是一个函数,你不能计算一个函数的长度,无论那意味着什么。同样地,length $ filter 尝试计算 filter 函数的长度。 - Rotsor

12

我认为你所提出的方法已经足够易读了。但是,如果只是出于好玩,你可以这样做:

numberOfElements = (.) (.) (.) length filter
或者
(.:) = (.) . (.)
numberOfElements = length .: filter

3
通常称呼(.*)(.:),例如lambdabot就是这样称呼它的。 - ehird
@C.A.McCann 可能是因为 (.) 短了一个字符? :) - is7s
我个人更喜欢 .*,事实上我上传了一个愚蠢的包到 Hackage(名为“composition”),它在 Data.Composition 下定义了它。 - Dan Burton
语义编辑器组合风格的一个很棒的特性——fmap.fmapresult.result——是它始终保持组合一致。如果你想要深入三层,只需添加第三个fmapresult(或者firstsecond等)。相比之下,当你添加或删除fmap时,fmap fmap fmap会变成完全不同的东西。 - Conal

7

您可能想了解有关语义编辑器组合子的内容。从那里获取result组合子:

result :: (output -> output') -> (input -> output) -> (input -> output')
result = (.)

result组合子接收一个函数,并将其应用于另一个函数的结果。现在,我们来看一下我们拥有的函数:

filter :: (a -> Bool) -> [a] -> [a]
length :: [a] -> Int

现在,length适用于[a];恰好这是形如foo :: [a] -> [a]的函数的结果类型。因此,
result length :: ([a] -> [a]) -> ([a] -> Int)

但是filter的结果恰好是一个[a] -> [a]函数,因此我们想将result length应用于filter的结果:

number_of_elements = result (result length) filter

1
我会重写最后一行代码,以隔离组合的SEC(语义编辑器组合器):number_of_elements = (result.result) length filter。这句话的意思是:通过将 length 应用于 filter 的结果来计算元素的数量。在这里,(result.result) 是编辑器组合器,而 length 是(语义)编辑器。一定要阅读SEC文章 - Conal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接