Я новичок в Common Lisp и в качестве первого проекта работал над простым сопоставлением шаблонов. У меня возникли проблемы с использованием оператора звездочки (*) для представления 0 или более любых элементов в списке. Таким образом, шаблон (x * z) и сопоставитель (x y y y z) вернут true, но образец (x * z) и сопоставитель (x y) вернут false.

Мои первые мысли:

(loop for x in pattern-list
  (eq x '*)
  ;if x is *, pause iterating through this list
  (loop for y in matcher-list
        ;somehow iterate one more value in the pattern list
        (eq x y) ;does the value just after the * in the pattern list equal the value in y?
        ;if they aren't the same symbol, just iterate matcher until they match, then resume incrementing though the pattern list
))

Извините, если мой синтаксис и скобки немного неправильны.

Это меньшая часть по сравнению с большим устройством сопоставления шаблонов, над которым я работал. Вот что у меня есть (в этом случае list1 - это список шаблонов, а список2 - список сопоставлений):

Большая часть этого кода возникла из этого сообщения SO:

Настройка одинаковой функции в общем lisp с использованием только "eq"

(defun comp-q (list1 list2) ;defun
  (if (and (not (null list1)) ;if list1 is not null AND
       (not (null list2))) ;if list2 is not null
  (let ((a (car list1)) (b (car list2))) ;a is the car (front) of list1 and b is the car of list 2
    (cond ((and (listp a) (listp b)) ;cond, evaluate the first thing in the list - are a and b lists?
           (and (comp-q a b) ;recursive call on a and b
                (comp-q (cdr list1) (cdr list2)))) ;recursive call on the cdr (tail) of a and b
          (t ;like an else for cond
           (and (or (eq a b) (eq a '?)) ;are a and b equal OR is a a '?'
                (comp-q (cdr list1) (cdr list2)))))) ;recursive call on the cdr of a and b
  (= (length list1) (length list2)))) ;are the lists equal?  only triggered if the null test fails (are they both not null)

Лучше всего использовать макрос loop? Можно ли «приостановить» или отслеживать итерации по списку (я знаю, что это похоже на массив)? Или я должен попытаться продолжить работу рекурсивно, вызывая car и cdr каждого списка, который реализуется в comp-q defun?

Спасибо.

0
JAL 15 Сен 2013 в 11:07
1
Вы не можете вложить loop. В противном случае для каждого x в pattern-list вы будете перебирать весь matcher-list. Я рекомендую использовать рекурсивный подход, по крайней мере, для начала - он должен быть более интуитивным, чем использование loop. Начнем с определения базового случая: учитывая списки, соответствует ли первый элемент matcher-list шаблону? Если нет, потерпите неудачу; если да, примите первый элемент и рекурсивно вызовите с хвостами списков по мере необходимости.
 – 
jlahd
15 Сен 2013 в 11:33
Для начала существуют разные стратегии сопоставления. Итак, чтобы ответить вам, подходит ли loop для этого, вам нужно будет сказать, какие правила вы используете для сопоставления материалов. Подумайте о правилах CFG и PEG. Как вы решаете вопросы приоритета и выбора. Вы обязательно соответствуете самому длинному из возможных расширений или самому короткому и так далее. Я также хотел бы изучить библиотеку iterate, потому что она позволяет переносить итерационные макросы. Кроме того, уже существует множество библиотек сопоставления шаблонов CL, optima, по моему опыту, является хорошей.
 – 
user797257
15 Сен 2013 в 15:01
Я вспомнил о iterate, потому что у него есть генераторы, что-то, что позволит вам продвигать итератор, управляющий циклом, что похоже на конкретную проблему, с которой вы столкнулись.
 – 
user797257
15 Сен 2013 в 15:05

1 ответ

Лучший ответ

Поскольку никто еще не дал ответа и был предложен рекурсивный подход, я придумал пример в Racket, чтобы вы начали. Преобразование в Common Lisp должно быть простым.

(define (match pattern matcher)

  ; is the symbol a wildcard (i.e. does it end with an asterisk?
  ;   yes -> return true + the symbol without the asterisk
  ;   no  -> return false + the symbol itself
  (define (is-wildcard sym)
    (let ((str (symbol->string sym)))
      (if (string=? (substring str (sub1 (string-length str))) "*")
          (values #t (string->symbol (substring str 0 (sub1 (string-length str)))))
          (values #f sym))))

  ; we know wi is a wildcard; let's loop over matcher until done
  (define (match-wildcard wi pattern matcher)
    (if (empty? matcher)
        (list (cdr pattern) matcher)
        (if (eq? wi (car matcher))
            (match-wildcard wi pattern (cdr matcher))
            (list (cdr pattern) matcher))))

  ; main loop
  (if (or (empty? pattern) (empty? matcher))
      (and (empty? pattern )(empty? matcher))
      (let ((pa (car pattern)) (ma (car matcher)))
        (if (eq? pa ma)
            (match (cdr pattern) (cdr matcher))
            (let-values (((wildcard wi) (is-wildcard pa)))
              (if wildcard
                  (apply match (match-wildcard wi pattern matcher))
                  #f))))))

Примеры:

(match '(x y* z) '(x y y y z))
=> #t

(match '(x z* y) '(x y))
=> #t

(match '(x y* z) '(x y))
=> #f

(match '(x y*) '(x y))
=> #t

HTH!

1
uselpa 17 Сен 2013 в 23:48
Большое спасибо! В итоге я сделал что-то очень похожее в Common Lisp, но всегда здорово иметь несколько идей. ((eq a '*) ;is a a '*' (or (comp-q2 (cdr list1) list2 c) ;increment list1 but not list2 (comp-q2 list1 (cdr list2) c))) ;increment list2 but not list1
 – 
JAL
17 Сен 2013 в 04:25