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