如何在scheme中使用符号和列表来处理数据?
我是scheme的新手,我正在编写一个检查规则成对不相交的函数(目前还不完整),我使用符号和列表来表示语法的错误。大写符号在语法中是非终结符,小写符号是终结符。我试图检查规则是否通过了成对不相交测试如何在scheme中使用符号和列表来处理数据?,scheme,racket,grammar,bnf,Scheme,Racket,Grammar,Bnf,我是scheme的新手,我正在编写一个检查规则成对不相交的函数(目前还不完整),我使用符号和列表来表示语法的错误。大写符号在语法中是非终结符,小写符号是终结符。我试图检查规则是否通过了成对不相交测试 我将基本上检查规则中是否只有一个唯一的终端。如果是这样,则该规则通过了成对不相交测试。在scheme中,我想通过用小写表示终端符号来实现这一点。该规则的一个例子是: '(A <= (a b c)) 由于grammar在一个列表中有一个列表,我认为您必须在调用symbol->string之前
'(A <= (a b c))
由于
grammar
在一个列表中有一个列表,我认为您必须在调用symbol->string
之前通过list?
进行测试(因为您发现,symbol->string
在列表中不起作用),否则您可以执行以下操作:
(map symbol->string (flatten grammar))
> '("A" "<=" "a" "A" "b")
(地图符号->字符串(展开语法))
>“(“A”)成对不相交
据我所知,检查集合是否成对不相交的唯一方法是枚举所有可能的对并检查匹配项。请注意,这并不遵循racket语法,但其含义应该仍然非常清楚
(define (contains-match? x lst)
(cond ((null? x) #f) ; Nothing to do
((null? lst) #f) ; Finished walking full list
((eq? x (car lst)) #t) ; Found a match, no need to go further
(else
(contains-match? x (cdr lst))))) ; recursive call to keep walking
(define (pairwise-disjoint? lst)
(if (null? lst) #f
(let ((x (car lst)) ; let inner vars just for readability
(tail (cdr lst)))
(not
;; for each element, check against all later elements in the list
(or (contains-match? x tail)
(contains-match? (car tail) (cdr tail)))))))
我不清楚您还想做什么,但这将是常规方法。根据您的数据,您可能需要使用不同(甚至定制)的相等性检查,但这与普通符号一样有效:
]=> (pairwise-disjoint? '(a b c d e))
;Value: #t
]=> (pairwise-disjoint? '(a b c d e a))
;Value: #f
符号与数据
本节基于我认为OP对scheme basics的一个基本误解,以及对他们实际目标的一些猜测。如果下一部分对您没有帮助,请澄清问题!
然而,让我困惑的是如何使用这些符号作为数据
在scheme中,你可以将一个符号与你想要的任何东西相关联。事实上,define
关键字实际上只是告诉解释器“每当我说包含匹配?
(这是一个符号)我实际上指的是那边的一大组指令,所以记住这一点。解释器通过将符号和它所指的东西存储在一个大表中来记住这一点,以便以后可以找到它
每当解释器遇到一个符号时,它都会查看它的表,看看它是否知道它的实际含义,并替换实际值,在本例中是一个函数
]=> pairwise-disjoint?
;Value 2: #[compound-procedure 2 pairwise-disjoint?]
我们告诉解释器保留符号,而不是使用quote运算符,”
或(quote…
):
综上所述,出于全局变量通常不好的原因,使用define
进行定义可能是一个非常糟糕的决定
为了保存对语法很重要的所有特定符号的定义,您可能正在寻找类似于a的东西,其中您知道的每个符号都是一个键,其细节是相关的值
而且,如果你想传递符号,你真的需要理解quote
和quasikote
一旦你在某个地方找到了自己的定义,剩下的唯一工作就是写一些我在上面做过的东西,这些东西可能更适合你的特定情况。数据类型
如果您有终端和非终端,为什么不为每个终端创建数据类型呢?在#lang racket
中,引入新数据类型的方法是使用struct
;; A Terminal is just has a name.
(struct Terminal (name))
;; A Non-terminal has a name and a list of terms
;; The list of terms may contain Terminals, Non-Terminals, or both.
(struct Non-terminal (name terms))
(define (find-terminals non-terminal)
(filter Terminal? (Non-terminal-terms non-terminal)))
处理非终端
现在,我们可以使用谓词Terminal?
在非终端的术语列表中找到终端,当我们将终端定义为struct
时,会自动提供该谓词
;; A Terminal is just has a name.
(struct Terminal (name))
;; A Non-terminal has a name and a list of terms
;; The list of terms may contain Terminals, Non-Terminals, or both.
(struct Non-terminal (name terms))
(define (find-terminals non-terminal)
(filter Terminal? (Non-terminal-terms non-terminal)))
成对不相交端子
过滤术语列表后,我们可以确定属性:
;; List(Terminal) -> Boolean
define (pairwise-disjoint? terminals)
(define (roundtrip terms)
(set->list (list->set terms)))
(= (length (roundtrip terminals)
(length terminals))))
当然,往返列表->设置->列表不一定是为了速度而优化的,并且分析实际工作的实现可能会证明重构是合理的,但至少它是被黑框的
笔记
使用struct
定义数据类型提供了在类型实例化时验证数据的各种选项。如果您查看Racket代码库,您将看到struct
在最近的部分中经常使用。不会(A这只是我在问题中发布的语法的简化版本。实际上,还有另一条规则指定了A,这意味着我们不会陷入无限递归。谢谢!我正在试图弄清楚解析器应该如何工作。是否有两条规则分别指定A
,使其成为一个重叠规则aded token,或者我误解了的意思,这是完全正确的。没有必要只有两个token指定“A”,但我们可以有更多。对于@user1680944,我在回答中添加了一个关于符号在scheme中如何工作的部分。听起来你只需要理解解释器“替换”它们的方式还有一个保存您的定义的地方。谢谢您的详细回答。但是,我想知道如何运行该函数。您是否可以提供一个执行示例?@user1680944老实说,您的问题不太清楚您想做什么。如果您编辑您的问题以提供示例输入,这将非常有用s、输出和错误。
(define (find-terminals non-terminal)
(filter Terminal? (Non-terminal-terms non-terminal)))
;; List(Terminal) -> Boolean
define (pairwise-disjoint? terminals)
(define (roundtrip terms)
(set->list (list->set terms)))
(= (length (roundtrip terminals)
(length terminals))))