【问题标题】:Implementing recursion in Haskell without input variable在没有输入变量的 Haskell 中实现递归
【发布时间】:2013-05-01 09:20:45
【问题描述】:

所以我对编程仍然很陌生,而且我在 Haskell 的语法方面遇到了很多困难。我有点知道我想实现什么,但我不确定如何去做,所以我来这里问。

所以我所拥有的是由 3 个不同函数定义的无特定顺序的“一堆”数字。一个例子是:

lowestnumber = 4
highestnumber 5 = True
highestnumber _ = False
above 4 = 11
above 11 = 18
above 18 = 2
above 2  = 3
above 3  = 5
above 5  = error "highest Number"
above _ = error "Not part of the pile"

现在,我想编写一个函数来检查某个数字是否是这个堆的一部分,以及一个不同的函数“sum' =”,它可以在没有输入变量的情况下对列表的所有元素进行求和。首先,我通过定义一个列表并使用 listcommands 来解决这些问题,以便总结并查看该列表中是否有“elem”,但我应该在不使用列表的情况下解决它。

所以我有如何解决这个问题的想法,但我不知道如何在没有收到无数错误的情况下实际编写它。 我为检查功能尝试过的一些示例:

check x = if above x /= error "Not part of the stack" || lowestnumber == x then True else False

我也尝试过像这样使用“_”进行检查,但它也不起作用:

check x if above x == _ || lowestnumber == x then True else False

我对 sum 函数的想法是这样的:

sum' = lowestnumber + above lowestnumber + above (above lowestnumber) + above (above (above lowestnumber))

或者类似的东西

sum' = lowestnumber + (above sum') 

据我了解

等等,但我不知道如何使用递归来实现这一点,这显然是要走的路。

希望这个问题不会太愚蠢!我希望你能帮助我:)

编辑:好的,这些是我的 3 个功能问题的解决方案

sumup' a b 
           |highestNumber a == True = a+b 
           |otherwise = sumup' (above a) (a+b)

sumup = sumup' lowestNumber 0



check' a b 
            |a == b = True
            |True == highestNumber a && a==b = True
            |True == highestNumber a && a/=b = False
            |check' (above a) (b) == True = True
            |otherwise = False

check b = check' (lowestNumber) (b)



above' :: Integer -> Integer -> Bool
above' x y
            | check x == False = False
            | check y == False = False
            | highestNumber y == True = False
            | highestNumber x == True = True
            | x==y = True
            | above' x (above y) == True = True
            | otherwise = False

【问题讨论】:

  • 首先,我建议阅读有关haskell 的适当教程,例如Learn You A Haskell,它是免费的!更重要的是,error 无法按照您尝试使用的方式工作。通常应该避免,您最好查看Maybe
  • 我一直在阅读《Learn You A Haskell》,我在那里找不到我的问题的答案,但我可能只是做得还不够!无论如何,我会调查一下Maybe
  • 你能用命令式语言写吗?如果您可以比较它,它可能更容易理解。将其发布到jsfiddle.net 并尝试用haskell 进行翻译。
  • 我不能。 Haskell 是我学习的第一门语言。
  • 好的,将您的完整代码发布到hpaste.org,这样我们就有了更好的画面。另外,我建议您在 haskell irc 中闲逛一段时间,以便在您尝试一些东西时直接问一些小的问题。

标签: haskell recursion


【解决方案1】:

如果您想在没有列表的情况下执行此操作,请保留一个运行总计,并使用递归。

如果您在highestnumber,只需将其添加到您当前的总数中并停止, 否则,将这个数字添加到您的总数 total + n 中,然后转到下一个 above n

add n total |highestnumber n = total + n
            |otherwise = add (above n) (total + n)

那你就可以了

answer = add lowestnumber 0

【讨论】:

  • 谢谢!这很好用,我现在会尝试自己重新创建它。
  • 啊,我现在是一只快乐的小狗!我能够重新创建您的功能,并且我设法按照您的示例创建了检查功能。我认为这真的帮助我理解了递归!非常感谢。可悲的是,我似乎无法将我的检查功能复制到 cmets 中,但它看起来很糟糕:D
  • @user2299050:你也可以写下你的解决方案作为答案!
  • @yatima2975 哈哈,是的,我想我会这样做! :D
【解决方案2】:

应该在没有列表的情况下执行此操作,这很可悲,因为这将是非常惯用的解决方案。

下一个惯用的方法是通用的东西,能够遍历你的堆。你基本上想要一个 fold 超过数字:

foldlMyPile :: (a -> Int -> a) -> a -> {- Pile -> -} a
foldlMyPile f = go lowestNumber
 where go n accum
         | highestNumber n  = result
         | otherwise        = go (above n) result
        where result = f accum n

一旦你有了这个,你就可以用它来定义总和、元素等等like they are defined on lists

sumPile :: Int
sumPile = foldlMyPile (+) 0

elemPile :: Int -> Bool
elemPile n = foldlMyPile $ \alreadyFound n' -> alreadyFound || n==n'

【讨论】:

  • 您的foldrMyPile 是左折叠(具有翻转组合功能,f),而不是右折叠。 :) 一方面,它是尾递归的。因此,您的elemPile 没有提前截止。
  • 你是对的!虽然实际上,我想这取决于你所说的左右......无论如何,改变了名字和签名。
  • 不,有明显区别。左折叠是尾递归的,不能提前停止。右折叠是​​递归的,并且可以。 (go n z | highestNumber n = f n z | otherwise = f n $ go (above n) z)。
