Как я могу сравнить объекты синтаксиса в ракетке? - PullRequest
0 голосов
/ 22 ноября 2018

Я хотел бы сравнить содержимое кода двух объектов синтаксиса и игнорировать такие вещи, как контексты.Является ли преобразование их в данные единственным способом сделать это?Нравится:

(equal? (syntax->datum #'(x+1)) (syntax->datum #'(x+1)))

Ответы [ 2 ]

0 голосов
/ 23 ноября 2018

Вот конкретный пример того, как вы могли бы сделать «лучший подход», упомянутый Лейфом Андерсеном.

Я использовал это в нескольких местах для тестирования целей, хотя, если кто-то захочет использовать его в не тестовом коде, он, вероятно, захочет повторно посетить некоторые из проектных решений.

Однако такие вещи, как шаблон equal?/recur, используемый здесь, должны быть полезны независимо от того, как вы решите определить, что означает равенство.

Некоторые из решений, которые вы можете пожелатьчтобы сделать другой выбор:

  • На идентификаторах вы хотите убедиться, что области действия точно одинаковы (bound-identifier=?)или вы хотите предположить, что они будут связаны вне объекта синтаксиса, и проверить, что они связаны с одной и той же вещью, даже если они имеют разные области действия (free-identifier=?)?Обратите внимание, что если вы выберете первый, то проверка результатов раскрытия макроса иногда будет возвращать #false из-за различий в области, но если вы выберете второй, то, если какой-либо идентификатор не связан вне объекта синтаксиса, тогда онбыло бы так, как если бы вы заботились только о равенстве symbol=? в именах, поэтому он вернет #true в некоторых местах, где это не должно быть.Я выбрал первый bound-identifier=? здесь, потому что для тестирования «ложноположительный результат», где тест не пройден, лучше, чем «ложноотрицательный», где тесты успешно выполняются в тех случаях, когда этого не должно быть.

  • В исходных местоположениях вы хотите проверить, равны ли они или игнорировать их?Этот код игнорирует их , потому что это только для целей тестирования, но если вам нужно равенство только для вещей, которые имеют одинаковое расположение источника, вы можете проверить это, используя такие функции, как build-source-location-list.

  • В свойствах синтаксиса вы хотите проверить, что они равны, или вы хотите их игнорировать?Этот код игнорирует их , потому что только для целей тестирования, но если вы хотите проверить, можете ли вы проверить это, используя такие функции, как syntax-property-symbol-keys.

Наконец, вот код.Это может быть не совсем то, что вы хотите, в зависимости от того, как вы ответили на вопросы выше.Тем не менее, его структура и способ использования equal?/recur могут быть вам полезны.

(require rackunit)

;; Works on fully wrapped, non-wrapped, and partially
;; wrapped values, and it checks that the the inputs
;; are wrapped in all the same places. It checks scopes,
;; but it does not check source location.
(define-binary-check (check-stx=? stx=? actual expected))

;; Stx Stx -> Bool
(define (stx=? a b)
  (cond
    [(and (identifier? a) (identifier? b))
     (bound-identifier=? a b)]
    [(and (syntax? a) (syntax? b))
     (and (bound-identifier=? (datum->syntax a '||) (datum->syntax b '||))
          (stx=? (syntax-e a) (syntax-e b)))]
    [else
     (equal?/recur a b stx=?)]))
0 голосов
/ 23 ноября 2018

Если вы хотите сравнить оба объекта, не разбирая их вообще, тогда да.

ОДНАКО, проблема с этим методом состоит в том, что он сравнивает только данные, привязанные к двум объектам синтаксиса, и фактически не будетсравните их обязательную информацию.

Аналогия, которую я слышал (от Райана Калпеппера ), это то же самое, что взять две картины, выпить их цвета и посмотреть,они одинаковые.Хотя они могут быть похожи в некоторых отношениях, вы пропустите много отличий от разных цветов.

Лучший подход (хотя и требует некоторой работы), это использовать syntax-e чтобы разрушить объект синтаксиса в более примитивные списки объектов синтаксиса и делать это до тех пор, пока вы не получите идентификаторы (в основном, объект синтаксиса, данные которого являются символом), оттуда обычно можно использовать free-identifier=? (а иногда bound-identifier=?, чтобы увидеть, может ли каждый идентификатор связывать друг друга, и identifier-binding для сравнения идентификаторов уровня модуля.

Причина, по которой не существует ни одного простого предиката для сравнения двух объектов произвольного синтаксиса, заключается в том, что, как правило, на самом деле не существует одного хорошего определения того, что делает два фрагмента кода равными, даже если вы заботитесь только о синтаксическом равенстве. Например,использование функций, упомянутых выше, не отслеживает внутренние привязки в объекте синтаксиса, поэтому вы все равно получите очень строгое определение того, что значит быть«Равно».то есть оба объекта синтаксиса имеют одинаковую структуру с идентификаторами, которые либо связаны с одним и тем же модулем, либо free-identifier=?.

Таким образом, прежде чем использовать этот ответ, я настоятельно рекомендую вам сделать шаг назади убедитесь, что это действительно то, что вы хотите сделать.Однажды на голубой луне это так, но большую часть времени вы на самом деле пытаетесь решить похожую, но более простую проблему.

...