Function 使用Haskell创建高阶函数

Function 使用Haskell创建高阶函数,function,haskell,functional-programming,Function,Haskell,Functional Programming,我最近一直在自学Haskell,其中一个练习是实现一个包含两个参数的函数:一个列表和一个值。该函数将检查该值是否在列表中两次或多次。我不能使用函数元素或成员 我尝试删除不等于该值的值。然后检查新列表的大小(如果大于1),然后输出True,如果不是,则输出False。我在尝试在函数中使用函数时遇到问题 remove2 val [] = [] remove2 val (x:xs) = if ( not (x == val)) then remove2

我最近一直在自学Haskell,其中一个练习是实现一个包含两个参数的函数:一个列表和一个值。该函数将检查该值是否在列表中两次或多次。我不能使用函数元素或成员

我尝试删除不等于该值的值。然后检查新列表的大小(如果大于1),然后输出
True
,如果不是,则输出
False
。我在尝试在函数中使用函数时遇到问题

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

高阶函数是将另一个函数作为参数或返回另一个函数的函数

你手头上的问题很容易用一个简单的方法解决

scan
是一个将状态信息(是否已找到一个实例)作为附加参数的函数

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)

人们可以使用更高阶的函数(如
fold
)来重写它,尽管我不确定如何实现短路行为(在找到两个实例后立即停止)。

列表中的每个函数都可以这样写:

f     [] = ...   -- also called the "base case"
f (a:as) = ...   -- also called the recursive case
让我们将此思想应用于编写一个函数,该函数确定数字3至少出现在列表中一次:

hasA3 :: [Int] -> Bool
hasA3  []     = ...
hasA3  (a:as) = ...
显然
hasA3[]=False
。我将让您去研究如何编写递归案例。提示:函数可能必须检查a==3

现在,让我们编写一个函数来确定一个列表是否包含两个或多个三。我们再次从两个案例开始:

hasTwo3s :: [Int] -> Bool
hasTwo3s []     = ...
hasTwo3s (a:as) = ...
同样,基本情况很简单。递归案例提示:您可能需要检查a==3,然后可能需要使用
hasA3
函数。

我将从最后一个注释开始添加:

可以使用更高阶的函数(如
fold
虽然我不知道如何实现短路 行为(发现两个实例后立即停止)

让我们转换原始代码:

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)
转到布尔运算符,我们得到:

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)
现在,参数
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)
为最后一个参数引入显式lambda(实际上不需要,但有助于可读性):

我们现在看到最后一个递归模式是
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)
请注意,
foldr
似乎是通过四个参数调用的。这是因为
go x next
生成一个函数,因此
foldr go(\\\\->False)list
也会生成一个函数。我们现在可以恢复显式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)

最后,请注意,由于
|
具有短路行为,我们确实实现了与原始代码等效的
foldr

有一种更简单的方法:

isMemberTwice needle haystack = n >= 2
  where n = length $ filter (== needle) haystack
但是,这种方法的缺点是,如果您传递一个非常长的列表,它将评估整个列表,这是不必要的:您只需要查看是否至少出现了两次
needle

因此,更好的解决方案是避免在筛选列表上使用
length
,而只使用pattern match:如果它匹配
(::U;)
,则必须至少出现两次:

isMemberTwice needle haystack = case occurrences of (_:_:_) -> True
                                                    _       -> False
  where occurrences = filter (== needle) haystack

这与高阶函数有什么关系?@DanielJour我真的不确定,但curried函数不是高阶函数吗?调用另一个函数不是一个高阶函数。@LuisFernandoPineda:不,不:我冒昧地从下面的最后一个注释继续。真棒:)当我第一次使用基于
折叠的解决方案时,我没有想到
|
的短路行为。这很聪明:)也许你应该在以后的代码中添加这一点依靠惰性评估来实现其短路行为。
isMemberTwice needle haystack = case occurrences of (_:_:_) -> True
                                                    _       -> False
  where occurrences = filter (== needle) haystack