Reputation: 2824
I am trying to create a function that computes this using apply and mapcar.
I am stuck after using the first mapcar to get all the differences of p-q in a list. How do I square all the elements in the list and sum them up?
(defun euclidean-distance-map (p q)
;; get a list of differences of p - q
(mapcar #'- p q))
Upvotes: 0
Views: 697
Reputation: 60004
If you are really required to stick to HOFs (reduce
& mapcar
), then here are a few options:
(defun euclidean-distance-map (p q)
(let ((d (mapcar #'- p q))) ; get a list of differences of p & q
(sqrt (reduce #'+ (mapcar #'* d d)))))
(defun euclidean-distance-map (p q)
(sqrt (reduce #'+ (mapcar (lambda (x) (* x x)) (mapcar #'- p q)))))
(defun euclidean-distance-map (p q)
(sqrt (reduce #'+ (mapcar (lambda (x y)
(let ((d (- x y)))
(* d d)))
p q))))
apply
vs reduce
Using apply
instead of reduce
is a bad idea (both because of call-arguments-limit
and stylistically), but here you go:
(defun euclidean-distance-map (p q)
(let ((d (mapcar #'- p q))) ; get a list of differences of p & q
(sqrt (apply #'+ (mapcar #'* d d)))))
(defun euclidean-distance-map (p q)
(sqrt (apply #'+ (mapcar (lambda (x) (* x x)) (mapcar #'- p q)))))
(defun euclidean-distance-map (p q)
(sqrt (apply #'+ (mapcar (lambda (x y)
(let ((d (- x y)))
(* d d)))
p q))))
Without the proverbial "sufficiently smart compiler", mapcar
allocates storage which is immediately discarded.
This may not necessarily be a problem with a modern generational GC though.
Note that the iterative version using loop
is no less clear:
(defun euclidean-distance-map (p q)
(sqrt (loop for x in p
and y in q
for d = (- x y)
sum (* d d))))
Lisp is a multi-paradigm language, you do not have to force yourself into a specific framework.
Upvotes: 7
Reputation: 522
Here's a variation that uses higher-order functions and doesn't allocate a list for the differences:
(defun euclidian-distance (u v)
(let ((sum 0.0))
(mapc (lambda (x y)
(let ((d (- x y)))
(incf sum (* d d))))
u
v)
(sqrt sum)))
Upvotes: 1
Reputation: 2386
coredump- actually answered it, but let's elaborate a bit. The problem can be split into the following tasks/steps (from the outermost to innermost):
(sqrt .)
.
, we need to sum up squared differences. Here apply
will do the work. We want (+ a1 a2 a3 ...)
, the problem is we don't know how many of the items we want to add. Assuming these items can be put into the list, can do (apply #'+ .)
. So, up to now we have (sqrt (apply #'+ .))
.mapcar
: (mapcar (lambda (x) (* x x)) .)
.(mapcar #'- u v)
.Overall,
(defun euclidian-distance (u v)
(sqrt (apply #'+ (mapcar (lambda (x) (* x x)) (mapcar #'- u v)))))
> (euclidian-distance '(1 4 3) '(1 1 -1))
> 5.0
Upvotes: 2