方案,何时使用符号而不是字符串?

时间:2010-06-26 21:40:34

标签: scheme stream regular-language

我提前为我的原始英语道歉;我会尽力避免语法错误等。

两个星期前,我决定更新我对Scheme(及其启示)的了解,同时实现我手中的一些数学材料,特别是来自自动机理论和我所注册的计算课程的常规语言。

到目前为止,我一直将字母表作为符号列表而不是

  1. 字符列表,因为我想要有可变大小的字母。
  2. 字符串列表,因为我有点觉得它不够优雅。
  3. 我没有经验,想知道你对这个特殊选择的看法。是否为某些特定任务保留了符号,这是因为我滥用了它们?对此我的任何评论都非常感谢,因为我正在寻求指导。

    在进一步的范围内,还有时间在字母表上实现所有可能的单词集,这是无限的。我正在考虑通过允许的单词的最大大小来限制集合。再说一遍,这是一个很好的做法,还是应该代替流?我觉得溪流会是一种更好的方法,但我还没有学会它们,所以我真的不知道使用它们的含义。

    无论如何,欢迎任何建议或评论。我非常感谢您花时间阅读我的疑惑。祝周末愉快!

1 个答案:

答案 0 :(得分:7)

简单的区别是符号比较便宜。可以使用eq?将两个符号进行比较。这是因为在编译期间,编译器基本上会枚举所有带数字的符号,所以你只是比较整数,这是一个非常便宜的机器操作。

这意味着当且仅当两个符号由相同的字符组成时,它们是相同的。没有办法辨别代码中具有相同字符的两个符号,它们是常量,它们是相同的对象,就像33一样。

然而,两个字符串很可能是驻留在不同内存位置的不同对象,要比较它们需要分别比较每个字符以进行匹配。

因此,符号应该并且通常用于此,例如,考虑:

(assq 'symb  '((foo 1 2 3) (bar symb) (symb "match")))

这将返回列表(symb "match"),这与比较一样便宜:

(assq 4  '((0 1 2 3) (1 symb) (4 "match")))

返回列表(4 "match")。但是,当使用字符串作为键assq不再足够时,使用eq?过程进行比较时,必须使用assoc,它使用equal?过程,即因为它递归地比较结构,所以成本更高。上面的实现实际上足够便宜,通常可以用作在解释器中建模关联数组和环境的方法,即使它肯定不是随机访问。

编辑:正如你所问,在溪流上:

Scheme标准支持一个很好的对,一个是名为force的函数,另一个是名为delay特殊形式。实际上是什么

(delay (+ 1 2 3))

或者代替(+ 1 2 3)返回的任何其他代码都是所谓的“承诺”,它延迟了该承诺中答案的计算,但承诺在评估{{{}时,结果将是相同的。 1}}我们到达那里。这可能看起来毫无用处,但是说结果取决于一些可以改变的变量:

6

很明显,承诺确实只被评估过一次,当再次被强制时,它会产生与第一次评估时相同的值。

这是流中常用的或概念性的“无限列表”。在这种情况下,列表的cdr是对列表的其余部分的承诺,当它被检索时被强制执行,否则它将转变为非终止计算,例如,通常我们定义:

(define x 4) ; x is bound to 4.
(let ((promise (delay (+ 2 x)))) ; evaluating the expression now would result into 6, but we delay it.
  (set! x 5) ; we change the value of x.
  (display (force promise)) ; displays 7
  (set! x 6) ; change it again
  (force promise) ; ALSO displays 7, not 8
)

由于这个人无法评估其论点,因此需要采用特殊形式:

(define (stream-cdr x) (force (cdr x))) ; it forces that promise to evaluate it.
; and optionally
(define stream-car car) ; same

您的Scheme编译器或解释器现在会将(define-syntax stream-cons (syntax-rules () ((stream-cons x y) (cons x (delay y)))) 的每次出现转换为任意x和y的(stream-cons x y)

所以,作为一个简单的例子,既然我们的评估被推迟到我们需要它,我们就可以创建一个无限的零列表:

(cons x (delay y))

列出了自己的列表,如果我们没有使用流,那肯定永远不会终止,没用,但它显示了这个想法。我们可以一次又一次地使用(define zero-stream (stream-cons 0 zero-streams)) ,而无需到达空列表,我们只会再次获得相同的'0'的无限列表。

一个更实际的例子是所有斐波那契数字的列表,这是一些更复杂的东西:

stream-cdr

Stream-map是法线贴图的模拟,它的定义非常复杂,但我相信你可以查找它,它会自己生成一个流。所以`(stream-map(lambda(x y)(+ 1 x y))零(零)将生成一个完全填充一个的流。纤维流本身是递归定义的。给出前两个元素,其余的是从两个流中计算出来的,这两个流恰好是fibs和fibs本身的stream-cdr。