我试图使一个函数,如果收到的数字是一个有效的增值税号码,返回true,否则False。
为了使数字有效,算法是:
设s为第8位数字乘以2,第7位数字乘以3,第6位数字乘以4,第5位数字乘以5,第4位数字乘以6,第3位数字乘以7,第2位数字乘以8,第1位数字乘以9的乘积之和。设r为s除以11的整数的余数。否则,最后一位数字必须从r中减去11
所以我做了这个函数:
verify :: Int -> Bool
verify x
| r (s (x`div`10) 2 0) == 0 && x `mod` 10 == 0 = True
| r (s (x`div`10) 2 0) == 1 && x `mod` 10 == 0 = True
| x `mod` 10 == (s (x`div`10) 2 0) - 11 = True
| otherwise = False
s :: Int -> Int -> Int -> Int
s x y acc
| x `div`10 == x = (acc + x*y)
| otherwise = ((x `mod` 10)*y + s (x`div`10) (y+1) acc+((x `mod` 10)*y))
r :: Int -> Int
r x = x `mod` 11
但是当我运行验证502618418
它应该返回 True
时,但该函数始终返回 False
(对于任何数字)。我不明白为什么?
正如保罗·约翰逊在评论中提到的,当前的第一项任务是从增值税号中获取十进制数字列表。
也许将计算数字的逻辑与计算 r 和 s 的逻辑交织在一起不是一个好主意。生成的代码可能难以调试。
让我们尝试使用ghci
解释器以交互方式开发代码。
为此,我们可以先作弊一点,只需对整数使用 Show
实例即可。
$ ghci
GHCi, version 8.8.4: https://www.haskell.org/ghc/ :? for help
λ>
λ> vat1 = 502618411
λ>
λ> vat1
502618411
λ>
λ> show vat1
"502618411"
λ>
但是在Haskell中,< code>String只是< code>Char的列表,所以这与:< code>['5 ',' 0 ',' 2 ',' 6 ',' 1 ',' 8 ',' 4 ',' 1']是一回事。
λ>
λ> show vat1 == ['5', '0', '2', '6', '1', '8', '4', '1', '1']
True
λ>
我们可以使用 Data.Char
模块中的 ord
函数从字符中获取数字 ASCII 代码:
λ>
λ> import qualified Data.Char as Ch
λ> :type Ch.ord
Ch.ord :: Char -> Int
λ>
λ> map Ch.ord (show vat1)
[53,48,50,54,49,56,52,49,49]
λ>
由于从0到9的数字连续出现在ASCII序列中(从48开始),我们通过减去字符“0”的代码得到这些数字:
λ>
λ> map (\c -> (Ch.ord c) - (Ch.ord '0')) (show vat1)
[5,0,2,6,1,8,4,1,1]
λ>
所以我们有了数字列表。让我们定义函数:
λ>
λ> getDigits vat = map (\ch -> (Ch.ord ch) - (Ch.ord '0')) (show vat)
λ>
λ> getDigits vat1
[5,0,2,6,1,8,4,1,1]
λ>
但是如果我们不想使用Show实例呢?
然后我们可以使用纯数值递归函数。让我们将已经生成的数字列表,例如<code>dgs</code>,作为累加器。在ghci
中使用多行工具:
λ>
λ> :{
|λ> getDigitsR dgs n = let (q,r) = (divMod n 10)
|λ> in if (q==0) then (r : dgs)
|λ> else getDigitsR (r : dgs) q
|λ>
|λ> :}
λ>
测试:
λ>
λ> getDigitsR [9,9,9] vat1
[5,0,2,6,1,8,4,1,1,9,9,9]
λ>
λ> getDigitsR [] vat1
[5,0,2,6,1,8,4,1,1]
λ>
因此,我们可以编写getDigits
的替代定义:
λ>
λ> getDigits vat = getDigitsR [] vat
λ>
根据规范,一个数字的权重从9开始,每一步递减1。重量低于2时,求和停止。这导致根据vat号的数字计算< code>s的递归定义如下:
λ>
λ> :{
|λ> getSr w [] = 0
|λ> getSr w (dg:dgs) = if (w < 2) then 0 else (w*dg + getSr (w-1) dgs)
|λ> :}
λ>
λ>
λ> digits1
[5,0,2,6,1,8,4,1,1]
λ>
λ> getSr 9 digits1
146
λ>
这导致了计算s的这个简单函数:
λ>
λ> getS vat = getSr 9 (getDigits vat)
λ>
但是如果使用库的风格是首选呢?
人们普遍认为,随着Haskell程序员获得更多的经验,他们倾向于使用更少的直接手动递归,而更多的是内部递归库函数。
如果我们想在这里这样做,我们需要用数字压缩权重:
λ>
λ> ws = [9,8,7,6,5,4,3,2]
λ> digits = getDigits vat1
λ>
λ> zip ws digits
[(9,5),(8,0),(7,2),(6,6),(5,1),(4,8),(3,4),(2,1)]
λ>
在这一点上,我们可以做乘法并求和它们的结果:
λ>
λ> map (\(w,d) -> w*d) (zip ws digits)
[45,0,14,36,5,32,12,2]
λ>
λ> sum $ map (\(w,d) -> w*d) (zip ws digits)
146
λ>
库函数zipwith使简化成为可能:
λ>
λ> sum $ zipWith (*) ws digits
146
λ>
因此,我们可以编写< code>getS函数的替代版本:
λ>
λ> :{
|λ> getS vat = let ws = [9,8,7,6,5,4,3,2]
|λ> digits = getDigits vat
|λ> in sum $ zipWith (*) ws digits
|λ> :}
λ>
λ> getS vat1
146
λ>
注意,这种样式使处理任意权重变得更容易。
因此,我们的工作基本上已经完成:
λ>
λ> vat1
502618411
λ> getS vat1
146
λ> r = mod 146 11
λ> r
3
λ>
通过选择样式和提供类型签名,将代码变成最终形状,这是留给读者的练习。
正如标题所解释的,我有一个非常基本的编程问题,但我还没有找到。过滤掉所有(非常聪明的)“为了理解递归,你必须首先理解递归。”各种在线线程的回复我仍然不太明白。 当我们面对不知道自己不知道的事情时,我们可能会提出错误的问题或错误地提出正确的问题。我将分享我的“想法”。我的问题是希望有类似观点的人能够分享一些知识,帮助我打开递归灯泡! 以下是函数(语法用Swift编写): 我们将使用2和5作为参数:
我正在尝试在XSLT 2.0中编写一个尾递归函数,它遍历日期的多值变量并返回最早的一个。出于某种原因,我的函数未被SaxonHE9.4识别为尾递归,当输入文件有超过150-200个条目左右时,我会收到以下错误: tail\u rec\u测试第73行出错。xsl:嵌套函数调用太多。可能是由于无限递归。内置模板规则 以下是我的xml输入: 这就是我的xsl-file的样子: 如何将其转换为正确的尾部递
各种各样的书籍、文章、博客帖子表明,将递归函数重写为尾部递归函数可以加快速度。毫无疑问,对于生成斐波那契数或计算阶乘等琐碎情况,它会更快。在这种情况下,有一种典型的重写方法,即使用“辅助函数”和用于中间结果的附加参数。 尾部递归很好地描述了尾部递归函数和非尾部递归函数之间的差异,以及如何将递归函数转换为尾部递归函数。对于这种重写来说什么是重要的-函数调用的数量是相同的(重写之前/之后),不同之处在
我有一个递归函数,需要创建一个由特殊对象组成的数组... 我的自定义对象由此类填充: 这是我的递归函数: 在这个函数中,RandomBool()方法随机返回true/false。。。RandomId()也不重要。。。 问题在于“位置”数组。我希望每个项目都有特定的位置数组,例如: 对于第一步,每个项目都需要有:[0]、[1]、[2]、[3]。。。 下一步,假设我们选择了3:[3,0],[3,1],
让我们举这个例子 js编译器知道所有的函数声明,所以我可以在< code > main < code > main(second())内部调用< code>second。我不明白递归函数是如何在函数声明内部调用同一个函数的 我的思考过程是:好吧,这是函数声明,这是函数所做的,但是如何 即使声明没有完成,我也可以调用相同的函数
问题内容: 我很难弄清楚这里出了什么问题: 例如, 输出。它应该输出6,因为长度看起来像这样:5-> 16-> 8-> 4-> 2-> 1 进行一些调试后,我看到正确返回了,但是递归中出了点问题。我不太确定 谢谢你的帮助。 问题答案: 在这两个块中,进行递归调用后不会返回任何值。您需要在递归调用之前先输入,例如。如果您没有明确声明,该函数将返回。 这样可以解决此问题,但是有一种方法可以使您的代码更