我正在研究无上下文的语法,我有一个返回(语法的终值)的函数
例如: 我有非终端功能导致(A B),从呼叫说((A猫)(B happy np)(A B伤心)) 所以技术上A和B是语法的非终端。现在我希望能够得到终端(猫快乐np伤心)
(define terminals
(lambda (lsts)
(cond
((null? lsts) lsts)
((not(member? (car(car lsts)) (n-terminals lsts)))
(cons (car(car lsts)) (terminals (car (cdr lsts)))))
(else (terminals (cdr lsts))))))
PS:以上描述了n端子的功能。 会员?是一个布尔函数,如果一个项是列表的成员,则返回true,否则返回false。 我的函数返回一个空的lst。我在这里缺少什么?
答案 0 :(得分:1)
问题在于找到终端符号时的操作。这里确实存在两个不同的问题。
首先,您只需查看每个规则的第一个符号。您的lsts
变量是一系列规则;每个规则都是符号列表。因此(car (car lsts))
会为您提供第一条规则的第一个符号。
第二个问题是你找到终端符号后如何递归。通常,您会传递terminals
列表。但是,在您找到终端符号(即以(member? ...)
开头的符号)后的情况下,您将其传递给(car (cdr lsts))
。我们知道lsts
是一个列表列表。所以(cdr lsts)
也是一个列表列表(当然还是一个空列表)。这意味着(car (cdr lsts))
只是普通列表。
我认为解决这个问题的最简单方法是将其分解为两个函数。首先,编写一个从单规则获取终端的函数。也就是说,不要只关注整个输入((A cat) (B happy np) (A B sad))
,而是像(B happy np)
那样关注一个规则。所以编写一个函数,给定(B happy np)
,给你(happy np)
。接下来,编写一个函数,将此函数应用于lsts
中的每个规则,并组合所有输出。 (您可能会发现append
函数在这里很有用。)
将其写入两个单独的函数应该可以更容易地考虑问题。生成的代码也将更简单,更容易阅读,这是一个奖励。
另外,风格无关紧要:你可以(而且应该)写
(define foo
(lambda (args) ...))
as
(define (foo args)
...)
这两个表达式具有相同的含义,但第二个表达式更易于阅读和使用。
编辑:正如我在评论中所说,我会首先计算非终端列表,并将其用于一次执行一行的函数。你可以通过嵌套你的两个函数并利用Scheme的词法范围来使这个变得简单:(define (terminals rules)
(let ((non-terminals (n-terminals rules)))
(define (helper rule) ; this takes a rule and returns all its terminal symbols
... ; you can use non-terminals in here
)
; now here you have to call helper on every rule and combine the results
))
另一种选择是将其分解为两个非嵌套的函数,并传入一个非终端符号列表:
(define (terminals-from-rule rule non-terminals)
... ; implement, using non-terminals as the list of non-terminal symbols
)
(define (terminals rules)
... ; here you need to calculate the list of non-terminals and pass
; it into each call to terminals-from-rule along with the rule.
)