【问题标题】:How to invert the predicate here?如何在这里反转谓词?
【发布时间】:2021-06-01 21:59:24
【问题描述】:

我有以下过滤程序:

; (2) filter
(define (filter test sequence)
  ; return a list of the elements that pass the predicate test
  (let ((elem (if (null? sequence) nil (car sequence)))
        (rest (if (null? sequence) nil (cdr sequence))))
    (cond ((null? sequence) nil)
          ((test elem) (cons elem (filter test rest)))
          (else (filter test rest)))))

下面是一个使用它返回列表的偶数元素的示例:

(define even? (lambda (x) (= (modulo x 2) 0)))
(define sequence '(1 2 3 4 5 8 9 11 13 14 15 16 17))
(filter even? sequence)
; (2 4 8 14 16)

有没有一种简单的方法来使用not 测试来反转选择?例如,我认为以下可能有效:

(filter (not even?) sequence)

但它返回一个错误。我当然可以单独定义odd

(define odd?  (lambda (x) (not (even? x))))

但我尽量不这样做。有没有办法编写odd 过程而不直接定义它,而是直接使用not,就像我在上面尝试做的那样?

【问题讨论】:

  • 我感觉到你的痛苦,但你真的必须像Scheme一样使用Scheme。 nil 不是 Scheme 中的空列表。当(null? sequence) 为真时,您想返回()。好吧,不,你也不能拥有它,因为它不是自我评估的;你必须引用它:'().
  • 从列表中删除匹配或不匹配的项目是非常常见的操作。我会避免强迫程序员编写尴尬的否定,而只提供keep-ifremove-if 函数。

标签: scheme lisp racket predicate function-composition


【解决方案1】:

Common Lisp 中有一个 complement 函数,它可以满足您的需求。 complement 是一个高阶过程,它接受一个过程作为它的参数,并返回一个接受与输入过程相同的参数并执行相同动作的过程,但返回的真值是相反的。

Racket 有一个类似的过程,negate,并且在 Scheme 中很容易实现:

(define (complement f)
  (lambda xs (not (apply f xs))))
> (filter even? '(1 2 3 4 5))
(2 4)
> (filter (complement even?) '(1 2 3 4 5))
(1 3 5)
> (> 1 2 3 4 5)
#f
> ((complement >) 1 2 3 4 5)
#t

在球拍中:

scratch.rkt> (filter even? '(1 2 3 4 5))
'(2 4)
scratch.rkt> (filter (negate even?) '(1 2 3 4 5))
'(1 3 5)
scratch.rkt> (> 1 2 3 4 5)
#f
scratch.rkt> ((negate >) 1 2 3 4 5)
#t

【讨论】:

    【解决方案2】:

    对此的一般答案是简单地编写not 和您关心的功能。 Racket 有一个 compose 函数可以做到这一点,但您可以自己轻松编写一个简单的函数:

    (define (compose-1 . functions)
      ;; simple-minded compose: each function other than the last must
      ;; take just one argument; all functions should return just one
      ;; value.
      (define (compose-loop fns)
        (cond
          ((null? fns)
           (λ (x) x))
          ((null? (cdr fns))
           (car fns))
          (else
           (λ (x) ((car fns) ((compose-loop (cdr fns)) x))))))
      (compose-loop functions))
    

    当然,让它更高效、更通用需要更多的工作。

    然后你可以定义odd?(当然已经定义好了):

    (define odd? (compose-1 not even)
    

    或者实际上定义一个更通用的 CL 风格的complement 函数:

    (define (complement f)
      (compose-1 not f))
    

    【讨论】:

    • 谢谢,出于好奇,您拥有的功能与我在上面的答案中写的功能相比有什么优势?它主要是做所有的空检查,还是你的工作的例子,但我的会失败?
    • @David542: 我的 compose-1 将组成 any 单参数单值函数:(compose-1 zero? (lambda (x) (+ x 1))) 会返回一个函数,它会告诉你它是否参数是 -1(好吧,有更简单的方法可以做到这一点)。
    【解决方案3】:

    一种选择是编写一个invert 函数,该函数将把事情一起柯里化(所以初始函数仍然接受一个参数),直到最终评估发生:

    (define invert (lambda (func) (lambda (x) (not (func x)))))
    (define sequence '(1 2 3 4 5 6 8 9 11 13 14 15 16 17))
    (filter (invert even?) sequence)
    ; (1 3 5 9 11 13 15 17)
    

    【讨论】:

    • invert 这个定义的一个缺点是它只适用于一元过程。也就是说,这对even? 有效,但对<> 无效。我在我的回答中展示了这个例子:((negate >) 1 2 3 4 5) --> #t,但是((invert >) 1 2 3 4 5) --> arity 不匹配。我展示的 CL complement、Racket negatecomplement 的 Scheme 定义都可以在 n 元过程上运行。
    • @adabsurdum 啊,好的,感谢您指出这一切!
    猜你喜欢
    • 2021-04-19
    • 1970-01-01
    • 2018-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-21
    • 1970-01-01
    • 2011-04-28
    相关资源
    最近更新 更多