簡體   English   中英

SICP - 階乘與命令的功能實現

[英]SICP - Imperative versus Functional implementation of factorial

我正在和Racket和Dr. Racket一起學習SICP。 我也在觀看講座:

https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-001-structure-and-interpretation-of-computer-programs-spring-2005/video-lectures/5a-assignment-狀態和-的副作用/

在第3章,作者提出了命令式編程的概念。

試圖說明其含義,他們將使用函數式編程的階乘過程的實現與使用命令式編程的實現進行對比。

Bellow你有一個使用函數式編程的迭代過程的遞歸定義:

(define (factorial-iter n)
  (define (iter n accu)
    (if (= n 0)
        accu
        (iter (- n 1) (* accu n))))
  ; (trace iter)
  (iter n 1))

在教授提出必要的實施之前,我嘗試了自己。

我使用命令“set!”到達了這段代碼:

(define (factorial-imp n count product)
  (set! product 1)
  (set! count 1)
  (define (iter count product)
    (if (> count n)
        product
        (iter (add1 count) (* product count))))
  (iter count product))

然而,教授的實施與我的命令式實施完全不同:

(define (factorial-imp-sicp n)
  (let ((count 1) (i 1))
    (define (loop)
      (cond ((> count n) i)
            (else (set! i (* count i))
                  (set! count (add1 count))
                  (loop))))
    (loop)))

這兩個代碼,我的實現和教授的代碼都達到了相同的結果。 但我不確定它們是否具有相同的性質。

因此,我開始問自己:我的實施真的很有必要嗎? 只需使用“設置!” 保證嗎?

我仍然在輔助迭代過程中使用參數,而教授的輔助迭代函數根本沒有任何參數。 這是回答我問題的核心問題嗎?

謝謝! SO用戶一直在幫助我!

你的解決方案非常瘋狂,因為它看起來勢在必行,但事實並非如此。 (下面的一些內容是特定於Racket的,但不重要。)

從您的實施開始:

(define (factorial-imp n count product)
  (set! product 1)
  (set! count 1)
  (define (iter count product)
    (if (> count n)
        product
        (iter (add1 count) (* product count))))
  (iter count product))

好吧, countproduct參數的唯一原因是為這些變量創建綁定:從不使用參數的值。 所以讓我們用let明確地做,然后我將它們最初綁定到一個未定義的對象,所以很明顯從未使用過綁定(我還重命名了內部函數的參數,所以很明顯這些是不同的綁定):

(require racket/undefined)

(define (factorial-imp n)
  (let ([product undefined]
        [count undefined])
    (set! product 1)
    (set! count 1)
    (define (iter c p)
      (if (> c n)
          p
          (iter (add1 c) (* p c))))
    (iter count product)))

好吧,好吧,現在很明顯表單的任何表達式(let ([x <y>]) (set! x <z>) ...)都可以立即替換為(let ([x <z>]) ...)只要<y>沒有任何副作用並終止。 這就是這種情況,所以我們可以重寫如下:

(define (factorial-imp n)
  (let ([product 1]
        [count 1])
    (define (iter c p)
      (if (> c n)
          p
          (iter (add1 c) (* p c))))
    (iter count product)))

好的,所以現在我們有了一些形式(let ([x <y>]) (fx)) :這可以簡單地用(f <y>)代替:

(define (factorial-imp n)
  (define (iter c p)
    (if (> c n)
        p
        (iter (add1 c) (* p c))))
  (iter 1 1))

現在很明顯,實際上,您的實現並非以任何有用的方式都是必不可少的。 它確實改變了綁定,但它只進行了一次,並且從未在突變之前使用原始綁定。 這基本上是編譯器編寫者稱之為“靜態單一賦值”的東西我認為:每個變量都被賦值一次,在分配之前不會被使用。


PS:'出色的瘋狂'並不意味着侮辱,我希望不是這樣,我很樂意回答這個問題!

set! 通過更改綁定來引入副作用,但是在不使用傳遞的值的情況下將其從傳遞的值更改為1 ,然后您永遠不會更改值,可能會看到它,就像11是傳遞給助手的常量一樣:

(define (factorial-imp n ignored-1 ignored-2)
  (define (iter count product)
    (if (> count n)
        product
        (iter (add1 count) (* product count))))
  (iter 1 1))

幫助程序通過遞歸更新它的countproduct ,因此100%正常運行。

如果你要用命令式語言做同樣的事情,那么你就可以在一個循環之外創建一個變量,你可以在循環的每一步更新,就像教授的實現一樣。

在您的版本中,您已經更改了合同。 用戶需要傳遞兩個不用於任何內容的參數。 我通過將它們稱為ignored-1ignored-2

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM