【问题标题】:Explicit recursion in HaskellHaskell 中的显式递归
【发布时间】:2012-04-11 17:29:36
【问题描述】:

任务:我正在尝试编写一个类型签名为 minimum_recursive :: (a -> a -> Bool) -> [a] -> a 的函数。对于它的第一个参数,它接受一个我将调用less 的函数,该函数接受两个参数,如果第一个参数小于第二个参数,则返回 True,否则返回 False。 minimum_recursive 也接受一个列表作为它的第二个参数。使用显式递归,minimum_recursive 应该确定其输入列表 [a] 中的最小值。

我的想法:我想把实际的递归放在一个也接受累加器的辅助函数中。我会以第一项作为累加器来调用辅助函数。

到目前为止我有什么:到目前为止我有以下几点:

-- function as first parameter to min'
-- accepts two params, returns True if
-- first must come before second in sorted
-- order
less :: Ord a => a -> a -> Bool
less a b = a < b

-- Subpart B
minimum_recursive :: (a -> a -> Bool) -> [a] -> a
minimum_recursive func list = minimum_recursive_h func list []

我遇到了麻烦,甚至不知道如何开始写minimum_recursive_h

注意:我知道可能有更简单的方法来完成这项任务,但我必须按照上面的说明去做。

【问题讨论】:

  • 如果这是一个家庭作业问题,你应该这样标记它。无论如何,这里有一个指针:基本情况很简单,现在假设您已经有一个函数可以找到长度列表中的最小元素n - 1,您将如何将它与当前元素结合以获得长度列表的答案n?
  • 我想我会找到长度为n - 1的列表的最小元素,然后将其与原始列表的元素n进行比较?我不知道 Haskell 的语法。我会做一些研究。
  • 另请参阅标准库中minimumBy 的来源。

标签: haskell recursion


【解决方案1】:

你可以这样做:

minimum_recursive _ [] = error "no minimum of empty list"
minimum_recursive _ [x] = x
minimum_recursive f (x:xs) = let m = minimum_recursive f xs
                             in if f x m then x else m

或者,使用累加器:

minimum_recursive _ [] = error "no minimum of empty list"
minimum_recursive f (x:xs) = helper f x xs
    where
      helper _ m [] = m
      helper f m (x:xs) 
          | f m x = helper f m xs
          | otherwise = helper f x xs

【讨论】:

  • 真的很喜欢蓄能器解决方案。在我的问题中使用 less 函数时,以及当我使用如下空列表执行我的主要操作时的任何原因:main :: IO () main = print $ minimum_recursive less [] 我收到此错误 Ambiguous type variable "a0" in the constraints: (Show a0) arising from a use of "print" at hello.hs:27:8-12 (Ord a0) arising from a use of "less" at hello.hs:27:34-37
  • @gonzoc0ding:问题是你从不限制列表元素的类型。 []any 类型的有效列表。在这种情况下,GHC 无法自动为您选择“正确”的类型。如果需要,可以给出显式类型,例如minimum_recursive less ([] :: [Integer])
  • 反对票是为了什么?我更喜欢使用折叠(或者真的是minumumBy,但发布者说他出于某种原因需要显式递归。
【解决方案2】:

如果您想要列表中的最小元素,我建议您将当前拥有的最小元素作为参数添加到函数中。

minimum_recursive :: (a -> a -> Bool) -> a -> [a] -> a
minimum_recursive f min [] = min
minimum_recursive f min (x:xs) | f min x   = minimum_recursive f min xs
                               | otherwise = minimum_recursive f x   xs

您还应该将调用它的函数中的类型从a 更改为Maybe a,因为空列表中没有最小的元素。 Here some help about Maybe

如果您想在没有额外参数的情况下执行此操作,您也可以将最小的元素存储在列表的开头。在这种情况下,使用 Maybe 很重要

minimum_recursive :: (a -> a -> Bool) -> [a] ->Maybe a
minimum_recursive f [] = Nothing
minimum_recursive f (x:[]) = Just x
minimum_recursive f (y:(x:xs)) | f y x     = minimum_recursive f (y:xs)
                               | otherwise = minimum_recursive f (x:xs)

这就是在折叠中找到最小值的方法。看看函数式编程的美妙之处。但这不适用于空列表

simplefold :: [a] -> a
simplefold (x:xs) = foldl min x xs  

但是我们可以将这个函数嵌入到一个检查列表是否为空并在这种情况下返回 Nothing 的函数中。

betterfold :: [a] -> Maybe a
betterfold [] = Nothing
beterfold l   = Just (simplefold l)

【讨论】:

  • 关于Maybe的好建议,但是这个问题可以在minimum_recursive不加参数的情况下解决。
  • 如果我想使用 foldl1 而不是显式递归怎么办?有什么建议吗?
  • 添加了如何折叠到答案
  • 太棒了。我想做的一件事是使简单折叠具有与 minimum_recursive 相同的类型签名,例如,将 less 函数作为参数并在计算中使用它
  • 用你作为参数的函数替换 min
【解决方案3】:

递归解决问题的经典方法如下:

  1. 假设您几乎解决了问题,除了最后一步。
  2. 编写代码,在给定除最后一步之外的所有解决方案的情况下,计算该最后一步产生的解决方案。
  3. 编写基本情况。

在列表的情况下,这转化为这种模式:

  1. 基本情况:[] 的解决方案应该是什么? (如果有的话;对于您的 minimum_recursive 函数,这将是一个错误)。
  2. 对于非空列表x:xs,假设您已经拥有almostTheResult = minimum_recursive f xs。鉴于此,您如何计算 minimum_recursive (x:xs)

给你一个大大的提示:你的minimum_recursive可以用foldr和这个函数来实现:

minBy :: (a -> a -> Bool) -> a -> a -> a
minBy pred x y = if pred x y then x else y

foldr 函数完全符合我上面的描述。 foldr 的第一个参数是计算给定列表头和尾的部分解的最终解的函数,第二个参数是结果基本情况。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-12-28
    • 2020-02-18
    • 2015-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-16
    相关资源
    最近更新 更多