在没有输入变量的情况下在Haskell中实现递归

时间:2013-05-01 09:20:45

标签: haskell recursion

所以我对编程仍然很陌生,而且我在Haskell的语法方面苦苦挣扎。我知道自己想要实施什么,但我不确定该怎么做,所以我来这里问。

所以我拥有的是#34;桩"没有特定顺序的数字由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' ="它汇总了列表中没有输入变量的所有元素。首先,我通过定义一个列表并使用list命令来解决这些问题,以便总结并查看某些内容是否为" 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

5 个答案:

答案 0 :(得分:6)

假设没有列表就可以做到这一点,这很难过,因为这将是非常惯用的解决方案。

最后一个惯用的是通用的,能够遍历你的桩。你基本上想要一个 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'

答案 1 :(得分:6)

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

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

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

然后你可以做

answer = add lowestnumber 0

答案 2 :(得分:3)

Haskell中的各种高阶函数捕获各种递归(和corecursion )模式,如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

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

答案 3 :(得分:3)

关于您的三个新功能。

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 :: 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

所以这个似乎有点儿。

答案 4 :(得分:2)

  

首先,我通过定义列表并使用来解决这些问题   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以在其输出中包含break元素。