简体   繁体   English

使用Haskell创建高阶函数

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM