【问题标题】:Scheme: find out if an "complex" element is in a "complex" list方案:找出“复杂”元素是否在“复杂”列表中
【发布时间】:2012-10-20 16:14:11
【问题描述】:

我正在使用 R5RS 标准的 Scheme 实现。

现在假设您必须找出元素 '(2 3 4) 是否在列表 '(1 2 3 4) 中。

至于示例,更严格地说,您希望:

1. (is-in? '(2 3 4) '(1 2 3 4)) -> #f
2. (is-in? '(2 3 4) '(1 (2 3 4)) -> #t

问题:如何获得这种行为,如示例 1 所示?

让我解释一下:当您搜索列表时,您可以使用carcdr 来获取其部分。现在,如果您递归地遍历列表,您最终会得到:

3. (cdr '(1 2 3 4)) -> '(2 3 4)
4. (cdr '(1 (2 3 4)) -> '((2 3 4))

所以最终,我们得到了 2 个列表。您可以在这里看到,子列表 '(2 3 4) 包含在 both 来自 3 和 4 的结果中。

请看3和4与1和2的矛盾:而'(2 3 4)不包含在'(1 2 3 4)中,递归调用cdr返回'(2 3 4),这is 等于 '(2 3 4) - 并且使用 equal? 函数,在递归调用中的某处,我们最终得到 1 和 2 的 #t:

5. (is-in? '(2 3 4) '(1 2 3 4)) -> #t
6. (is-in? '(2 3 4) '(1 (2 3 4)) -> #t

那么如何从 1 获得这种行为呢?我想要一个函数,它适用于所有不同类型的数据。这是我的函数,它像 5 和 6 一样工作(通过应该像 1 和 2 一样工作):

(define or (lambda (x y)
             (cond ((eq? x y) (eq? x #t))
                   (#t #t)
                   )
             )
  )

(define and (lambda (x y)
              (cond ((eq? x y) (eq? x #t))
                    (#t #f)
                    )
              )
  )

(define atom? (lambda (x)
                (not (pair? x))
                )
  )

(define length (lambda (x)
                       (cond ((eq? x '()) 0)
                             ((atom? x) 1)
                             (#t (+ (length (car x)) (length (cdr x))))
                             )
                       )
  )

(define equal? (lambda (x y)
                 (cond ((and (atom? x) (atom? y)) (eq? x y))
                       ((not (eq? (length x) (length y))) #f)
                       ((not (and (pair? x) (pair? y))) #f)
                       (#t (and (equal? (car x) (car y)) (equal? (cdr x) (cdr y))))
                       )
                 )
  )

(define is-in? (lambda (x y)
                 (cond ((equal? x y) #t)
                       (#t (cond ((pair? y) (or (is-in? x (car y)) (cond ((eq? (length y) 1) #f)
                                                                          (#t (is-in? x (cdr y)))
                                                                          )))
                                 (#t #f)
                                 )
                           )
                       )
                 )
  )

更新

我想要的是有一个general函数,它可以告诉你某个object是否在另一个object中。我将实体命名为 object 以强调该函数应该适用于任何输入值,无论是简单的还是复杂的,就像地狱一样。

示例用法:

1. (is-in? 1 '(1 2 3)) ;-> #t
2. (is-in? '(1) '(1 2 3)) ;-> #f
3. (is-in? '(2 . 3) '(1 2 . 3)) ;-> #f
4. (is-in? '(2 . 3) '(1 (2 . 3))) ;-> #t
5. (is-in? '2 '(1 2 . 3)) ;-> #t
6. (is-in? '(2) '(1 2 . 3)) ;-> #f
7. (is-in? '(1 2 (3 4 (5 6 . (7 . 8)) 9) 10 11 (12 . 13)) '(1 (2 3 ((4 ((6 (3 . ((1 2 (3 4 (5 6 . (7 . 8)) 9) 10 11 (12 . 13)))) 3) 4)) 5) 2))) ;-> #t
8. (is-in? '(2 3 4) '((1 (2 3 4)) (1 2 3 4))) ;-> #t
9. (is-in? '(2 3 4) '(1 2 3 4)) ;-> #f
10. (is-in? '(2 3 4) '(1 (2 3 4))) ;-> #t
11. (is-in? '(1) '(1)) ;-> #t

【问题讨论】:

    标签: list scheme lisp member


    【解决方案1】:

    首先——你为什么要重新定义andorequal?length?这些是内置的原语。另外你对atom?的定义是错误的,应该是:

    (define (atom? x)
      (and (not (pair? x))
           (not (null? x))))
    

    我猜你需要从头开始实现这个作为家庭作业的一部分。让我们看看如何实现,填空以获得您的答案:

    (define (is-in? ele lst)
      (or <???>                          ; trivial case: ele == list
          (member? ele lst)))            ; call helper procedure
    
    (define (member? ele lst)
      (cond ((null? lst)                 ; if the list is empty
             <???>)                      ; then the element is not in the list
            ((atom? lst)                 ; if the list is not well-formed
             (equal? <???> <???>))       ; then test if ele == list
            (else                        ; otherwise
             (or (equal?  ele <???>)     ; test if ele == the 1st element in the list
                 (member? ele <???>)     ; advance the recursion over the `car`
                 (member? ele <???>))))) ; advance the recursion over the `cdr`
    

    请注意,member? 中的第二种情况是必需的,因为在给出的示例中存在格式错误的列表(以非空值结尾)。上述解决方案将正确处理问题中提供的所有示例。

    【讨论】:

    • 谢谢你的回答,奥斯卡。您能否详细说明并展示如何为自己正确实现成员?这实际上是我的“功课”,我在帖子中描述的怪异并没有让我完成它。顺便说一句,试试这样: (is-in? '(2 3 4) '((1 (2 3 4)) (1 2 3 4))) -> #f.结果仍然是错误的,应该是#t。有没有我想要的功能?
    • 添加:您自己的功能类似于 Scott Hunter 提供的功能。像这样尝试它并返回错误答案: (is-in? '(2 3 4) '((1 (2 3 4)) (1 2 3 4)))
    • 请查看更新后的问题 - 我添加了示例用法,说明是否足够。
    • @Vadim 检查新更新,我简化了答案。这是我的最终版本,你应该可以自己找到解决方案
    • 谢谢,现在会解决这个问题:)
    【解决方案2】:
    (define (is-in? e lst)
       (cond ((null? lst) #f) ; if list is empty, we failed
             ((eq? e (car lst)) #t) ; check 1st element of list
             ((list? (car lst)) (is-in? e (append (car lst) (cdr lst)))) ; search inside car if it is a list
             (#t (is-in? e (cdr lst))))) ; check rest of list
    

    您可以将eq? 替换为更精细的内容以处理其他相等定义。

    注意:这里假设所有序列都是列表;您必须对其进行调整以处理不是列表的点对。

    【讨论】:

    • 感谢这个例子,但不幸的是它会产生错误的结果: (is-in? '(2 3 4) '((1 (2 3 4)) (1 2 3 4) )) -> #f
    • 为什么是append?这是一个相当不优雅的解决方案,如果我们只是在列表中寻找某些东西,则无需创建新列表!无论如何,它在 OP 的示例中失败了。
    • 有些人认为尾递归很优雅。除了(如前所述)处理点对之外,唯一的问题是在添加的 is-in? 调用中缺少 arg。
    • 将此答案与我自己的答案进行比较 - 仅使用列表遍历。查找内容时无需创建新列表!无论是哪种编程语言,这都是低效且丑陋的
    • “美丽在旁观者的眼中”,而您获得的效率(可能只是部分)被尾递归优化所抵消(非常干净,我不会否认)代码不能利用。或者,换一种说法:不需要创建新的堆栈帧...
    猜你喜欢
    • 1970-01-01
    • 2016-01-11
    • 1970-01-01
    • 2019-06-01
    • 2021-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多