以编程方式填写Scheme中的letrec。宏还是评估?

时间:2009-12-13 02:45:36

标签: macros scheme

我正在玩NFA进行字符串识别。我有一个宏,它创建一个消耗输入的函数,并将其余的函数传递给其他函数。因为我的NFA图中可能有循环,所以我使用letrec将整个事物放在一起。这是一些代码(在PLT-Scheme中测试):

(define-syntax-rule (match chars next accepting)
  ; a function that consumes a list of chars from a list l. 
  ; on success (if there's more to do) invokes each of next on the remainder of l.
  (lambda (l) 
    (let loop ((c chars) (s l))
      (cond
        ((empty? c)
         (cond 
           ((and (empty? s) accepting) #t)
           (else 
            (ormap (lambda (x) (x s)) next))))
        ((empty? s) #f)
        ((eq? (car c) (car s)) 
         (loop (cdr c) (cdr s)))
        (else #f)))))

; matches (a|b)*ac. e .g. '(a a b b a c)
(define (matches? l)
  (letrec
      ([s4 (match '( ) '()        #t)]
       [s3 (match '(c) `(,s4)     #f)]
       [s2 (match '(a) `(,s3)     #f)]
       [s1 (match '( ) `(,s2 ,s5) #f)]
       [s5 (match '( ) `(,s6 ,s7) #f)]
       [s6 (match '(a) `(,s8)     #f)]
       [s7 (match '(b) `(,s8)     #f)]
       [s8 (match '( ) `(,s1)     #f)])
    (s1 l)))


(matches? '(a c))
(matches? '(a b b b a c))
(matches? '(z a b b b a c))

现在,如果我有一个简单的数据结构来表示我的NFA,就像列表一样。 e.g。

'((s4 () () #t)
  (s3 (c) (s4) #f) 
  ...)

我的问题是:我如何将该列表转换为以前的letrec语句?我对Macros不太好,我的理解是我可能不应该使用eval。

3 个答案:

答案 0 :(得分:4)

如果列表在编译时已知(我的意思是,在程序开始运行之前),那么您可以使用宏。否则,您必须使用eval

没关系。这是eval的良好用途之一。 :)

答案 1 :(得分:3)

我想出了这个似乎可以完成工作的宏 (我也不是专家):

(define-syntax nfa
  (syntax-rules (let-bindings)
    ; All the let bindings have been expanded
    [(nfa start (let-bindings . bindings))
     (lambda (l) (letrec bindings (start l)))]
    ; Otherwise, expand the next binding
    [(nfa start (let-bindings . bindings) (s c n a) . rest)
     (nfa start (let-bindings (s (match 'c (list . n) a)) . bindings) . rest)]
    ; Insert the expanded bindings list
    [(nfa start states)
     (nfa start (let-bindings) . states)]))

; matches (a|b)*ac. e .g. '(a a b b a c)
(define matches?
  (nfa s1 ([s4 ( ) ()      #t]
           [s3 (c) (s4)    #f]
           [s2 (a) (s3)    #f]
           [s1 ( ) (s2 s5) #f]
           [s5 ( ) (s6 s7) #f]
           [s6 (a) (s8)    #f]
           [s7 (b) (s8)    #f]
           [s8 ( ) (s1)    #f])))

诀窍是使用中间形式来创建“子句循环”, 并保留标识符(参见let-bindings)以区分这些中间形式 直接使用宏。

答案 2 :(得分:1)

我认为您的问题可以分为 2子问题

  1. 编写一个消耗NFA描述并自动生成NFA的宏,我称之为 make-NFA
  2. 将make-NFA应用于以编程方式生成的列表,我将此宏称为 apply-macro
  3. 第二个子问题很简单:

    (define-syntax apply-macro
      (syntax-rules ()
        ((_ macro ls)
         (eval 
          `(macro ,@ls)
          (interaction-environment)))))
    ;(define ls '(1 2 3))
    ;(apply-macro if ls)=>2
    

    第一个问题,我有一个DFA样本,你可以自己写一个NFA:

    (define-syntax make-DFA
      (syntax-rules (: ->)
        ((_ init-state (state : result (symbol -> next) ...) ...)
         (letrec 
             ((state 
               (lambda(sigma)
                 (cond
                   ((null? sigma) result)
                   (else
                    (case (car sigma)
                      ((symbol) 
                       (next (cdr sigma)))...
                      (else false))))))... )
           init-state)))) 
    
    (define DFA1
      (make-DFA q1
                 (q1 : true (#\a -> q2)
                     (#\b -> q3))
                 (q2 : false (#\a -> q1)
                     (#\b -> q4))
                 (q3 : false (#\a -> q4)
                     (#\b -> q1))
                 (q4 : true (#\a -> q3)
                     (#\b -> q2))))
    (DFA1 (string->list "ababa"));=>#f
    

    好吧,可能是define-macro是实现apply-macro的更好方法。