问题
I'm currently trying to write a function from a list of points that returns the distance from a point p to a point in my point list that is farthest away from p. My list of points are the following:
((2 . 4) (3 . 6) (5 . 12) (-4 . 3) (8.4 . 9) (0 . -1))
I have also made some abstractions to retrieve the general car and cdr (for easier visibility in the code), as well as the car and cdr of the list itself.
(define (get-x p) (car p)
(define (get-y p) (car p)
(define (get-first-point pt-list)
(get-x pt-list))
(define (get-rest-points pt-list)
(get-y pt-list))
I have also already written out a generalized distance formula that I can use for any two points that I choose (keep in mind that I abstracted car and cdr to be get-x and get-y respectively).
(define (distance a b)
(if (or (null? a) (null? b))
0
(sqrt (+ (expt (- (get-x a)
(get-x b)) 2)
(expt (- (get-y a)
(get-y b)) 2)))))
Now that I have this, I know that I need to compare the various distances throughout my entire set of points and choose the maximum return value. I have a partial solution, but not one that's correct for every point.
(define (max-distance p pt-list)
(if (null? pt-list)
0
(max (distance p (get-first-point pt-list)) ; 1
(distance p (get-first-point (get-rest-points pt-list))) ; 2
(distance p (get-first-point (get-rest-points (get-rest-points pt-list)))) ; 3
(distance p (get-first-point (get-rest-points (get-rest-points (get-rest-points pt-list))))) ; 4
(distance p (get-first-point (get-rest-points (get-rest-points (get-rest-points (get-rest-points pt-list)))))) ; 5
(distance p (get-first-point (get-rest-points (get-rest-points (get-rest-points (get-rest-points (get-rest-points pt-list))))))) ; 6
)
)
)
You can probably get the gist of what I'm (horribly) attempting at doing, but this is why I need the help.
回答1:
Compute the distance to the car
of points
. Either that distance is the maximum or the maximum from the cdr
of points
is. Result is a simple recursive algorithm traversing the list of points.
(define (max-distance-p p points)
(if (null? points)
0
(max (distance p (car points))
(max-distance-p p (cdr points)))))
The 0
is returned if you pass in points
as an empty list; that might be a bit questionable. If so:
(define (max-distance-p p points)
(assert (not (null? points)))
(if (null? (cdr points))
(distance p (car points))
(max (distance p (car points))
(max-distance-p p (cdr points)))))
回答2:
This is a case where you want to fold a function f over your list of points. The function f should take a distance d and point x and return the maximum d and the distance between x and your special designated point p. I've described folds in a bit more detail in some other answers:
- Flattening a List of Lists
- Another way of writing a mimum value procedure in Scheme?
The main point is that in a fold, you take a function, an initial value, and a list. You apply the function to the first element of the list and the initial value to produce some new value. Now you recursively fold with the the same function, the new value, and the rest of the list. This is essentially a loop like this:
current value = initial value
while list is not empty
current value = result of function called with first element of list and current value
list = rest of list
return current value
In Scheme, where tail call optimization is guaranteed, it is that loop. In your case, you just need to determine what the initial value should be, and what the function should be. Since distances are always non-negative, a reasonable initial value is zero. The function is a bit more complicated. current value will be a distance, but the first element of list will be a point. The result of the function needs to be a new distance, and the new distance should be the maximum of the distance current value and the distance between some the point from list and the special point. In Racket, this kind of fold is called foldl, so we end up with something like this:
(define special-points '((2 . 4) (3 . 6) (5 . 12) (-4 . 3) (8.4 . 9) (0 . -1)))
(define (distance a b)
(let ((square (lambda (x)
(* x x))))
(sqrt (+ (square (- (car a) (car b)))
(square (- (cdr a) (cdr b)))))))
(define (max-distance point points)
(foldl (lambda (current-point current-distance)
(max current-distance
(distance point current-point)))
0
points))
> (max-distance '(4 . 12) special-points)
13.601470508735444
> (max-distance '(8 . 8) special-points)
13.0
> (max-distance '(2 . 5) special-points)
7.615773105863909
If you're not using Racket, you'll have to write your own foldl
, but it's really not very hard. (This actually isn't quite as sophisticated as Racket's foldl
, which can take any positive number of lists, but it will work for this case.)
(define (foldl function initial-value list)
(if (null? list)
initial-value
(foldl function
(function (car list) initial-value)
(cdr list))))
回答3:
get-y
should use cdr
, not car
.
get-x
and get-y
miss a closing parenthesis.
For max-distance
, I'd go for
(define (max-distance p pt-list)
(apply max (map (lambda (x) (distance p x)) pt-list)))
which means that you don't need get-first-point
and get-rest-points
.
Illustration (using (1 . 1) as p):
> (map (lambda (x) (distance '(1 . 1) x)) '((2 . 4) (3 . 6) (5 . 12) (-4 . 3) (8.4 . 9) (0 . -1)))
'(3.1622776601683795 5.385164807134504 11.704699910719626 5.385164807134504 10.897706180660222 2.23606797749979)
> (apply max (map (lambda (x) (distance '(1 . 1) x)) '((2 . 4) (3 . 6) (5 . 12) (-4 . 3) (8.4 . 9) (0 . -1))))
11.704699910719626
Depending on your definition of "farthest" you may want to include abs
in the expression.
来源:https://stackoverflow.com/questions/19534355/finding-maximum-distance-between-two-points-in-a-list-scheme