【问题标题】:Programatically filling in a letrec in Scheme. Macros or eval?以编程方式填写 Scheme 中的 letrec。宏还是评估?
【发布时间】:2010-12-26 02:35:43
【问题描述】:

我只是在使用 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,比如列表列表。例如

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

我的问题是:如何将该列表转换为之前的 le​​trec 语句?我对宏不太了解,我的理解是我可能不应该使用 eval。

【问题讨论】:

  • 出于好奇,match 是否有任何理由成为宏而不是常规函数?
  • 调用函数会导致参数被评估,需要避免这些参数才能让 letrec 工作。

标签: macros scheme


【解决方案1】:

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

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

【讨论】:

  • 听你这么说再次证实了我的想法。在有人证明并非如此之前,您会得到复选标记。
【解决方案2】:

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

(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)以区分这些中间形式 直接使用宏。

【讨论】:

  • +1。我感谢您的辛勤工作和示例,但我的问题是我的列表是以编程方式生成的。比如说,为了举例,它存储为nfa-rules,开头是s1。然后我希望能够做到(nfa s1 nfa-rules)。让宏“窥视内部”nfa 规则并迭代内容是我遇到的问题。
  • 我认为 Jason 所说的可能是正确的。如果我在编译时知道该列表,我可以使用您的宏来简化编码。但是,如果列表是通过程序生成的,我可能不得不在运行时使用eval 将其转换为适当的代码。
  • 如果您要将 NFA 作为数据进行操作,那么实现它们可能会更直接,并且当您必须在代码中硬编码其中一些时,使用宏仅是为了更容易输入.不过,您的 letrec/macro 技巧很巧妙 :-)
  • 此外,没有宏会“窥视”任何变量:它们会在完成任何评估之前操纵代码,因此就它们而言,nfa-rules 只是一个词。
【解决方案3】:

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

  1. 编写一个使用NFA描述并自动生成NFA的宏,我把这个宏称为ma​​ke-NFA
  2. 将 make-NFA 应用于以编程方式生成的列表,我将此宏称为 apply-macro

第二个子问题很简单:

(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 的更好方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-10
    • 1970-01-01
    • 2020-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多