Scheme/Racket:一个函数,它将列表分成两个与某个谓词匹配的元素列表和与之不匹配的元素列表

Pad*_*rry 1 lisp scheme predicate list racket

我希望在Scheme中创建一个函数,它接受一个谓词和一个元素列表,然后输出两个单独的列表.一个具有匹配给定谓词的原始列表的元素,另一个具有与其不匹配的元素.

我现在所拥有的代码我认为应该隔离与谓词匹配的代码并输出它们的列表,但代码将无效.

    (define tear
(lambda (pred xs)
    (cond[(null? xs) '()]
         [(list? (car xs))(cons((tear (pred (car xs)))(tear (pred (cdr xs)))))]
         [(pred (car xs))(cons((car xs)(tear (pred (cdr xs)))))]
         [else tear (pred (cdr xs))])))
(tear number? '(1 2 3 a b c))
Run Code Online (Sandbox Code Playgroud)

我的编译器产生的结果是:

    tear: arity mismatch;
 the expected number of arguments does not match the given number
  expected: 2
  given: 1
  arguments...:
   #f
  context...:
   /home/jdoodle.rkt:2:4: tear
Command exited with non-zero status 1
Run Code Online (Sandbox Code Playgroud)

您可以提供的任何帮助/信息将非常感谢.

Ale*_*uth 5

让我们一步一步修复您的代码.添加缩进和空格以使其可读:

(define tear
  (lambda (pred xs)
    (cond
      [(null? xs) 
       '()]
      [(list? (car xs))
       (cons ((tear (pred (car xs))) (tear (pred (cdr xs)))))]
      [(pred (car xs))
       (cons ((car xs) (tear (pred (cdr xs)))))]
      [else 
       tear (pred (cdr xs))])))

(tear number? '(1 2 3 a b c))
Run Code Online (Sandbox Code Playgroud)

我看到的第一个问题是在函数调用的内部(在参数周围)而不是在外部放置括号.您可以使用cons递归调用来执行此操作tear.例如,tear (pred (cdr xs))您应该将第一个paren移动到函数之前.请记住,表达式中的括号几乎总是表示形状为的函数调用(function argument ...).

  • (cons (A B)) 应该改写成 (cons A B)
  • (tear (Pred Xs)) 应该改写成 (tear Pred Xs)
  • tear (Pred Xs) 应该改写成 (tear Pred Xs)

通过这些修复,您的代码如下所示:

(define tear
  (lambda (pred xs)
    (cond
      [(null? xs) 
       '()]
      [(list? (car xs))
       (cons (tear pred (car xs)) (tear pred (cdr xs)))]
      [(pred (car xs))
       (cons (car xs) (tear pred (cdr xs)))]
      [else 
       (tear pred (cdr xs))])))

(tear number? '(1 2 3 a b c))
;=> (1 2 3)
(tear number? '(1 2 "not a number" 3 4))
;=> (1 2 3 4)
Run Code Online (Sandbox Code Playgroud)

但是,当有嵌套列表时,它仍然会做一些奇怪的事情:

(tear list? (list '(1 2 3) "not a list" '(4 5)))
;=error> (() ())
Run Code Online (Sandbox Code Playgroud)

为了保持一致,它应该将两个列表放入一个列表中:((1 2 3) (4 5)).要做到这一点,只需删除第二种cond情况:

(define tear
  (lambda (pred xs)
    (cond
      [(null? xs) 
       '()]
      [(pred (car xs))
       (cons (car xs) (tear pred (cdr xs)))]
      [else 
       (tear pred (cdr xs))])))

(tear number? '(1 2 3 a b c))
;=> (1 2 3)
(tear list? (list '(1 2 3) "not a list" '(4 5)))
;=> ((1 2 3) (4 5))
Run Code Online (Sandbox Code Playgroud)

它现在似乎正好完成你想要的一半.您希望它返回两个列表:一个用于传递的元素,另一个用于失败的元素.它目前只返回第一个列表.

你应该做的第一件事是记录它如何返回这两个列表.由于总有两个,您可以将它们作为多个值返回.

;; tear returns two values:
;;  - a list of the elements of `xs` that passed `pred`
;;  - a list of the elements of `xs` that failed `pred`
Run Code Online (Sandbox Code Playgroud)

使用多个值有两个部分:返回它们并接收它们.使用(values A B)返回他们,(let-values ([(A B) ....]) ....)要匹配的结果,就像一个递归调用的结果.

这意味着每次这样的递归调用(f .... (tear ....) ....)都应该成为

(let-values ([(A B) (tear ....)])
  (values (f .... A ....)
          ???))
Run Code Online (Sandbox Code Playgroud)

将其应用于您的代码:

;; tear returns two values:
;;  - a list of the elements of `xs` that passed `pred`
;;  - a list of the elements of `xs` that failed `pred`
(define tear
  (lambda (pred xs)
    (cond
      [(null? xs) 
       (values '()
               ???)]
      [(pred (car xs))
       (let-values ([(A B) (tear pred (cdr xs))])
         (values (cons (car xs) A)
                 ???))]
      [else
       (let-values ([(A B) (tear pred (cdr xs))])
         (values A
                 ???))])))
Run Code Online (Sandbox Code Playgroud)

现在填写???漏洞,使用示例.

  • (tear number? '()) 应该返回两个空列表: () ()
  • (tear number? '(1 2)) 应返回完整列表和空列表: (1 2) ()
  • (tear number? '(a b)) 应该返回一个空列表和一个完整列表: () (a b)

第一个例子对应第一个???孔,第二个例子对应第二个孔,依此类推.

这告诉我们应该填充第一个孔'(),第二个孔应该填充,B第三个孔应该填充(cons (car xs) B).

(define tear
  (lambda (pred xs)
    (cond
      [(null? xs) 
       (values '() '())]
      [(pred (car xs))
       (let-values ([(A B) (tear pred (cdr xs))])
         (values (cons (car xs) A)
                 B))]
      [else
       (let-values ([(A B) (tear pred (cdr xs))])
         (values A
                 (cons (car xs) B)))])))

(tear number? '(1 2 3 a b c))
;=> (1 2 3)
;   (a b c)
(tear list? (list '(1 2 3) "not a list" '(4 5)))
;=> ((1 2 3) (4 5))
;   ("not a list")
Run Code Online (Sandbox Code Playgroud)