Functional programming SICP-阶乘的命令式与功能性实现

Functional programming SICP-阶乘的命令式与功能性实现,functional-programming,lisp,racket,sicp,imperative-programming,Functional Programming,Lisp,Racket,Sicp,Imperative Programming,我正在与Racket和Dr.Racket一起研究SICP书。我还观看了关于以下内容的讲座: 在第三章中,作者提出了命令式编程的概念 为了说明其含义,他们将使用函数式编程的阶乘过程实现与使用命令式编程的阶乘过程实现进行了对比 下面是使用函数式编程的迭代过程的递归定义: (define (factorial-iter n) (define (iter n accu) (if (= n 0) accu (iter (- n 1) (* accu n))))

我正在与Racket和Dr.Racket一起研究SICP书。我还观看了关于以下内容的讲座:

在第三章中,作者提出了命令式编程的概念

为了说明其含义,他们将使用函数式编程的阶乘过程实现与使用命令式编程的阶乘过程实现进行了对比

下面是使用函数式编程的迭代过程的递归定义:

(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-sicp n)
  (let ((count 1) (i 1))
    (define (loop)
      (cond ((> count n) i)
            (else (set! i (* count i))
                  (set! count (add1 count))
                  (loop))))
    (loop)))
(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)))
(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))
嗯,
count
product
参数的唯一原因是为这些变量创建绑定:这些参数的值永远不会被使用。让我们用
let
显式地实现这一点,我将首先将它们绑定到一个未定义的对象,这样就可以清楚地看到绑定从未被使用过(我还将参数重命名为内部函数,因此很明显这些是不同的绑定):

好的,现在很明显,只要
没有副作用并且终止,任何形式的
(let([x])(set!x)
的表达式都可以立即被
(let([x]))
替换。这里就是这种情况,因此我们可以将上述内容改写如下:

(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])(fx))
:这可以被
(f)
代替:

现在很明显,您的实现在任何有用的方面实际上都不是必需的。它确实会变异绑定,但只会变异一次,并且在变异之前从不使用原始绑定。我认为,这本质上是编译器编写者称之为“静态单赋值”的东西:每个变量只被赋值一次,在赋值之前不被使用



PS:“精彩的疯狂”并不是一种侮辱,我希望它没有被认为是这样,我喜欢回答这个问题

使用
设置
通过更改绑定引入了副作用,但是您可以在不使用传递的值的情况下将其从传递的值更改为
1
,并且此后您永远不会更改该值。您可能会将其视为
1
1
是传递给助手的常量,如下所示:

(define (factorial-imp n ignored-1 ignored-2)
  (define (iter count product)
    (if (> count n)
        product
        (iter (add1 count) (* product count))))
  (iter 1 1))
助手通过递归更新其
计数
产品
,因此100%可用

如果您在命令式语言中也这样做,那么您将在循环外创建一个变量,并在循环的每一步进行更新,就像实现一样


在你的版本中,你修改了合同。用户需要传递两个不用于任何用途的参数。我已经说明,通过称它们为
ignored-1
ignored-2

我对如何解释这一评论感到怀疑。。。既然你没有投票支持这个问题,我认为这是一种侮辱。但是谢谢,没问题。很好的回答!=)@抱歉,我现在记不起投票了。
(define (factorial-imp n ignored-1 ignored-2)
  (define (iter count product)
    (if (> count n)
        product
        (iter (add1 count) (* product count))))
  (iter 1 1))