Решение I - прямая рекурсия
Однако проблема заключается в функции absolute
в конце всей формулы.
Для того чтобы это было выполнено в самом окончательном варианте для всей суммы, вы должны поместить свою функцию во внешнюю функцию и применить abs
к вызову функции:
(define (signed-area plist)
(define (.signed-area plist)
(cond ((< (length plist) 3) 0)
(else (+
(/ (- (* (first plist) (fourth plist))
(* (second plist) (third plist)))
2)
(.signed-area (cddr plist))))))
(abs (.signed-area plist))) ;; final absolute
Вместо деления на для каждого слагаемого вы можете сделать это также в
самый конец на всю абсолютную сумму. Итак, очень очень минимально более эффективный - но в практике совершенно незначительное улучшение.
(define (signed-area plist)
(define (.signed-area plist)
(cond ((< (length plist) 3) 0)
(else (+
(- (* (first plist) (fourth plist))
(* (second plist) (third plist)))
(.signed-area (cddr plist))))))
(* (/ 1 2) (abs (.signed-area plist))))
Решение II - рекурсия хвостового вызова
Это метод, который экономит память и позволяет избежать вложения рекурсивных вызовов для интерпретатора. Так что обычно считается лучшей практикой.
Кроме того, вы можете более четко видеть, что происходит между этапами - потому что
вам нужно просто сконцентрироваться на acc
и на том, что с ним делается на каждом шаге рекурсии, - тогда вы понимаете формулу / процедуру, примененную к результатам каждого отдельного шага. Из-за этих двух причин рекурсивный вызов хвоста является предпочтительным методом для lisp-программистов для формулирования рекурсивных функций.
(define (signed-area plist)
(define (.signed-area plist acc) ; introduce accumulator
(cond ((< (length plist) 3) (* (/ 1 2) (abs acc)))
(else (.signed-area (cddr plist) ; in next round remove first pair
(+ (- (* (first plist) (fourth plist))
(* (second plist) (third plist)))
acc))))) ; add shoelace product to accumulator
(.signed-area plist 0)) ; call inner recursive function with accumulator start value 0
;
Test
Все три определения функций дают правильный результат 2
:
(signed-area (list 1 2 3 4 5 6))
;; => 2
Для проверки / тестирования рассчитайте вручную пример:
;; to check, calculate shoelace by hand:
;; visualize:
;; 1 2
;; 3 4
;; 5 6
;
;; and then calculate:
(* (/ 1 2)
(abs (+ (- (* 1 4)
(* 2 3))
(- (* 3 6)
(* 4 5)))))
;; => 2
Для списка рассылки
(define (signed-area list-of-posn)
(define (.signed-area list-of-posn acc) ; introduce accumulator
(cond ((< (length list-of-posn) 2) (* (/ 1 2) (abs acc)))
(else (.signed-area (cdr list-of-posn) ; in next round remove first posn
(+ (- (* (posn-x (first list-of-posn)) (posn-y (second list-of-posn)))
(* (posn-x (second list-of-posn)) (posn-y (first list-of-posn))))
acc))))) ; add shoelace product to accumulator
(.signed-area list-of-posn 0)) ; call inner recursive function with accumulator start value 0