因此我们的类被赋予了将十进制数转换为八进制表示的赋值。我设法修补它直到它工作,但我有一些问题理解它的工作原理。 是否有可能以更简单的方式解释递归?感谢。
(define octify
(lambda (n)
(cond
((zero? n) 0)
((zero? (quotient n 8)) n)
(else (+ (* 10 (octify (quotient n 8))) (remainder n 8))))))
答案 0 :(得分:3)
首先,一个"数字"不是十进制或八进制。 A"数字"是一种数学概念,它以某种格式存储在计算机中,带有一堆位。十进制和八进制是指数字的不同字符串表示。也就是说,"十进制"和" octal"等只在谈论字符串时才有意义,特定的数字可以转换成十进制或八进制或其他任何字符串。
生成整数的八进制(或其他一些基础)字符串表示是编程中的常见基本任务。你已经基本弄清楚的算法:取数字的剩余部分得到最后一个数字,然后用数字递归数字的商来得到数字的其余部分(除了最后一个数字之外)。
您正在做的事情的一个奇怪之处在于您没有生成字符串,正如通常为此任务所做的那样。相反,您试图将其打包回一个数字,这样生成的数字十进制表示看起来就像原始数字的八进制表示形式。 (这恰好是可能的,因为任何八进制表示也是某个数字的有效十进制表示。例如,这对于十六进制是不可能的。)换句话说,您正在将数字转换为其八进制字符串表示,然后将该字符串解析为数字,就好像它是十进制表示一样。例如,取数字42,其十进制表示为字符串" 42"和八进制表示是字符串" 52"。您的程序返回数字52(其八进制表示形式为字符串" 64")。
您可能会因为在解释器中输入此内容而感到困惑,当您计算或打印数字时,它会输出十进制表示。但重要的是要理解一个数字与字符串完全不同。 (如果你在解释器中计算了一个字符串,也许它会用引号或其他东西包围它。)如果你的程序输出八进制表示的字符串,而不是打印时看起来像它的数字,那将是最有意义的。 / p>
答案 1 :(得分:1)
数字的主流代表,即掀起风暴的世界,是positional notation。它是与商和余数运算概念密切相关的表示,您可以从递归函数定义中看到它。那是为什么?
让我们快速说一下:位置符号不是数字的唯一可行表示。每隔一段时间出现的一种方法是计算方法,其中数字是零或一个数字。我们可以用棍棒。由于我们讨论的是程序,我们使用数据类型。
Number :== Zero
| Successor(n) where n is a number
将此读作“数字为零,或另一个数字的后继”。或者,要在支持结构化表示的方案(如Racket)中对其进行编码,我们可以写下:
(define-struct Zero ())
(define-struct Successor (n))
例如,使用此表示法表示三将为(Successor (Successor (Successor (Zero)))
。 (如果我记得正确的话,这种表示称为Peano。)
处理这种结构化数据类型的函数通常具有与数据类型本身相同的形状。也就是说,在Peano中表示的函数看起来像这样:
;; a peano-eating-function-template: peano-number -> ???
(define (a-peano-eating-function-template a-num)
(cond [(Zero? a-num)
...]
[(Successor? a-num)
...
(a-peano-eating-function-template (Successor-n a-num))
...]
其中...
将特定于您尝试在Peano数字上解决的特定问题。这是他们正在研究的数据结构之后的功能问题。作为Peano-eating功能的一个具体例子,这里有一把钢琴变成一堆星星:
;; peano->stars: peano-number -> string
;; Turn a peano in a string of stars. We are all made of stars.
(define (peano->stars a-num)
(cond [(Zero? a-num)
""]
[(Successor? a-num)
(string-append "*"
(peano->stars (Successor-n a-num)))]))
无论如何,所以数据类型自然会导致具有特定形状的函数。这导致我们回到位置符号。我们可以将位置表示法捕获为数据类型吗?
事实证明我们可以!位置表示法,例如十进制表示法,可以用类似于Peano数字描述的工作方式来描述。我们将此表示称为 Base10 ,它看起来像这样:
Base10 :== Zero
| NonZero(q, r) where q is a Base10, and r is a digit.
Digit :== ZeroD | OneD | TwoD | ... | NineD
如果我们想要用结构语言进行编程,那么
(define-struct Zero ())
(define-struct NonZero(q r))
(define-struct ZeroD ())
(define-struct OneD ())
(define-struct TwoD ())
(define-struct ThreeD ())
(define-struct FourD ())
;; ...
例如,数字四十二可以在Base10中表示为:
(NonZero (NonZero (Zero) (FourD)) (TwoD))
让人惊讶。那看起来有点......疯了。但是让我们更多地依靠这一点。和以前一样,处理Base10的函数通常具有与Base10结构相匹配的形状:
;; a-number-eating-function-template: Base10 -> ???
(define (a-number-eating-function-template a-num)
(cond
[(Zero? a-num)
...]
[(NonZero? a-num)
... (a-number-eating-function-template (NonZero-q a-num))
... (NonZero-r a-num)]))
也就是说,我们可以通过遵循Base10本身的结构,获得几乎免费在Base10上运行的递归函数的形状。
......但这是一种处理数字的疯狂方式,对吧?好吧......记住四十二:
的古怪代表(NonZero (NonZero (Zero) (FourD)) (TwoD))
这是表示相同数字的另一种方式。
((0 * 10 + 4) * 10 + 2)
几乎相同的想法。在这里,让我们摆脱几个括号。我们可以用以下符号表示四十二:
42
我们的编程语言是硬编码的,知道如何处理数字的这种表示法。
检查Zero的等价物是什么?我们知道那个。
(= n 0) ;; or (zero? n)
检查NonZero的等价物是什么?简单!
(> n 0)
NonZero-q和NonZero-r的等价物是什么?
(quotient n 10)
(remainder n 10)
然后,我们可以通过即插即用来获得递归函数的形状,这些函数使用数字输入来处理位置。
(define (a-decimal-eating-function-template n)
(cond [(= n 0)
...]
[(> n 0)
... (a-decimal-eating-function-template (quotient n 10))
... (remainder n 10)]))
看起来很熟悉? :)
有关更多信息,请参阅How to Design Programs等教科书。
答案 2 :(得分:0)
显然,对于n,(octify 0)
应为0且(octify n)
为0,使得0 <0。 n&lt; 8是n。下一个条件是复杂的。第一个问题是“(octify (quotient n 8))
返回什么?”。对于基数为10的数字,除以10将删除最右边的数字 - 145/10 = 14(假设整数除法)。对于基数为8的数字,除以8的工作方式类似。因此,假设(octify (quotient n 8))
被正确定义(我们必须假设),octify
将返回除n的最后一位以外的所有数字的数字。现在,我们需要取这个数字并向左“推”一个数字。将它乘以10可以做到这一点,因为打印机将在10-base打印它。 (remainder n 8)
得到n的最后一位数。现在我们有第一个数字(最后一个数字为零)和最后一个数字,所以我们可以通过添加它们来组合它们。此时,函数已完成,并返回正确的值。