[英]Creating higher order functions using Haskell
I have recently been teaching myself Haskell, and one of my exercises was to implement a function that takes two arguments: a list and a single value. 我最近一直在自学Haskell,我的练习之一是实现一个带有两个参数的函数:一个列表和一个单个值。 The function would check if the value is in the list twice or more.
该函数将检查该值是否在列表中两次或更多次。 I cannot use the function element or member.
我不能使用function元素或成员。
I tried removing the values that are not equal to the value. 我尝试删除不等于该值的值。 Then checking for the size of the new list if its more than 1 then it outputs
True
if not it outputs False
. 然后检查新列表的大小是否大于1,然后输出
True
,否则输出False
。 I having problem trying to use a function inside a function. 我在尝试在函数内部使用函数时遇到问题。
remove2 val [] = []
remove2 val (x:xs) = if ( not (x == val))
then remove2 val xs
else x:remove2 val xs
isMemberTwice :: (Eq val) => val -> [val] -> Bool
isMemberTwice val [] = False
isMemberTwice val (x:xs)
| ( >= (length (remove2 val [val])) 2) = True
| otherwise = a `isMemberTwice’` xs
A higher order function is a function that takes another function as argument or returns another function. 高阶函数是将另一个函数作为参数或返回另一个函数的函数。
Your problem at hand is easily solvable using a short recursive function: 使用简短的递归函数可以轻松解决您遇到的问题:
memtwice :: (Eq a) => a -> [a] -> Bool
memtwice value list = scan value list False
where scan _ [] _ = False
scan v (x:xs) True =
if v == x then True
else scan v xs True
scan v (x:xs) False =
scan v xs (v == x)
The scan
is a function that carries state information (whether there has already been found one instance) as additional parameter. scan
是一种将状态信息(是否已经找到一个实例)作为附加参数的功能。
One could rewrite this using higher order functions such as fold
though I'm not sure how one could implement the short circuit behaviour (stopping as soon as two instances have been found) then. 虽然我不确定一个人如何实现短路行为(一旦找到两个实例就立即停止),则可以使用诸如
fold
高阶函数来重写它。
Every function on a list can be written in this form: 列表中的每个函数都可以用以下形式编写:
f [] = ... -- also called the "base case"
f (a:as) = ... -- also called the recursive case
Let's apply this idea to writing a function which determine the number 3 appears in a list at least once: 让我们将此想法应用于编写一个确定数字3至少出现在列表中一次的函数:
hasA3 :: [Int] -> Bool
hasA3 [] = ...
hasA3 (a:as) = ...
Clearly hasA3 [] = False
. 显然
hasA3 [] = False
。 I'll leave you to figure out how to write the recursive case. 我将让您弄清楚如何编写递归案例。 Hint: the function might have to check if a == 3.
提示:函数可能必须检查a == 3。
Now let's write a function which determines if a list contains two or more threes. 现在让我们编写一个函数,该函数确定列表是否包含两个或多个三。 Again we start with the two cases:
同样,我们从两种情况开始:
hasTwo3s :: [Int] -> Bool
hasTwo3s [] = ...
hasTwo3s (a:as) = ...
Again, the base case is easy. 同样,基本情况很容易。 Hints for the recursive case: you might have to check if a == 3 and then you might want to use the
hasA3
function. 递归情况的提示:您可能必须检查a == 3,然后才能使用
hasA3
函数。
I will add to Daniel Jour's answer starting from its final note: 我将在最后的注释中加入Daniel Jour的回答 :
One could rewrite this using higher order functions such as
fold
though I'm not sure how one could implement the short circuit behaviour (stopping as soon as two instances have been found) then.虽然我不确定一个人如何实现短路行为(一旦找到两个实例就立即停止),则可以使用诸如
fold
高阶函数来重写它。
Let's transform the original code: 让我们转换原始代码:
memtwice value list = scan value list False
where scan _ [] _ = False
scan v (x:xs) True =
if v == x then True
else scan v xs True
scan v (x:xs) False =
scan v xs (v == x)
Moving to boolean operators we get: 转向布尔运算符,我们得到:
memtwice value list = scan value list False
where scan _ [] _ = False
scan v (x:xs) True = v == x || scan v xs True
scan v (x:xs) False = scan v xs (v == x)
Now, the parameter v
is always value
, so let's remove the parameter. 现在,参数
v
始终是value
,因此让我们删除该参数。
memtwice value list = scan list False
where scan [] _ = False
scan (x:xs) True = value == x || scan xs True
scan (x:xs) False = scan xs (value == x)
Introducing an explicit lambda for the last argument (not really needed, but helps readability): 为最后一个参数引入一个显式的lambda(不是真正需要的,但有助于提高可读性):
memtwice value list = scan list False
where scan [] = (\_ -> False)
scan (x:xs) = \found -> if found
then value == x || scan xs True
else scan xs (value == x)
We now see that the last recursion pattern is a foldr
: indeed we have a base-case definition for scan []
, and the recursive case scan (x:xs)
is defined only in terms of scan xs
. 现在,我们看到最后一个递归模式是一个文件
foldr
:实际上,我们有scan []
的基本情况定义,并且递归案例scan (x:xs)
仅根据scan xs
定义。
memtwice value list = foldr go (\_ -> False) list False
where go x next = \found -> if found
then value == x || next True
else next (value == x)
Note that foldr
seems to be called with four parameters. 请注意,似乎使用四个参数调用了
foldr
。 This is because go x next
produces a function, hence foldr go (\\_ -> False) list
does as well. 这是因为
go x next
产生一个函数,因此foldr go (\\_ -> False) list
也可以。 We can now revert the explicit lambda. 现在,我们可以还原显式lambda。
memtwice value list = foldr go (\_ -> False) list False
where go x next True = value == x || next True
go x next False = next (value == x)
Finally, note that since ||
最后,请注意,由于
||
has short-circuiting behaviour, we did achieve an equivalent foldr
to the original code. 具有短路行为,我们确实实现了与原始代码等效的文件
foldr
。
There's an easier way really: 确实有一种更简单的方法:
isMemberTwice needle haystack = n >= 2
where n = length $ filter (== needle) haystack
However, the downside with this approach is that, if you pass it a really long list, it'll evaluate the entire list, which is unnecessary: you only need to see if there are at least 2 occurrences of needle
. 但是,这种方法的缺点是,如果将一个非常长的列表传递给它,它将评估整个列表,这是不必要的:您只需要查看是否至少出现了2个
needle
。
So a better solution is to avoid using length
on the filtered list and instead just use pattern match: if it matches (_:_:_)
, there must be at least 2 occurrences: 因此,更好的解决方案是避免在过滤列表上使用
length
,而仅使用模式匹配:如果匹配(_:_:_)
,则必须至少出现两次:
isMemberTwice needle haystack = case occurrences of (_:_:_) -> True
_ -> False
where occurrences = filter (== needle) haystack
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.