Clojure простое сопоставление с образцом - PullRequest
4 голосов
/ 14 июня 2011

В продолжение моего предыдущего вопроса я пытаюсь реализовать простое сопоставление с образцом в Clojure.

Я бы хотел что-то вроде следующего:

(match target
  [ub]    expr1    ; ub should be bound to actual value in expr1
  ['< ub] expr2    ; match the literal less-than symbol
                   ; and ub should be bound to actual value in expr2
  [lb ub] expr3    ; lb and ub should be bound to actual values in expr3
  :else   expr4    ; default case if none match
)

Использование:

(match [< 5.0] ...)

должен организовать выполнение expr2 во время выполнения.

Я хотел бы написать макрос, но я не уверен в расширении.

Я рассматриваю вопрос о расширении каждого case-and-clause до let с привязками к внутренним переменным и проверкой того, что литеральные символы ('<) действительно соответствуют шаблону. Может быть для второго шаблона (['< ub]):

(let [[sym1 ub] pattern]
  (if (= '< sym1)
    expr1)

Нужно ли использовать (gensym) для привязок? Как?

Увеличенное изображение:

(range-case target
            [0.0 < 1.0] :greatly-disagree
            [< 2.0]     :disagree
            [< 3.0]     :neutral
            [< 4.0]     :agree
            [5.0]       :strongly-agree
            42          :the-answer
            :else       :do-not-care)

Я пытаюсь сопоставить шаблоны [...] и преобразовать их в следующее:

[ub]          (if previous-ub `(and (<= ~previous-ub ~target) (<= ~target ~ub))
                              `(< ~target ~ub))
['< ub]       (if previous-ub `(and (<= ~previous-ub ~target) (< ~target ~ub))
                              `(< ~target ~ub))
[lb ub]       `(and (<= ~lb ~target) (<= ~target ~ub))
['< lb ub]    `(and (< ~lb ~target) (<= ~target ~ub))
[lb '< ub]    `(and (<= ~lb ~target) (< ~target ~ub))
['< lb '< ub] `(and (< ~lb ~target) (< ~target ~ub))

У меня есть cond, который проверяет, что регистр является вектором. Это сопоставление с образцом должно происходить внутри этого случая.

1 Ответ

3 голосов
/ 15 июня 2011

Моя первая идея была в основном такой же: привязывать вещи к внутренним местным жителям и тестировать их содержимое в большом and.Для литералов значение связано с сгенерированным локальным;символы используются непосредственно в привязке.

Я также добавил, что вектор спецификации соответствует длине целевого вектора.В противном случае вы не можете иметь [ub], а также [lb ub], поскольку ни одна из них не содержит проверку, которая может быть неудачной.Поэтому всегда будет выбран первый.

Вот код:

(defn make-clause
  [expr-g [spec expr & more :as clause]]
  (when (seq clause)
    (let [tests-and-bindings (map (fn [x]
                                    (if-not (symbol? x)
                                      (let [x-g (gensym "x")]
                                        [`(= ~x ~x-g) x-g])
                                      [nil x]))
                                  spec)
          tests    (keep first tests-and-bindings)
          bindings (map second tests-and-bindings)]
      `(let [[~@bindings] ~expr-g]
         (if (and (= (count ~expr-g) ~(count spec)) ~@tests)
           ~expr
           ~(make-clause expr-g more))))))

(defmacro match
  [expr & clauses]
  (let [expr-g  (gensym "expr")]
    `(let ~[expr-g expr]
       ~(make-clause expr-g clauses))))

И пример расширения.Я не использовал синтаксическую кавычку в примере, чтобы уменьшить шум в расширении, но вы должны понять:

(let [expr98 [(quote <) 3.0]]
  (let [[ub] expr98]
    (if (and (= (count expr98) 1))
      (if previous-ub
        (and (<= previous-ub target) (<= target ub))
        (< target ub))
      (let [[x99 ub] expr98]
        (if (and (= (count expr98) 2) (= (quote <) x99))
          (if previous-ub
            (and (<= previous-ub target) (< target ub))
            (< target ub))
          (let [[lb ub] expr98]
            (if (and (= (count expr98) 2))
              (and (<= lb target) (<= target ub))
              nil)))))))

Вызов был:

(match ['< 3.0]
  [ub]    (if previous-ub
            (and (<= previous-ub target) (<= target ub))
            (< target ub))
  ['< ub] (if previous-ub
            (and (<= previous-ub target) (< target ub))
            (< target ub))
  [lb ub] (and (<= lb target) (<= target ub))))

Надеюсь, чтопоможет вам начать работу.

...