【问题标题】:Finding maximum distance between two points in a list (scheme)查找列表中两点之间的最大距离(方案)
【发布时间】:2013-10-23 06:28:00
【问题描述】:

我目前正在尝试从点列表中编写一个函数,该函数返回从点 p 到我的点列表中离 p 最远的点的距离。我的要点如下:

((2 . 4) (3 . 6) (5 . 12) (-4 . 3) (8.4 . 9) (0 . -1))

我还做了一些抽象来检索一般的 car 和 cdr(为了在代码中更容易看到),以及列表本身的 car 和 cdr。

(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))                                                                                         

我还写了一个通用距离公式,我可以将它用于我选择的任何两个点(请记住,我将 car 和 cdr 分别抽象为 get-x 和 get-y)。

(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)))))

现在我有了这个,我知道我需要比较整个点集的各种距离并选择最大的返回值。我有一个部分解决方案,但不是一个对每个点都正确的解决方案。

(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
    )
)

)

您可能可以了解我正在(可怕地)尝试做的事情的要点,但这就是我需要帮助的原因。

【问题讨论】:

  • (define (get-x p) (car p)(define (get-y p) (car p) 中,其中一个应该是cdr。尽管get-xget-y 将与carcdr 相同,但您并没有将它们用作列表函数,因此(define (get-first-point pt-list) (get-x pt-list)) 确实不是一个好习惯。像这样的别名函数背后的想法是,您可以稍后更改数据结构。假设点和列表总是具有相同的表示是不合理的,因此不应根据另一个的访问器来定义一个的访问器。

标签: scheme distance


【解决方案1】:

这是一个你想fold一个函数f覆盖你的点列表的情况。函数 f 应该取距离 d 和点 x 并返回最大值 d 之间的距离>x 和你的特殊指定点p。我在其他一些答案中更详细地描述了折叠:

重点是在折叠中,您需要一个函数、一个初始值和一个列表。您将函数应用于列表的第一个元素和初始值以产生一些新值。现在您使用相同的函数、新值和列表的其余部分递归折叠。这本质上是一个这样的循环:

当前值 = 初始值
列表不为空
当前值 = 列表的第一个元素和当前值调用函数的结果
列表 = 列表的其余部分
返回 当前值

在保证尾调用优化的Scheme中,就是那个循环。在您的情况下,您只需要确定初始值应该是什么以及函数应该是什么。由于距离总是非负数,因此一个合理的初始值为零。功能有点复杂。 current value 将是一个距离,但 list 的第一个元素将是一个点。函数的结果需要是一个新的距离,新的距离应该是当前值的距离和list中某个点到特别点。在 Racket 中,这种折叠称为foldl,所以我们最终会得到这样的结果:

(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

如果您不使用 Racket,则必须编写自己的 foldl,但这并不难。 (这实际上并不像 Racket 的 foldl 那样复杂,它可以接受任何正数的列表,但它适用于这种情况。)

(define (foldl function initial-value list)
  (if (null? list)
      initial-value
      (foldl function
             (function (car list) initial-value)
             (cdr list))))

【讨论】:

    【解决方案2】:

    计算到pointscar 的距离。该距离是最大值,或者是与pointscdr 之间的最大值。结果是一个简单的遍历点列表的递归算法。

    (define (max-distance-p p points)
      (if (null? points)
          0
          (max (distance p (car points))
               (max-distance-p p (cdr points)))))
    

    如果您将points 作为空列表传入,则返回0;这可能有点可疑。如果是这样:

    (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)))))
    

    【讨论】:

      【解决方案3】:

      get-y应该使用cdr,而不是car

      get-xget-y 缺少右括号。

      对于max-distance,我会选择

      (define (max-distance p pt-list)
        (apply max (map (lambda (x) (distance p x)) pt-list)))
      

      这意味着您不需要get-first-pointget-rest-points

      插图(使用 (1 . 1) 作为 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
      

      根据您对“最远”的定义,您可能希望在表达式中包含 abs

      【讨论】:

      • 对于reduce/fold,这是一个比(apply ... (map ...))更好的用例。
      • (好点;我应该在我的原始评论中提到它。)带有applymap 的版本不必要地创建了一个与点列表长度相同的中间列表,可以使用如果有很多点(无论多少都是不必要的空间),那么会增加很多额外的空间,并且使用带有非常大的参数列表的apply 也可能是一个问题。使用foldl 避免了中间存储空间以及长参数列表和apply 的潜在问题。
      • 在 Common Lisp(不是 Scheme,因此不能直接适用)中,有一个常量 call-arguments-limit 定义了可以调用函数的参数的限制。将apply 与长列表一起使用是解决此问题的简单方法。我不知道 Scheme 中是否有类似的限制,但相同的技术实现细节表明尝试避免它是合理的。也就是说,中间空间的使用确实是很大的原因。
      • @JoshuaTaylor 同意。尽管如此,我仍将保留我的示例,因为对于 OP,map 和 apply 比 fold 更容易理解。
      • 花了一段时间才找到一个明确的链接,但在 SFRI 13 中,a comment about string-concatenate 说:“请注意,(apply string-append string-list) 成语对于长字符串列表并不可靠,因为某些方案实现限制了可以传递给 n 元过程的参数数量。”不过,我希望我能找到一个更权威的来源,例如,保证被接受的参数的最小数量。
      猜你喜欢
      • 2012-09-01
      • 1970-01-01
      • 2019-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-16
      • 2023-01-20
      • 1970-01-01
      相关资源
      最近更新 更多