简体   繁体   English

Haskell整数奇数位检查器

[英]Haskell Integer Odd Digits Checker

I seem to be stuck on a question and have no idea how to approach it or what Im doing wrong with my current code. 我似乎陷入了一个问题,不知道如何处理它或我当前的代码做错了什么。

I have to write a function called oddDigits which takes a single integer argument and returns a boolean result. 我必须编写一个名为oddDigits的函数,它接受一个整数参数并返回一个布尔结果。 It should return True if and only if the argument is a positive integer with an odd number of digits. 当且仅当参数是具有奇数位数的正整数时,它应该返回True。 If the argument is zero or negative, the function should stop with an error message. 如果参数为零或负数,则函数应该以错误消息停止。

Also, cant convert the argument into a string. 此外,无法将参数转换为字符串。 Have to use recursion. 必须使用递归。 I have a feeling each digit could be stored in a list recursively and then the length of the list could determine the answer. 我感觉每个数字可以递归地存储在列表中,然后列表的长度可以确定答案。

So far, I have this: 到目前为止,我有这个:

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 

Sorry if the code looks horrible. 对不起,如果代码看起来很糟糕。 I am new to Haskell and still learning. 我是Haskell的新手,还在学习。 What exactly am I doing wrong and how could I correct it? 我究竟做错了什么,我怎么能纠正它?

First off, this seems a pretty weird thing to check. 首先,这看起来很奇怪。 Perhaps what you're doing wrong is to ever consider this problem... 或许你做错了就是要考虑这个问题......

XKCD维基百科打印机

But if you persist you want to know the property of an integer having an odd number of digits ... oh well. 但是如果你坚持下去,你想知道一个奇数位数的整数的属性......哦。 There's a lot that could be improved. 有很多可以改进的地方。 For starters, (x < 0) || (x == 0) 首先, (x < 0) || (x == 0) (x < 0) || (x == 0) doesn't need the parentheses – < and == (infix 4) bind more tightly than || (x < 0) || (x == 0)不需要括号 - <== (中缀4)绑定比||更紧密 . If you're not sure about this, you can always ask GHCi: 如果您对此不确定,可以随时询问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 ||

But here you don't need || 但在这里你不需要|| anyway because there's a dedicated operator for less-than-or-equal. 无论如何,因为有一个小于或等于的专用运算符。 Hence you can just write 因此你可以写

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

Then, you can “convert” the number to a string. 然后, 可以将数字“转换”为字符串。 Converting to string is generally a really frowned-upon thing to do because it throws all structure, typechecking etc. out of the window; 转换为字符串通常是一个非常令人讨厌的事情,因为它会抛出窗口中的所有结构,类型检查等; however “number of digits” basically is a property of a string (the decimal expansion), rather than a number itself, so this is not entirely unsensible for this specific task. 但“数字位数”基本上字符串的属性(十进制扩展),而不是数字本身,因此对于此特定任务而言,这并非完全不可见。 This would work: 这可行:

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

however it's a bit redundancy department redundant. 然而,这是一个多余的冗余部门。 You're checking if something is True , then give True as the result... why not just put it in one clause: 你正在检查某些东西是否为True ,然后给出True作为结果......为什么不把它放在一个子句中:

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

That's perhaps in fact the best implementation. 这可能实际上是最好的实现。

For any proper , sensible task, I would not recommend going the string route. 对于任何正确 ,明智的任务,我不建议去字符串路线。 Recursion is better. 递归更好。 Here's what it could look like: 这是它的样子:

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

There's nothing wrong with your general approach of converting to a list of digits, then finding the length of the list. 您转换为数字列表,然后查找列表长度的一般方法没有任何问题。 Really where you went wrong is trying to cram everything into one function. 真正出错的地方是试图把所有东西塞进一个功能。 As you found out first hand, it makes it very difficult to debug. 正如您第一手发现的那样,它使调试变得非常困难。 Functional programming works best with very small functions. 功能编程最适合非常小的功能。

If you separate out the responsibility of converting an integer to a list of digits, using a digs function like the one from this answer , the rest of your algorithm simplifies to: 如果你将整数转换为数字列表的责任分开,使用像这个答案digs函数,你的算法的其余部分简化为:

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

leftaroundabout's eventual answer is very nice, however it fails for numbers like 2,3 and 23. Here's a fix. leftaroundabout的最终答案是非常好的,但它对2,3和23这样的数字失败了。这是一个修复。

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

Its much more elegant than my initial answer, below. 它比我最初的答案要优雅得多,如下。 I'm including it to introduce a common functional paradigm, a worker/wrapper transformation of the problem. 我将它包括在内,介绍一个常见的功能范例,即问题的工人/包装转换。 Here the wrapper gives the interface and passes off the work to another function. 这里的包装器提供了接口,并将工作传递给另一个函数。 Notice that the negativity check only needs to be done once now. 请注意,否定性检查现在只需要进行一次。

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' carries a piece of internal data with it, the initial Bool. oddDigits'带有一个内部数据,最初的Bool。 My first first thought was to have that Bool be a digit accumulator, counting the number of digits. 我的第一个想法是让Bool成为一个数字累加器,计算数字位数。 In that case, an "unwrapper" needs to be supplied, in this case the standard "odd" function: 在这种情况下,需要提供“unwrapper”,在这种情况下是标准的“奇数”功能:

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

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

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

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