将内置数学运算符与自定义结构一起使用

时间:2019-04-27 01:11:48

标签: racket

我希望能够做这样的事情:

(struct point (x y))

(define p1 (point 1 2))
(define p2 (point 10 20))

(+ p1 p2)  ; -> (point 11 22)

是否可以教诸如point之类的结构以与诸如+之类的内置数学运算符一起工作?

文档似乎在this page的第5.5节中实现了自定义(equal? ...)处理。我想做的事情非常相似...

还是应该只定义(point-add p1 p2)之类的函数?

1 个答案:

答案 0 :(得分:6)

您可以

  1. 选择point-add
  2. 使用自己的+与您要采用的所有可能的值类型匹配。如果您事先知道所有可能的值类型,这就足够了,但是要对其进行扩展以在客户端代码中包含新创建的结构定义并不容易。例如:

    ;; We will "shadow" Racket's + with our own +, but we still
    ;; need the functionality of Racket's +, so let's require
    ;; Racket's + but use the name racket:+ instead
    (require (only-in racket/base [+ racket:+]))
    
    (struct point (x y) #:transparent)
    
    (define (+ x y)
      (match* (x y)
        [((point a b) (point c d)) (point (+ a c) (+ b d))]
        [((point _ _) _) (error '+ "Can't add a point with non point")]
        [(_ (point _ _)) (error '+ "Can't add a point with non point")]
        [(_ _) (racket:+ x y)]))
    
    ;; in client's code
    
    (+ (point 1 2) (point 3 4)) ;=> (point 4 6)
    (+ 1 2)                     ;=> 3
    
  3. 定义一个新的generics,以便为gen:equal+hash做类似于equal?的事情。例如:

    (require racket/generic
             (only-in racket/base [+ racket:+]))
    
    (define-generics addable
      (add addable _)
      #:fast-defaults ([number?
                        (define (add x y) (racket:+ x y))]))
    
    (define + add)
    
    ;; in client's code
    
    (struct point (x y)
      #:transparent
      #:methods gen:addable
      [(define (add x y)
         (match* (x y)
           [((point a b) (point c d)) (point (+ a c) (+ b d))]
           [(_ _) (error 'add "Can't add a point with non point")]))])
    
    (struct point-3d (x y z)
      #:transparent
      #:methods gen:addable
      [(define (add x y)
         (match* (x y)
           [((point-3d a b c) (point-3d d e f))
            (point-3d (+ a d) (+ b e) (+ c f))]
           [(_ _) (error '+ "Can't add a point-3d with non point-3d")]))])
    
    (+ (point 1 2) (point 3 4)) ;=> (point 4 6)
    (+ (point-3d 1 2 3) (point-3d 4 5 6)) ;=> (point-3d 5 7 9)
    (+ 1 2) ;=> 3
    
  4. 要接受多个参数,请如下修改(3)

    (define +
      (case-lambda
        [() 0]
        [(x . xs) (foldl add x xs)]))
    
    ;; client's code
    
    (+ (point 1 2) (point 3 4) (point 5 6)) ;=> (point 9 12)
    (+ 1 2 3) ;=> 6
    (+) ;=> 0
    (+ 1) ;=> 1
    (+ (point-3d 1 2 3)) ;=> (point-3d 1 2 3)