使用Haskell创建更高阶的函数

时间:2015-10-28 05:47:40

标签: function haskell functional-programming

我最近一直在教自己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

4 个答案:

答案 0 :(得分:2)

高阶函数是一个函数,它将另一个函数作为参数或返回另一个函数。

使用短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)

scan是一个函数,它携带状态信息(是否已经找到一个实例)作为附加参数。

可以使用高阶函数重写这一点,例如fold,但我不确定如何实现短路行为(一旦找到两个实例就停止)。

答案 1 :(得分:1)

列表中的每个函数都可以用以下形式编写:

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) = ...

同样,基本情况很简单。递归案例的提示:您可能必须检查是否= = 3然后您可能想要使用hasA3函数。

答案 2 :(得分:1)

我将从最终说明开始添加Daniel Jour's answer

  

可以使用更高阶函数重写此内容,例如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(不是真的需要,但有助于提高可读性):

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)

我们现在看到最后一个递归模式是foldr:实际上我们有scan []的基本案例定义,递归案例scan (x:xs)仅以{的方式定义{1}}。

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也会生成函数。我们现在可以恢复显式的lambda。

foldr go (\_ -> False) list

最后请注意,由于memtwice value list = foldr go (\_ -> False) list False where go x next True = value == x || next True go x next False = next (value == x) 具有短路行为,我们确实与原始代码实现了等效||

答案 3 :(得分:1)

真的有一种更简单的方法:

isMemberTwice needle haystack = n >= 2
  where n = length $ filter (== needle) haystack

然而,这种方法的缺点是,如果你传递一个非常长的列表,它将评估整个列表,这是不必要的:你只需要看看是否至少 {2}次出现needle

因此,更好的解决方案是避免在筛选列表上使用length,而只使用模式匹配:如果匹配(_:_:_),则必须至少出现2次:

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