【问题标题】:Scheme: Trying to find largest element in two lists方案:试图在两个列表中找到最大的元素
【发布时间】:2021-11-01 23:07:56
【问题描述】:

我有 3 个函数 union、maximum 和 maximum_of_two。 Union 接受两个列表并将它们组合为一个(此功能已经过测试并且有效)。最大应该返回给定列表中的最大元素,但无论我是通过最大的_of_two还是单独调用它,都只返回#f。任何帮助将不胜感激。

(define (union l1 l2) 
    (cond (
        (null? l1) l2) 
        ((cons (car l1) (union (cdr l1) l2)))
    )
)

(define  (largest x a_list)
    (cond
        ((null? a_list) x)
        ((< x (car a_list)) (= x (car a_list)))
        (else (largest x (cdr a_list)))
    )

)

(define (largest_of_two l1 l2)
    (largest (car l1) (cdr (union l1 l2)))
)

(display(largest_of_two '(19 30 13 29 38) '(1 50 5 20 41)))

【问题讨论】:

  • 错误在((&lt; x (car a-list)) (= x (car a-list))),应该是((&lt; x (car a-list)) (largest (car a-list) (cdr a-list)))
  • “给定列表中的最大元素”暗示了一个参数 - 一个列表 - 而不是两个。随着这种变化,这变成了两个过程的微不足道的应用。
  • 我建议您习惯于传统的括号放置。您的方法建议了一个不存在的块结构。 (在我放弃“C 大括号”习惯之前,我个人对 Lisps 有很多问题。)

标签: scheme


【解决方案1】:

根本问题是(= x (car a_list))是一个比较,但是这里需要递归调用:

(define (largest x a_list)
  (cond ((null? a_list) x)
        ((< x (car a_list))  ; if x is less than the first element
         (largest (car a_list) (cdr a_list)))  ; call with first element and cdr
        (else
         (largest x (cdr a_list)))))

x 小于列表的第一个元素时,您想再次调用largest,并将第一个元素和一个缩减列表作为参数。

然而,用这样的两个参数调用largest 似乎很尴尬。如果我想找到列表中最大的元素(1 6 1 8 0 3),我必须调用(largest 1 '(6 1 8 0 3)),这并不理想。更好的方法是在每次迭代中丢弃前两个元素中较小的一个,直到只剩下一个元素:

(define (largest xs)
  (cond ((null? xs) #f)  ; empty input: no largest member
        ((null? (cdr xs))  ; only one member
         (car xs))
        ((> (car xs) (cadr xs))  ; first member is larger than the second
         (largest (cons (car xs) (cddr xs))))  ; keep the first member
        (else
         (largest (cdr xs)))))  ; discard the first member

当输入列表为空时返回数值结果是没有意义的,所以在这种情况下返回#f。如果输入列表仅包含单个值,则返回该值(请注意,此代码不会验证一个元素的列表是否包含数字,因此 (largest '(z)) --> z)。

否则列表至少包含两个值。如果第一个大于第二个,则将第一个放在列表的其余部分上,并删除第二个 ((cddr xs)),并在结果上再次调用 largest。否则第一个值不大于第二个值,因此第一个元素被丢弃 ((cdr xs)) 并在该结果上调用 largest

以条件形式发布的union 定义还有另一个小问题:

((cons (car l1) (union (cdr l1) l2)))

这里只有一个测试表达式。 Now, this is legal Scheme because when a selected conditional clause contains only a test expression, the value of the test expression is returned.但这不是惯用的,而且很难阅读。 largest 的发布定义在类似情况下使用了else,它也应该在这里使用。或者,只需使用 if 表单:

(define (union xs ys)
  (if (null? xs)
      ys
      (cons (car xs) (union (cdr xs) ys))))

保持一致;使用格式和换行符使代码清晰。当我们谈论风格时,请不要随意散布括号,并且更喜欢kebab-case(又名lisp-case)而不是snake_case作为lisps中的标识符。

有了largest 的新定义,largest-of-two 有了更简单的定义:

(define (largest-of-two xs ys)
  (largest (union xs ys)))
> (largest-of-two '(1 4 2 6 3 11 6 -2) '(3 8 -3 7 10 4))
11

【讨论】:

    【解决方案2】:

    过滤在 Scheme 和函数式语言中通常称为reduce。它已经是explained in detail here。我引用实现:

    (define (reduce fn list init)
      (if (null? list) init
          (fn (car list)
              (reduce fn (cdr list) init))))
    

    这是一个非常通用的函数,它接受操作、要操作的列表和一个累加器,它保存每个步骤的值。

    您只需要为两个参数实现最大函数。

    (define (max a b)
      (if (> a b)
          a
          b))
    

    然后您可以将其传递给reduce。你只需要添加一个初始值。

    (reduce max '(19 30 13 29 38) 0)
    

    在您的情况下,拆分输入列表可能会更好,因为所有元素都可以是负数。

    (let ((lst '(19 30 13 29 38)))
      (reduce max (cdr lst) (car lst)))
    

    如果您想做两次或更多参数,只需使用map

    (map (lambda (lst)
           (reduce max (cdr lst) (car lst)))
         '((19 30 13 29 38)
           (1 50 5 20 41)))
    

    以下将所有内容放在一个函数中。

    (define (largest-of . lists)
      (define (reduce fn list init)
        (if (null? list) init
            (fn (car list)
                (reduce fn (cdr list) init))))
      (define (max a b)
        (if (> a b)
            a
            b))
      (map (lambda (lst)
             (reduce max (cdr lst) (car lst)))
           lists))
    
    (largest-of '(19 30 13 29 38) '(1 50 5 20 41)) ;; => (38 50)
    

    这也适用于两个以上的列表。它仅受您的 Scheme 实现的最大参数数量的限制。

    它只适用于一个论点。

    (largest-of (largest-of '(19 30 13 29 38) '(1 50 5 20 41))) ;; => (50)
    

    您只需使用car 将值拆箱即可。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-21
      • 2021-08-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-22
      相关资源
      最近更新 更多