【问题标题】:Recursive numeric equality in SchemeScheme中的递归数字相等
【发布时间】:2017-09-01 18:50:26
【问题描述】:

Scheme 似乎认为整数和浮点数版本在使用 equal? 时是不同的,但在使用 = 比较它们时是一样的:

(equal? 2 2.0)  ; => #f
(= 2 2.0)       ; => #t

但是,如果我有一个包含一些数字部分(甚至是简单的数字列表)的递归结构,是否有一种方法可以使用 = 进行数字比较?

(equal? '(2 3) '(2.0 3.0))  ; => #f
(= '(2 3) '(2.0 3.0))       ; error: contract violation

我可以编写自己的相等检查器,如下所示:

(define myequal?
  (lambda (x y)
    (cond ((and (null? x) (null? y)) #t)
          ((or (null? x) (null? y)) #f)
          ((and (pair? x) (pair? y))
           (and (myequal? (car x) (car y))
                (myequal? (cdr x) (cdr y))))
          ((or (pair? x) (pair? y)) #f)
          ((and (number? x) (number? y)) (= x y))
          ((or (number? x) (number? y)) #f)
          (else (equal? x y)))))

但这似乎是一项足够常见的任务,Scheme 可能有一个内置方法来执行此操作。

【问题讨论】:

    标签: recursion scheme racket equality r5rs


    【解决方案1】:

    在 Racket 中,您可以借助 equal?/recur 内置过程构建您想要的平等概念:

    ;; equalish? : Any Any -> Boolean
    ;; Like equal?, but use = for numbers (including within compound data)
    (define (equalish? a b)
      (if (and (number? a) (number? b))
          (= a b)
          (equal?/recur a b equalish?)))
    
    (equalish? '(2 3) '(2.0 3.0))
    ;; => #t
    

    equal?/recur 过程通过对、结构等处理重复发生。

    【讨论】:

      【解决方案2】:

      Scheme 是一种极简语言,具有极少的原语。 2 和 2.0 不是同一个数字,因为 2.0 可以低于和高于 2,这是确切的数量 2。

      如果您有一个带有数字的列表并希望检查所有数字是否与= 相同,您可以使用来自SRFI-1 List Library 的every 来完成:

      ;; include the library. In R5RS this is impleentation specific
      ;; and worst case you need to load of external file to be portable.
      (load "srfi1.scm") 
      
      (every = '(2 3) '(2.0 3.0)) ; ==> #t
      

      在 R6RS 中它变得更简单了:

      #!r6rs
      
      (import (rnrs base)
              (only (srfi :1) every))
      
      (every = '(2 3) '(2.0 3.0)) ; ==> #t
      

      并且由于您已标记 Racket,因此您可能不会编写 Scheme,但可能是 #lang racket,它同时支持 SRFI-1 和它自己的 every 版本,称为 andmap:

      #lang racket
      
      (andmap = '(2 3) '(2.0 3.0)) ; ==> #t
      
      (require srfi/1)
      (every = '(2 3) '(2.0 3.0)) ; ==> #t
      

      编辑

      当没有更多类型特定选项时,所有将自身用于树结构和equal? 的树结构的通用解决方案:

      (define (make-equal . predicates-and-equal-procedures)
        (when (odd? (length predicates-and-equal-procedures))
          (error "make-equal needs an even number of predicate and equal function arguments"))
      
        (define (mequal? a b)
          (if (pair? a)
              (and (pair? b)
                   (mequal? (car a) (car b))
                   (mequal? (cdr a) (cdr b)))
              (let loop ((pe predicates-and-equal-procedures))
                (if (null? pe)
                    (equal? a b)
                    (let ((p? (car pe)))
                      (if (p? a)
                          (and (p? b)
                               ((cadr pe) a b))
                          (loop (cddr pe))))))))
        mequal?)
      
      (define list=?
        (make-equal number? =))
      
      (list=? '(1 2 a b "test") '(1.0 2 a b "test")) ; ==> #t
      
      (define equal-ci?
        (make-equal string? string-ci=? char? char-ci=?))
      
      (equal-ci? '(1 2 a b "Test") '(1 2 a b "test")) ; ==> #t
      
      (define inexact-eq-ci?
        (make-equal number? = string? string-ci=? char? char-ci=?))
      
      (inexact-eq-ci? '(1 2 a b "test") '(1.0 2 a b "TEST")) ; ==> #t
      

      【讨论】:

      • 啊,这是一个很好的数字列表解决方案。听起来,对于更复杂的结构,例如 '(a 2 (b 3)) 与 '(a 2.0 (b 3.0)),您可能必须编写自定义函数。
      • @JeffAmes 我编辑了一个创建通用相等标记的示例,您可以在其中指定如何比较某些类型。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-05
      • 2013-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多