Scheme 此方案代码如何返回值?

Scheme 此方案代码如何返回值?,scheme,racket,sicm,Scheme,Racket,Sicm,此代码取自Sussman和Wisdom对经典力学的结构和解释,其目的是推导主机支持的最小正浮点。 在DrRacket中运行它会在我的机器上生成2.220446049250313e-016 我的问题是,是什么导致它甚至返回一个值?这段代码是尾部递归的,在某些情况下计算机无法再除以2是有意义的。为什么它不扔 (define *machine-epsilon* (let loop ((e 1.0)) (if (= 1.0 (+ e 1.0)) (* 2 e)

此代码取自Sussman和Wisdom对经典力学的结构和解释,其目的是推导主机支持的最小正浮点。

在DrRacket中运行它会在我的机器上生成2.220446049250313e-016

我的问题是,是什么导致它甚至返回一个值?这段代码是尾部递归的,在某些情况下计算机无法再除以2是有意义的。为什么它不扔

(define *machine-epsilon*
  (let loop ((e 1.0))
     (if (= 1.0 (+ e 1.0))
         (* 2 e)
         (loop (/ e 2)))))
*machine-epsilon*
这段代码是尾部递归的,在某些情况下计算机无法再除以2是有意义的。为什么它不扔

(define *machine-epsilon*
  (let loop ((e 1.0))
     (if (= 1.0 (+ e 1.0))
         (* 2 e)
         (loop (/ e 2)))))
*machine-epsilon*

不,想法是不同的:在某些点上,计算机仍然可以除以2,但结果e与0无法区分[upd:仅在浮点加法的上下文中-注释中提到的非常好的点]e+1.0=1.0,这正是假设子句正在检查的结果。我们可以肯定地知道,从机器的角度来看,前面的e仍然大于零,否则我们将无法到达当前的执行点,因此我们只返回e*2。

当您摆脱混乱的命名let符号时,它会变得更清晰

(define (calculate-epsilon (epsilon 1.0))
  (if (= 1.0 (+ 1.0 epsilon))
      (* epsilon 2)
      (calculate-epsilon (/ epsilon 2))))

(define *machine-epsilon* (calculate-epsilon))
这就是代码的实际功能。 现在我们来看一下命名let表达式的优点。 它在本地定义函数并运行它。只是函数名为loop是非常不精确和混乱的,将epsilon命名为e是一个非常不愉快的选择。命名对于可读代码来说是最重要的

所以这个SICP的例子应该是一个错误命名选择的例子。好吧,也许他们这样做是为了训练学生

命名let定义并调用/运行函数/过程。避免它将导致更好的代码-因为更清晰

在通用lisp中,这样的构造将更清楚地表达:

(defparameter *machine-epsilon*
  (labels ((calculate-epsilon (&optional (epsilon 1.0))
              (if (= 1.0 (+ 1.0 epsilon))
                  (* epsilon 2)
                  (calculate-epsilon (/ epsilon 2)))))
    (calculate-epsilon)))
在CLISP实现中,这给出了:1.1920929E-7

这是递归的语法糖

您可以避免使用太多语法,直到您掌握了该语言,并尽可能多地使用内核语言编写代码,以关注基本问题。例如,在完整的SICP文本中,从未指定此语法糖用于迭代


r6rs对迭代的定义是。

此代码的目的不是找到机器可以支持的最小浮点:它是找到最小浮点,epsilon,使得=+1.0 epsilon 1.0为false。这个数字很有用,因为它是加法误差的上界,特别是你知道,+xy在[x+y*1-ε,x+y*1+ε]范围内,在第二个表达式中,+&c表示对数字的理想运算

特别地,/*机器epsilon*2是一个非常精细的数字,例如,/*机器epsilon*10000,对于许多合理的x值,*/*机器epsilon*x将非常接近*机器epsilon*。这只是=+/*机器epsilon*2 1.0 1.0是真的

我对浮点标准不太熟悉,但您可能想到的数字是Common Lisp称之为最小正双浮点或其变体的数字。在球拍中,你可以通过

(define *least-positive-mumble-float*
  ;; I don't know what float types Racket has if it even has more than one.
  (let loop ([t 1.0])
    (if (= (/ t 2) 0.0)
        t
        (loop (/ t 2)))))

我不确定这是否允许引发异常:它在实践中不允许,并且它得到了一个合理的答案。

如果测试求值为true,它将返回*2e。如果您将命名let重写为一个单独的函数,而不是循环,您可能会看得更清楚。*问题中定义的机器epsilon*是一个数字,它也是一个全局变量。@tfb是的,定义*machine epsilon*。。。是一个变量定义。我的意思是叫let的。。。构造模糊了两件事:函数定义及其调用。但事实上,在我开始写作的时候,我很困惑好的,我删除了一个很大的部分。e没有变得与0无法区分:+1.0 e变得与1.0无法区分,这是一件非常不同的事情。@tfb问题不是为什么if子句有效,对吗?:但是说真的,你是绝对正确的-=1.0+e1.0并不意味着=e0