Использование встроенных математических операторов с пользовательской структурой - PullRequest
5 голосов
/ 27 апреля 2019

Я хочу иметь возможность сделать что-то вроде этого:

(struct point (x y))

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

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

Можно ли научить структуру типа point работать со встроенными математическими операторами, такими как +?

Документы, похоже, могут реализовать пользовательскую обработку (equal? ...) в разделе 5.5 на этой странице .То, что я пытаюсь сделать, очень похоже ...

Или я должен просто определить функцию как (point-add p1 p2)?

1 Ответ

6 голосов
/ 27 апреля 2019

Вы можете либо

  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. Определите новый генерик , чтобы мы могли сделать что-то похожее на 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)
    
...