Ракетные эквиваленты Python __repr__ и __str__? - PullRequest
2 голосов
/ 03 мая 2019

В объектах python переопределение методов __repr__ и __str__ объекта позволяет обеспечить «однозначное» и «читаемое человеком» представление объекта соответственно. Как можно добиться подобного поведения в ракетке?

Я столкнулся с printable<%> интерфейсом здесь , который, кажется, должен использоваться для этой цели, но я не смог заставить его работать так, как я ожидал. Опираясь на стандартный пример "рыбы" из документации:

(define fish%
  (class* object% (printable<%>)
    (init size) ; initialization argument
    (super-new) ; superclass initialization
    ;; Field
    (define current-size size)

    ;; Public methods
    (define/public (get-size)
      current-size)
    (define/public (grow amt)
      (set! current-size (+ amt current-size)))
    (define/public (eat other-fish)
      (grow (send other-fish get-size)))

    ;; implement printable interface
    (define/public (custom-print port quoting-depth)
      (print "Print called!"))
    (define/public (custom-write port)
      (print "Write called!"))
    (define/public (custom-display port)
      (print "Display called!"))))

Это вывод, который я получаю:

> (define f (new fish% [size 10]))
> f
"Display called!""Display called!""Print called!"
> (print f)
"Write called!""Print called!"
> (display f)
"Display called!""Display called!"
> (write f)
"Write called!""Write called!"
> 

Итак, вопрос состоит из трех частей:

  1. Почему он ведет себя таким образом, т. Е. С множественными методами, вызываемыми при явно уникальном рендеринге объекта?

  2. Что должны оценивать методы custom-print, custom-write и custom-display? Должны ли они просто возвращать строку или они могут повлечь за собой побочный эффект печати, записи или отображения, в зависимости от обстоятельств? Например. должен ли метод пользовательской записи вызывать функцию write внутри?

  3. Правильно ли это использовать для этой цели? Если нет, есть ли один / что это?

Ответы [ 2 ]

2 голосов
/ 03 мая 2019

Что касается

  1. Почему он ведет себя таким образом, то есть с несколькими методами, вызываемыми при явно уникальном рендеринге объекта?

Вы случайно использовали print в write, поэтому при записи значения сначала будет напечатано значение.

(define/public (custom-write port)
   (print "Write called!"))

Аналогичная проблема присутствует в display.

Также не забудьте распечатать / записать / отобразить на соответствующий порт.

Попробуйте

#lang racket
(define fish%
  (class* object% (printable<%>)
    (init size) ; initialization argument
    (super-new) ; superclass initialization
    ;; Field
    (define current-size size)

    ;; Public methods
    (define/public (get-size)
      current-size)
    (define/public (grow amt)
      (set! current-size (+ amt current-size)))
    (define/public (eat other-fish)
      (grow (send other-fish get-size)))

    ;; implement printable interface
    (define/public (custom-print port quoting-depth)
      (print (~a "Print " current-size "\n") port))
    (define/public (custom-write port)
      (write (~a "Write " current-size "\n") port))
    (define/public (custom-display port)
      (display (~a "Display " current-size "\n") port))))

В ответе вы увидите:

> (define f (new fish% [size 10]))
> f
"Print 10\n"
> (display f)
Display 10
> (write f)
"Write 10\n"
1 голос
/ 25 июня 2019

Другой ответ уже помог вам найти проблему в вашем коде - вам нужно использовать порт, указанный в качестве аргумента, а не неявный (current-output-port), но объяснение не совсем верно. Чтобы ответить на ваши вопросы в обратном порядке:

  1. Правильно ли это использовать для этой цели? Если нет, есть ли один / что это?

Да, printable<%> - это правильная конструкция, используемая для настройки печати объекта на основе классов. В более общем смысле тип структуры, который не является классом, может настраивать печать, например, через универсальный интерфейс gen:custom-write или низкоуровневое свойство типа структуры prop:custom-write, которое используется для реализации printable<%>.

  1. Что должны оценивать методы custom-print, custom-write и custom-display? Должны ли они просто возвращать строку или они могут повлечь за собой побочный эффект печати, записи или отображения, в зависимости от обстоятельств? Например. должен ли метод custom-write вызвать функцию write для внутреннего использования?

Методы на самом деле должны вызывать побочный эффект выполнения ввода-вывода для порта, который они задают в качестве аргумента. Они должны использовать соответствующую функцию (например, write для custom-write, print для custom-print) внутри для рекурсивной печати / записи / отображения значений в полях. С другой стороны, при непосредственном выделении определенных символов они обычно должны использовать такие функции, как write-char, write-string или printf. Документы для gen:custom-write дают пример типа данных кортежа, который печатается как <1, 2, "a">: он использует write-string для угловых скобок и запятых, но рекурсивно print / write / display для элементов кортежа.

  1. Почему он ведёт себя так, т. Е. С множеством методов, вызываемых при явно уникальном рендеринге объекта?

Это самая сложная часть вашего вопроса. Печать в Racket настраивается через несколько зацепок: несколько примеров см. current-print, port-write-handler, global-port-print-handler и make-tentative-pretty-print-output-port. Многие из этих хуков настройки используют промежуточные порты в процессе вывода.

Одна вещь, которая не является частью объяснения, заключается в том, что вы использовали print в своей реализации, особенно когда print привязан к нормальной функции Racket с помощью лексической области видимости, а не метод вашего объекта.

В качестве иллюстрации рассмотрим следующую адаптацию вашего примера, которая сообщает (current-output-port) идентификатор порта, указанный в качестве аргумента метода:

#lang racket

(define report
  (let ([next-id 0]
        [id-cache (make-hash)])
    (λ (op port)
      (printf "~a ~a ~v\n"
              op
              (hash-ref id-cache
                        port
                        (λ ()
                          (define id next-id)
                          (hash-set! id-cache port id)
                          (set! next-id (add1 next-id))
                          id))
              port))))

(define fish%
  (class* object% (printable<%>)
    (super-new)
    ;; implement printable interface
    (define/public (custom-print port quoting-depth)
      (report "custom-print  " port))
    (define/public (custom-write port)
      (report "custom-write  " port))
    (define/public (custom-display port)
      (report "custom-display" port))))

(define f (new fish%))

f

(print f)
(newline)

(display f)
(newline)

(write f)

В DrRacket это генерирует вывод:

custom-display 0 #<output-port:null>
custom-display 1 #<output-port:null>
custom-print   2 #<printing-port>

custom-display 3 #<output-port:null>
custom-display 4 #<output-port:null>
custom-print   5 #<printing-port>

custom-display 6 #<output-port:null>
custom-display 7 #<printing-port>

custom-display 8 #<output-port:null>
custom-write   9 #<printing-port>

в то время как в командной строке вывод:

$ racket demo.rkt 
custom-write   0 #<output-port:null>
custom-print   1 #<output-port:redirect>

custom-write   2 #<output-port:null>
custom-print   3 #<output-port:redirect>

custom-display 4 #<output-port:null>
custom-display 5 #<output-port:redirect>

custom-write   6 #<output-port:null>
custom-write   7 #<output-port:redirect>
...