用牛顿法求根
Find root with Newton's method
我写 newton-method
以在 elisp 中从 Scheme 示例中找到根作为
#+begin_src emacs-lisp :session sicp :lexical t
(defun deriv(g)
(lambda (x)
(/ (- (funcall g (+ x dx)) (funcall g x))
dx)))
(defvar dx 0.00001)
(defvar tolerance 0.00001)
(defun fixed-point(f guess)
(defun close-enoughp(v1 v2)
(< (abs (- v1 v2)) tolerance))
(let ((next (funcall f guess)))
(if (close-enoughp guess next)
next
(fixed-point f next))))
(defun newton-transform(g)
(lambda (x)
(- x (/ (funcall g x) (funcall (funcall #'deriv g) x)))))
(defun newton-method(g guess)
(fixed-point (funcall #'newton-transform g) guess))
(defun curt(x)
(newton-method (lambda (y) (- (* y y y) x))
1.0))
(curt 12)
#+end_src
#+RESULTS:
: 2.2894284851069058
它有效,但观察扭曲的代码:
(defun newton-transform(g)
(lambda (x)
(- x (/ (funcall g x) (funcall (funcall #'deriv g) x)))))
三个 funcall
s,如果闭包更深,我无法想象其中的坏处。
对于 elisp 的问题是否有替代解决方案? (我想它贬低了闭包)
在newton-transform
中,(funcall #'deriv g)
等同于(deriv g)
,所以可以去掉3个funcall
中的一个。其他 2 个确实是必需的。
newton-method
相同:将 (funcall #'newton-transform g)
替换为 (newton-transform g)
。
PS。我强烈建议将 defun close-enoughp
移出 defun fixed-point
或将其变成 cl-flet
。 Lisp 不是 Scheme。
PPS。 close-enoughp
应该是 close-enough-p
.
可以简化一些函数调用,我们应该执行@sds 关于函数名称和约定的建议 - 如下所示:
(defvar dx 0.00001)
(defvar tolerance 0.00001)
(defun deriv (g)
(lambda (x)
(/ (- (funcall g (+ x dx)) (funcall g x))
dx)))
(defun close-enough-p (v1 v2)
(< (abs (- v1 v2)) tolerance))
(defun try (f guess)
(let ((next (funcall f guess)))
(if (close-enough-p guess next)
next
(try f next))))
(defun fixed-point (f first-guess)
(try f first-guess))
(defun newton-transform (g)
(lambda (x)
(- x (/ (funcall g x)
(funcall (deriv g) x)))))
(defun newton-method (g guess)
(fixed-point (newton-transform g) guess))
(defun curt (x)
(newton-method (lambda (y) (- (* y y y) x))
1.0))
注意我们在调用之前定义和命名的函数时不需要使用funcall
,例如deriv
和newton-transform
.
我写 newton-method
以在 elisp 中从 Scheme 示例中找到根作为
#+begin_src emacs-lisp :session sicp :lexical t
(defun deriv(g)
(lambda (x)
(/ (- (funcall g (+ x dx)) (funcall g x))
dx)))
(defvar dx 0.00001)
(defvar tolerance 0.00001)
(defun fixed-point(f guess)
(defun close-enoughp(v1 v2)
(< (abs (- v1 v2)) tolerance))
(let ((next (funcall f guess)))
(if (close-enoughp guess next)
next
(fixed-point f next))))
(defun newton-transform(g)
(lambda (x)
(- x (/ (funcall g x) (funcall (funcall #'deriv g) x)))))
(defun newton-method(g guess)
(fixed-point (funcall #'newton-transform g) guess))
(defun curt(x)
(newton-method (lambda (y) (- (* y y y) x))
1.0))
(curt 12)
#+end_src
#+RESULTS:
: 2.2894284851069058
它有效,但观察扭曲的代码:
(defun newton-transform(g)
(lambda (x)
(- x (/ (funcall g x) (funcall (funcall #'deriv g) x)))))
三个 funcall
s,如果闭包更深,我无法想象其中的坏处。
对于 elisp 的问题是否有替代解决方案? (我想它贬低了闭包)
在newton-transform
中,(funcall #'deriv g)
等同于(deriv g)
,所以可以去掉3个funcall
中的一个。其他 2 个确实是必需的。
newton-method
相同:将 (funcall #'newton-transform g)
替换为 (newton-transform g)
。
PS。我强烈建议将 defun close-enoughp
移出 defun fixed-point
或将其变成 cl-flet
。 Lisp 不是 Scheme。
PPS。 close-enoughp
应该是 close-enough-p
.
可以简化一些函数调用,我们应该执行@sds 关于函数名称和约定的建议 - 如下所示:
(defvar dx 0.00001)
(defvar tolerance 0.00001)
(defun deriv (g)
(lambda (x)
(/ (- (funcall g (+ x dx)) (funcall g x))
dx)))
(defun close-enough-p (v1 v2)
(< (abs (- v1 v2)) tolerance))
(defun try (f guess)
(let ((next (funcall f guess)))
(if (close-enough-p guess next)
next
(try f next))))
(defun fixed-point (f first-guess)
(try f first-guess))
(defun newton-transform (g)
(lambda (x)
(- x (/ (funcall g x)
(funcall (deriv g) x)))))
(defun newton-method (g guess)
(fixed-point (newton-transform g) guess))
(defun curt (x)
(newton-method (lambda (y) (- (* y y y) x))
1.0))
注意我们在调用之前定义和命名的函数时不需要使用funcall
,例如deriv
和newton-transform
.