[英]SICP Exercise 2.5 - How to represent negative numbers?
我目前正在閱讀 SICP,並在練習 2.5:
練習 2.5。 .證明如果我們將a和b表示為乘積的整數,我們可以僅使用數字和算術運算來表示非負整數對。 給出過程
cons
、car
和cdr
的相應定義。
我找到了一個代碼:
(define (my-cons a b)
(* (expt 2 a) (expt 3 b)))
(define (my-car x)
(define (car-iter x count)
(if (= 0 (remainder x 2))
(car-iter (/ x 2) (+ 1 count))
count))
(car-iter x 0))
(define (my-cdr x)
(define (cdr-iter x count)
(if (= 0 (remainder x 3))
(cdr-iter (/ x 3) (+ 1 count))
count))
(cdr-iter x 0))
我的問題是:如果需要將“非負整數”的要求更改為“接受負整數和非負整數”怎么辦?
例子 :
> (define x (my-cons 2 -5))
> (my-car x)
2
> (my-cdr x)
-5
如何修改代碼? 我想不通。
謝謝你。 願你有一個美好的一天。
正如 amalloy 在評論中所說,這確實是一個數學問題。 這種編碼之所以有效是因為算術的基本定理,即任何正自然數都有一個唯一的素數因式分解:每個正自然數都可以唯一地表示為多個素數的乘積,其中特別是 1 是乘積沒有素數。
因此,您可以使用一個或多個額外的素數對整數的符號進行編碼(實際上,您只需要一個,因為您可以將事物寫為 2^a3^b5^s 其中 s 是 [0, 3] 對兩個元素的符號進行編碼)。
另一種方法是簡單地使用現有的表示,但將整數映射到自然數。 這很好,因為它是一個實際證明,沒有比自然數更多的整數。 這樣的地圖可能是:
很容易看出這是一對一的對應關系,而且 0 映射到 0 (這使得 0 成為nil
的一個很好的值)。
這是這些地圖,寫在(對不起)打字球拍,因為我正在努力弄清楚我是否可以使用它。
(define (Z->N (i : Integer)) : Natural
;; map an integer to a natural
(if (>= i 0)
(* i 2)
(- (* (- i) 2) 1)))
(define (N->Z (n : Natural)) : Integer
;; map the naturals into the integers
(let-values ([(q r) (quotient/remainder n 2)])
(if (zero? r)
q
(- (- q) 1))))
現在你的實現還有另一個問題:它會很高興地處理不是 2^a3^b 形式的數字,例如任何具有其他主要因素的數字。 處理這個問題的方法是在提取冪時檢查數字是否具有該形式:實際上這意味着檢查數字是否為 2^a*3^b*1 的形式。
因此,下面的代碼會執行此操作,以及對上面的整數進行編碼。 這又是在類型化的 Racket 中(對不起,再次),它還使用了多個值,並且可能還使用了僅存在於 Racket 中的其他一些東西。
(define (defactor (n : Natural) (p : Natural)) : (Values Natural Natural)
;; Given n and a factor p, return m where p does not divide m,
;; and j, the number of factors of p removed (so n = m*p^j)
(let df-loop ([m : Natural n]
[j : Natural 0])
(let-values ([(q r) (quotient/remainder m p)])
(if (zero? r)
(df-loop q (+ j 1))
(values m j)))))
(define (kar&kdr (k : Positive-Integer)) : (Values Integer Integer)
;; Given something which should be a kons, return its kar & kdr.
;; If it is not a kons signal an error
(let*-values ([(k2 encoded-kar) (defactor k 2)]
[(k23 encoded-kdr) (defactor k2 3)])
(unless (= k23 1)
(error 'kar&kdr "not a cons"))
(values (N->Z encoded-kar) (N->Z encoded-kdr))))
(define (kons (the-kar : Integer) (the-kdr : Integer)) : Positive-Integer
(* (expt 2 (Z->N the-kar))
(expt 3 (Z->N the-kdr))))
(define (kar (the-kons : Positive-Integer)) : Integer
(let-values ([(the-kar the-kdr) (kar&kdr the-kons)])
the-kar))
(define (kdr (the-kons : Positive-Integer)) : Integer
(let-values ([(the-kar the-kdr) (kar&kdr the-kons)])
the-kdr))
我們可以更進一步,定義一個空列表的表示,它將是0
和一種制作列表的方式:
;;; since 2^a3^b is never zero, 0 is a good candidate for the empty
;;; list: 'kill' is a pun on 'nil'.
;;;
(define kill : Zero 0)
;;; And now we can write some predicates and a version of list.
;;; (kist 1 2 3) takes a very, very long time.
;;;
(define (kill? (x : Natural)) : Boolean
(zero? x))
(define (kons? (x : Natural)) : Boolean
(not (kill? x)))
(define (kist . (l : Integer *)) : Natural
(let kist/spread ((lt l))
(if (null? lt)
kill
(kons (first lt) (kist/spread (rest lt))))))
現在
> (define d (kons 123 -456))
> d
- : Integer [more precisely: Nonnegative-Integer]
51385665200410193914365219310409629004573395973849642473134969706165383608831740620563388986738635202925909198851954060195023302783671526117732269828652603388431987979605951272414330987611274752111186624164906143978901704325355283206259678088536996807776750955110998323447711166379786727609752016045005681785186498933895920793982869940159108073471074955985333560653268614500306816876936016985137986665262182684386364851688838680773491949813254691225004097103180392486216812280763694296818736638062547181764608
> (kar d)
- : Integer
123
> (kdr d)
- : Integer
-456
> (kdr (+ d 1))
kar&kdr: not a cons [,bt for context]
如果您嘗試計算,例如(kist 1 2 3)
,它將花費非常非常長的時間。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.