Найти корень методом Ньютона
Я пишу newton-method
найти корень из примера схемы в elisp как
#+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? (Думаю, закрытие недооценивается)
2 ответа
Несколько вызовов функций можно упростить, и мы должны реализовать совет @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-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
. Лисп - это не схема.
PPS.close-enoughp
должно быть close-enough-p
.