繁体   English   中英

Haskell - C栈溢出

[英]Haskell - C stack overflow

我刚刚被介绍给Haskell,所以我对如何使用该语言进行编码并不是很熟练,因此,如果这是重复的话,对不起,但是我不理解用该语言编写的其他代码。

我正在尝试编写一个算法,该算法将采用不超过给定值的正整数甚至整数的总和。 我试图编写代码但是我一直在收到C堆栈溢出错误。

这是我的代码:

sumint :: Int -> Int
sumint x
  | x==0 = 0  
  | x==1 = 1
  | (x `mod` 2 == 0) && (x >= 2) = x + (sumint x-2)
  | (x `mod` 2 /= 0) && (x >= 1) = x + (sumint x-1)

我错在哪里得到这个错误?

初始错误:无限递归

单步执行代码:

sumint :: Int -> Int

嘿,类型签名。 你摇滚。

sumint x
  | x==0 = 0

一个基础案例,很酷。

  | x==1 = 1

完全没必要的情况。 好的,确定......除外。 1甚至不是为什么我们将它包含在总和中? 它应该为零(或完全删除)。

  | (x `mod` 2 == 0) && (x >= 2) = x + (sumint x-2)

这个问题的关键在这里。 X甚至很棒。 2. X是正面的,是的。 结果是x + (sumint x) - 2不!

  • 错误1:注意函数应用程序比运算符更严格,因此应该是x + sumint (x-2)

这就是堆栈溢出的原因。 sumint 2 == 2 + (sumint 2) - 2 + (sumint 2) -2 + (sumint 2) -2 + ... ,yay无限递归。

  | (x `mod` 2 /= 0) && (x >= 1) = x + (sumint x-1)

另一种情况......赔率......积极......但为什么我们加入x? 你想添加平均值,而不是赔率。 因此,在我们得到的同时解决上述问题:

  • 错误2:如果确定x是奇数,请不要添加x 只需使用sumint (x-1)

那你就没有了。 如果x不是正数,会发生什么? 你需要(另一个)案例。

| otherwise = 0

下一期:没有积累

现在的问题是你正在构建一个大的thunk(未评估的计算),而不是通过在进展时累积结果来在恒定的空间中操作。 请注意,如果我们扩展你的计算,比如6,我们得到:

sumint 6 = 6 + sumint (6-2) 
      = 6 + 4 + sumint (4-2)
      = 6 + 4 + 2 + sumint (2-2)
      = 6 + 4 + 2 + 0

你真的不希望将所有这些添加内容分开,最好传入一个累加器,例如:

sumint x = go x 0
 where
  go n accumulator
         | n <= 0    = accumulator
         | odd n     = go (n-1) accumulator
         | otherwise = go (n-2) (accumulator + n)

旁注:其他stackoverflow公民可能会提到使累加器严格,这是一个很好的形式。 我不想通过这里的讨论分散当前提问者的注意力。 注意使用优化, -O2就足够了。

惯用解决方案

以上所有解决方案都相当冗长。 使用函数和累加器迭代列表的一般操作是一种fold Fold是函数式编程中常见的众多高度优化的结构遍历之一。 在这种情况下,“严格左侧折叠”是典型的候选者(来自Data.List )。 这就是foldl' '按照惯例foldl' '( ' )意味着它是严格的而l意味着离开。

sumint n = foldl' (+) 0 [val | val <- [0..n], even val]

在这里,我们折叠在列表上以获得我们的总和。 要创建感兴趣的列表中,我们使用列表理解-首先从枚举值0..n ,并跳过不符合谓词任何价值even

我们可以通过使用步长为2的sum函数和列表推导来进一步清理它并改进它,从而只给出我们想要的平均值:

sumint n = sum [0,2..n]

它是运算符优先级问题。 在Haskell中,函数应用程序具有最高可能的优先级。 因此,当你写sumint x - 2 ,即使你将它解释为sumint (x-2) ,Haskell也将它解释为(sumint x) - 2 因此-您正在尝试定义sumint x直接来讲sumint x -这只是堆积起来的递归函数调用,直到堆栈溢出。 如果要在函数应用程序之前评估减法,则需要添加显式括号。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM