Haskell使用高阶函数来计算列表中有多少项

时间:2014-01-15 14:44:26

标签: list haskell higher-order-functions

我正在尝试编写一行Haskell,它计算列表中两个指定的上限和下限之间的项目数,例如: countBetween 3 6 [5, 9, 2, 4, 6, 3, 1, 4] = 5自5,4,6,3& 4在界限3和6之间。

到目前为止,我有:

countBetween x1 x2 = filter (>=x1) . filter (<=x2)

哪个过滤列表只包含在这些边界内的数字,但我不知道如何计算列表拥有的项目数。我最初的想法是它会使用foldr

3 个答案:

答案 0 :(得分:4)

要直接回答这个问题,有一个名为hoogle的Haskell工具。我们希望length函数的类型为[a] -> Int,如果我们在hoogle中逐字搜索,我们最终会

 length :: [a] -> Int

所以

countBetween x y = length . filter (>= x) . filter (<= y)

请注意点,因为我们正在继续撰写函数。

答案 1 :(得分:4)

将我的评论转变为答案:

您需要使用length功能。在这种情况下,您可以使用已有的

组成它
countBetween lower upper = length . filter (>= lower) . filter (<= upper)

或者,您可以使用其完整参数定义函数

countBetween lower upper xs = length $ filter (>= lower) $ filter (<= upper) xs

eta减少的一般经验法则是首先将所有$转换为. s并在最后一个参数前添加$,因此上面的行变为

countBetween lower upper xs = length . filter (>= lower) . filter (<= upper) $ xs

然后你看看你的定义的最后一个参数是否也是表达式的最后一个参数,它是你的表达式中唯一出现参数的地方

countBetween lower upper xs = length . filter (>= lower) . filter (<= upper) $ xs
--                       ^                                                     ^

现在可以删除这些内容,只剩下

countBetween lower upper = length . filter (>= lower) . filter (<= upper)

然后重复一遍。在这种情况下,你不能再减少(轻松),所以你已经完成了!

我还建议用完整的参数定义你的函数,然后慢慢引入无点版本。有时候找到简单的方法来减少表达式很有趣,但是虽然可以通过一些定义进一步推动它,但并不总是建议。只在天然的地方进行,并使其更容易阅读。例如,使用pointfree包中的pointfree工具,您可以将您的定义一直减少到

 countBetween = ((length .) .) . (. (filter . flip (<=))) . (.) . filter . flip (>=)

但这很难理解。不要这样做。

答案 2 :(得分:1)

虽然使用内置长度功能当然是可取的,但这里有一个使用折叠器的“穷人长度”的解决方案:

countBetween x1 x2 = foldr (\v len -> len + 1) (0) . filter (>= x1) . filter (<= x2)

长度由foldr计算:

  • 我们从0
  • 的累加器开始
  • 对于每个列表元素v,我们只需将1添加到累加器