Haskell Integer Odd Digits Checker

时间:2017-01-27 00:14:43

标签: function haskell recursion

我似乎陷入了一个问题,并且不知道如何处理它或我当前的代码做错了什么。

我必须编写一个名为oddDigits的函数,它接受一个整数参数并返回一个布尔结果。当且仅当参数是具有奇数位数的正整数时,它应该返回True。如果参数为零或负数,则函数应该以错误消息停止。

此外,无法将参数转换为字符串。必须使用递归。 我感觉每个数字可以递归地存储在列表中,然后列表的长度可以确定答案。

到目前为止,我有这个:

oddDigits :: Integer -> Bool 

lst = [] 

oddDigits x 
    | (x < 0) || (x == 0) = error 
    | x `mod` 10 ++ lst ++ oddDigits(x `div` 10) 
    | length(lst) `mod` 2 /= 0 = True
    | otherwise = False 

很抱歉,如果代码看起来很糟糕。我是Haskell的新手,还在学习。我究竟做错了什么,我怎么能纠正它?

3 个答案:

答案 0 :(得分:4)

首先,这看起来很奇怪。或许你做错了就是考虑这个问题......

XKCD Wikipedia printer

但是如果你坚持下去,你想知道一个奇数位数的整数的属性......哦。还有很多可以改进的地方。对于初学者,(x < 0) || (x == 0)不需要括号 - <==(中缀4)比||更紧密地绑定。如果您对此不确定,可以随时询问GHCi:

Prelude> :i ==
class Eq a where
  (==) :: a -> a -> Bool
  ...
    -- Defined in ‘GHC.Classes’
infix 4 ==
Prelude> :i ||
(||) :: Bool -> Bool -> Bool    -- Defined in ‘GHC.Classes’
infixr 2 ||

但是在这里你不需要||,因为那里有一个小于或等于的专用运营商。因此你可以写

oddDigits x 
  | x <= 0  = error "bla bla"
  | ...

然后,you can将数字“转换”为字符串。转换为字符串通常是一个非常令人讨厌的事情,因为它会抛出窗口中的所有结构,类型检查等;但是“位数”基本上字符串的属性(十进制扩展),而不是数字本身,所以这对于这个特定任务来说并不完全不可见。这可行:

oddDigits x 
 | x <= 0                      = error "blearg"
 | length (show x)`mod`2 /= 0  = True
 | otherwise                   = False 
然而,它有点冗余部门是多余的。您正在检查某些内容是否为True,然后将True作为结果...为什么不将其放在一个子句中:

oddDigits x 
 | x <= 0     = error "blearg"
 | otherwise  = length (show x)`mod`2 /= 0

这可能实际上是最好的实施方式。

对于任何正确的,明智的任务,我不建议去字符串路由。递归更好。这就是它的样子:

oddDigits 1 = True
oddDigits x 
 | x <= 0     = error "blearg"
 | otherwise  = not . oddDigits $ x`div`10

答案 1 :(得分:3)

您转换为数字列表,然后查找列表长度的一般方法没有任何问题。真正出错的地方是试图把所有东西塞进一个功能。正如您第一手发现的那样,它使调试变得非常困难。函数式编程最适合非常小函数。

如果您将整数转换为数字列表的责任分开,使用digs函数(如this answer中的函数),则算法的其余部分将简化为:

oddDigits x | x <= 0 = error
oddDigits x = odd . length $ digs x

答案 2 :(得分:0)

leftaroundabout的最终答案非常好,但是对于像2,3和23这样的数字它是失败的。这是一个修复。

oddDigits x 
  | x <= 0     = error "blearg"
  | x < 10     = True
  | otherwise  = not . oddDigits $ x`div`10

它比我最初的答案要优雅得多,如下。我将它包括在内,介绍一个常见的功能范例,即问题的工人/包装转换。这里的包装器提供了接口,并将工作传递给另一个函数。请注意,否定性检查现在只需要进行一次。

oddDigits :: Integer -> Bool
oddDigits x
   | x <= 0 = False
   | otherwise = oddDigits' True x

oddDigits' :: Bool -> Integer -> Bool
oddDigits' t x
   | x < 10 = t
   | otherwise = oddDigits' (not t) $ x `div` 10

oddDigits'带有一条内部数据,最初的Bool。我的第一个想法是让Bool成为一个数字累加器,计算数字位数。在这种情况下,需要提供“unwrapper”,在这种情况下是标准的“奇怪”功能:

oddDigits x
   | x <= 0 = False
   | otherwise = odd . oddDigits'' 1  $  x

其中oddDigits'' :: Integer -> Integer -> Integer