[英]Haskell using Higher-Order Functions to count how many items there are in a list
I'm trying to write a line of Haskell that count the number of items in a list that are between two stated upper and lower bounds, eg countBetween 3 6 [5, 9, 2, 4, 6, 3, 1, 4] = 5
since 5, 4, 6, 3 & 4 are between the bounds 3 and 6. 我正在尝试编写一行Haskell,以对列表中两个规定的上限和下限之间的项目数进行计数,例如
countBetween 3 6 [5, 9, 2, 4, 6, 3, 1, 4] = 5
因为5、4、6、3和4在边界3和6之间。
So far I have: 到目前为止,我有:
countBetween x1 x2 = filter (>=x1) . filter (<=x2)
Which filters the list to only include numbers that are within those bounds, but I don't know how to count how many items that list possesses. 哪个过滤器列表仅包含那些范围内的数字,但我不知道如何计算列表拥有的项目数。 My initial thoughts is that it would use
foldr
? 我最初的想法是,它将使用文件
foldr
?
To answer this directly, there's a Haskell tool called hoogle . 为了直接回答这个问题,有一个称为hoogle的Haskell工具。 We'd expect the type of a
length
function to be [a] -> Int
and if we search this verbatim in hoogle we end up with 我们希望
length
函数的类型为[a] -> Int
,如果我们在搜索中逐字搜索,最终得到
length :: [a] -> Int
So 所以
countBetween x y = length . filter (>= x) . filter (<= y)
Note the dot since we're continuing to compose functions. 注意点,因为我们正在继续编写函数。
To turn my comment into an answer: 要将我的评论变成答案:
You need to use the length
function. 您需要使用
length
功能。 In this case you would compose it with what you already have as 在这种情况下,您可以将其与已经拥有的
countBetween lower upper = length . filter (>= lower) . filter (<= upper)
Alternatively, you could define your function with its full arguments as 另外,您可以将函数的完整参数定义为
countBetween lower upper xs = length $ filter (>= lower) $ filter (<= upper) xs
The general rule of thumb for eta reduction is to first turn all your $
s into .
减少eta的一般经验法则是首先将您的所有
$
变成.
s and add a $
before the last argument, so the above line becomes s并在最后一个参数之前添加
$
,因此上一行变为
countBetween lower upper xs = length . filter (>= lower) . filter (<= upper) $ xs
Then you look to see if the last argument to your definition is also the last argument to your expression, and it's the only place in your expression that the argument appears 然后,您查看定义的最后一个参数是否也是表达式的最后一个参数,并且它是表达式中唯一出现该参数的位置
countBetween lower upper xs = length . filter (>= lower) . filter (<= upper) $ xs
-- ^ ^
These can now be removed, leaving you with 现在可以将其删除,让您
countBetween lower upper = length . filter (>= lower) . filter (<= upper)
Then repeat. 然后重复。 In this case, you can't eta reduce any more (easily), so you're done!
在这种情况下,您无法再(轻松)进行eta减少,那么就完成了!
I would also recommend defining your functions with full arguments to begin with then slowly introducing point free versions. 我还建议先使用完整的参数定义函数,然后再慢慢引入无点版本。 Sometimes it's fun to find clever ways to eta reduce expressions, but while it's possible to push it further with some definitions, it isn't always recommended.
有时,找到巧妙的方法来对表达式进行eta简化会很有趣,但是尽管可以通过一些定义将其进一步推广,但并不总是建议这样做。 Only do it where it's natural and makes it easier to read.
仅在自然且易于阅读的地方使用。 For example, using the
pointfree
tool from the pointfree
package, you can eta reduce your definition all the way to 例如,使用
pointfree
包中的pointfree
工具,您可以将定义完全减少到
countBetween = ((length .) .) . (. (filter . flip (<=))) . (.) . filter . flip (>=)
But this is hardly readable. 但这很难理解。 Don't do this.
不要这样
While using the built-in length function is certainly preferrable, here's a solution using a "poor man's length" with foldr: 虽然最好使用内置的长度功能,但以下是将“可怜的人的长度”与文件夹一起使用的解决方案:
countBetween x1 x2 = foldr (\v len -> len + 1) (0) . filter (>= x1) . filter (<= x2)
The length is computed by foldr: 长度由文件夹计算:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.