让我们从最初的问题快速绕道一些稍微相关的问题。假设您得到以下内容:您想编写一个评估器,它接受“字符串构建”表达式,(* 3 "hello")
并将其“评估”为“hellohellohello”。我们想做的其他例子包括
(+ "rock" (+ (* 5 "p") "aper")) ==> "rockpppppaper"
(* 3 (+ "scis" "sors")) ==> "scissorsscissorsscissors"
为了解决这样的问题,我们需要准确地指定输入的形状。本质上,我们想要描述一种数据类型。我们会说我们的输入将是“字符串表达式”。让我们str-exprs
简称他们。然后让我们定义什么是str-expr
.
Astr-expr
是:
<string>
(+ <str-expr> <str-expr>)
(* <number> <str-expr>)
通过这种表示法,我们试图说str-expr
s 都适合这三种形状中的一种。
一旦我们对数据的形状有了很好的了解,我们就有更好的指南来编写处理函数str-exprs
:它们必须对这三个备选方案进行案例分析!
;; A str-expr is either:
;; a plain string, or
;; (+ str-expr str-expr), or
;; (* number str-expr)
;; We want to write a definition to "evaluate" such string-expressions.
;; evaluate: str-expr -> string
(define (evaluate expr)
(cond
[(string? expr)
...]
[(eq? (first expr) '+)
...]
[(eq? (first expr) '*)
...]))
'...' 是我们将要填写的内容。
实际上,我们知道如何填写更多关于 '...' 的内容:我们知道在第二种和第三种情况下,内部部分本身就是 str-exprs。这些是可能发生重复的主要地点:由于我们的数据是根据自身描述的,处理它们的程序也可能需要引用它们自己。简而言之,处理str-expr
s 的程序几乎肯定会遵循这种形式:
(define (evaluate expr)
(cond
[(string? expr)
... expr
...]
[(eq? (first expr) '+)
... (evaluate (second expr))
... (evaluate (third expr))
...]
[(eq? (first expr) '*)
... (second expr)
... (evaluate (third expr))
...]))
这一切甚至都没有做任何实际工作:我们可以完全弄清楚这部分,因为这就是数据的形状告诉我们的。填写 '...' 的其余部分以使这一切顺利进行实际上并不算太糟糕,尤其是当我们还考虑我们已经编写的测试用例时。(代码)
正是这种标准的数据分析/案例分析是您问题的核心,它是HTDP等课程广泛涵盖的内容。这不是特定于 Scheme 或 Racket 的:您会在 Java 中进行相同类型的数据分析,并且您会在许多其他地方看到相同类型的方法。在 Java 中,用于案例分析的底层机制可能会有所不同,也许是动态调度,但核心思想都是一样的。您需要描述数据。一旦有了数据定义,就可以使用它来帮助您勾勒出处理该数据所需的代码。使用测试用例来确定如何填写草图。