【解决方案3】:

Haskell 中的各种高阶函数捕获各种递归(和核心递归)模式,如iteratefoldrunfoldr 等。

这里我们可以使用until :: (a -> Bool) -> (a -> a) -> a -> a,其中until p f x产生迭代应用f直到p成立的结果,从x开始:

sumPile = snd $ 
    until (highestnumber . fst) 
          (\(a,b)->(above a, b + above a)) 
          (lowestnumber,   lowestnumber)

还有,

inThePile p = p==until (\n-> highestnumber n || n==p) above lowestnumber

基本上,使用累加器进行递归,在从起始案例向前的路上构建其结果,而常规递归在从基本案例返回的路上构建其结果。

【讨论】:

    【解决方案4】:

    关于您的三个新功能。

    sumup' a b 
           | highestNumber a == True = a+b 
           | otherwise = sumup' (above a) (a+b)
    
    sumup = sumup' lowestNumber 0  -- sum up all numbers in the pile
    

    这几乎与 AndrewC'c 的答案完全相同。很好,除了== Temp 完全是多余的,不需要。 sumup' 通常也将成为一个内部函数,移动到 where 子句中。因此,它不必具有描述性名称。有些使用(受方案启发?)loop,有些使用 go(因为 do 是保留的语法关键字)。我个人最近才开始使用g

    sumup = g lowestNumber 0     -- sum up all numbers in the pile
      where
        g n tot                  -- short, descriptive/suggestive var names 
           | highestNumber n  = n + tot    
           | otherwise        = g (above n) (n + tot)
    

    check b = check' lowestNumber b   -- don't need any parens here
    
    check' a b 
            |a == b = True
            |True == highestNumber a && a==b = True  -- `True ==` not needed
            |True == highestNumber a && a/=b = False -- `True ==` not needed
            |check' (above a) (b) == True = True     -- `== True` not needed
            |otherwise = False
    

    这通常会写成

    check' a b = (a == b) ||
                 (highestNumber a && a==b) || 
                 (  not (highestNumber a && a/=b) 
                    && check' (above a) b  )
    

    在第二次测试中,如果a==b 为真,则它已经在第一条规则中起作用,因此我们可以假设a/=b 今后。所以第二次测试总是错误的;我们得到

    check' a b = (a == b) ||
                 (not (highestNumber a) && check' (above a) b)
    

    看起来还不错。也可以用警卫再次写成,如

    check' a b | (a == b)        = True
               | highestNumber a = False
               | otherwise       = check' (above a) b
    

    或者,为了保持一致性,使用简短的暗示性变量名称,并交换参数顺序,

    check' n i | highestNumber i = i == n 
               | otherwise       = i == n || check' n (above i)
    

    这与第一个 sumup 代码的结构非常相似。


    现在,第三个功能。首先,它也可以很容易地用check' 来定义,只需从给定的低数字而不是最低的数字开始:

    higher top low = check low && not (highestNumber low) 
                               && check' top (above low) 
    

    (“higher”是一个更有特色的名字,是吗?)。你的版本:

    higher :: Integer -> Integer -> Bool
    higher x y
            | check x == False = False         -- not(check x == False)  -- ==
            | check y == False = False         --     check x == True    -- ==
            | highestNumber y == True = False  --     check x
            | highestNumber x == True = True
            | x==y = True
            | higher x (above y) == True = True
            | otherwise = False
    

    再次简化,

    higher x y = check x && check y 
                 && not (highestNumber y) 
                 && ( highestNumber x 
                      || x==y                  -- really?
                      || higher x (above y) )  -- too strong
    

    所以这个看起来有问题。

    【讨论】:

      【解决方案5】:

      首先我通过定义一个列表并使用 listcommands 以总结并查看是否有“elem” 列表,但我应该在不使用列表的情况下解决它。

      您可以通过扩展 elem 来解决这个问题,如下所示:

      x `elem` [1,2,3]
      

      相同
      x == 1 || x == 2 || x == 3
      

      当你在它的时候

      sum' = 4 + 11 + 18 + 2 + 4  + 5
      

      您还可以使用类似的东西构建所有元素的列表

      elements = takeUntil highestnumber (iterate above lowestnumber)
      
      takeUntil p xs = foldr (\x r -> if p x then [x] else x:r) [] xs
      

      这是我看到的唯一方法,您可以在不使用常量的情况下编写校验和求和函数。


      我们不能使用takeWhile (not . highestnumber),因为我们会错过最大的数字。因此,takeUntil 必须以这种方式定义以在其输出中包含中断元素。

      【讨论】:

      • 相反,它不存在。 :) 胡歌knows nothing about it.
      • @这很有趣吗。 Frege 有这个,所以我认为 Haskell 也有。似乎与 dropWhile 和 dropUntil 相同。
      • :) 你的takeUntil 包含破坏元素吗?
      • 应该,不应该,否则它总是可以写成takeWhile(不是.p)。快速检查结果并非如此(grrrrr)。无论如何,更感谢你的澄清。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-20
      • 2016-12-11
      • 1970-01-01
      • 2023-03-24
      • 1970-01-01
      相关资源
      最近更新 更多