1.11 S-Expression Matching
Since the equal? function works on any kind of value, it can compare two S-expressions to determine whether they are the same:
> (equal? `{+ 1 2} `{+ 1 2}) - Boolean
#t
> (equal? `{+ 1 2} `{+ 1 4}) - Boolean
#f
Suppose that you don’t just want to recognize `{+ 1 2}, but you want to recognize any list-like S-expression that has three elements where the first element is '+-like and the other two elements are number-like. That recognition problem is tedious to implement, due to the many required many checks and coercions.
> (define (is-plus-numbers? se) (and (s-exp-list? se) (let ([l (s-exp->list se)]) (and (= 3 (length l)) (let ([a (first l)]) (and (s-exp-symbol? a) (eq? '+ (s-exp->symbol a)))) (s-exp-number? (second l)) (s-exp-number? (third l)))))) > (is-plus-numbers? `{+ 1 2}) - Boolean
#t
> (is-plus-numbers? `1) - Boolean
#f
> (is-plus-numbers? `{+ 3 y}) - Boolean
#f
> (is-plus-numbers? `{{+} 1 2}) - Boolean
#f
The s-exp-match? function simplifies recognition tasks for S-expressions. It’s like equal? on S-expressions, but the first S-expression can have special symbols that match different classes of values, instead of matching themselves literally. The special symbols include NUMBER, which matchs any number, so is-plus-numbers? is more simply implemented like this:
> (define (is-plus-numbers? se) (s-exp-match? `{+ NUMBER NUMBER} se)) > (is-plus-numbers? `{+ 1 2}) - Boolean
#t
> (is-plus-numbers? `{+ 3 y}) - Boolean
#f
Other special symbols include SYMBOL, which matches any symbol, and ANY, which matches anything.
> (define (single-argument-lambda? se) (s-exp-match? `{lambda {SYMBOL} ANY} se)) > (single-argument-lambda? `{lambda {x} {+ x 1}}) - Boolean
#t
> (single-argument-lambda? `{lambada 0}) - Boolean
#f
The symbol ... is even more special. It causes the preceeding S-expression to match zero or more times to cover multiple elements in an enclosing list. For example, `{SYMBOL ...} would match a list-like S-expression that has any number of symbol-like elements.
> (define (any-argument-lambda? se) (s-exp-match? `{lambda {SYMBOL ...} ANY} se)) > (any-argument-lambda? `{lambda {x} {+ x 1}}) - Boolean
#t
> (any-argument-lambda? `{lambda {x y z} {+ x 1}}) - Boolean
#t
> (any-argument-lambda? `{lambda {} {+ x 1}}) - Boolean
#t
> (any-argument-lambda? `{lambada 0}) - Boolean
#f