;; The first three lines of this file were inserted by DrRacket. They record metadata ;; about the language level of this file in a form that our tools can easily process. #reader(lib "htdp-intermediate-lambda-reader.ss" "lang")((modname matching-examples) (read-case-sensitive #t) (teachpacks ()) (htdp-settings #(#t constructor repeating-decimal #f #t none #f () #f))) (require racket/match) #| Pattern matching: Inspired by Prolog, where [X,3,Z] = [2,3,4] has the solution: X=2, Z=4 [X,3,X,4|Rst] = [2,3,2,4,5,6] has the solution: X=2, Rst=[5,6] Also adopted by python: (x,y,z) = (2,3,4) assigns x=2,y=3,z=4 [x,y,*r] = [2,3,4,5,6] assigns x=2,y=3,z=[4,5,6] with some reasonable but slightly odd behavior: [x,y,*r] = (2,3,4,5,6) assigns x=2,y=3,z=[4,5,6] (mix collection-types?) [x,y,*r] = 'hello' assigns x='h', y='e', r = ['l','l','o'] (not 'llo') (x,y,*r) = (2,3,4,5,6) assigns x=2,y=3,z=[4,5,6] (tuple faking fixed-size?) BUT not prolog's full pattern matching: [x,3,x,4,*rst] != [2,3, 2,4,5,6] is NOT legal (no constants on left-hand-side) [x,a,x,b,*rst] = [2,3,999,4,5,6] assigns x=2 THEN x=999 !!, y=[5,6] and a=3,b=4 Racket's match is as general as Prolog's, but not as concise as python's simple version. See the remaining examples, for a bunch of related matching constructs. |# ; 'match' does pattern-matching between a target-value, ; and a pattern-containing-variables. ; Its syntax is reminiscent of 'cond': (match (list 1 2 3) [(list a b a) (+ a b)] [(list a b c) (* (+ a b) c)]) (match (list 1 2 1) ; notice constraint when re-using vars: [(list a b a) (+ a b)] [(list a b c) (* (+ a b) c)]) ; Once we have this, there is a 'matching' forms of lambda: ; it's a little bit like overriding: ; (define/match (func1 nums) [((list a b a)) (+ a b)] [((list a b c)) (* (+ a b) c)] [((cons a rst)) a] [(nonlist) (format "got a non-list, ~v." nonlist)]) (func1 (list 1 2 3)) (func1 (list 1 2 1)) (func1 (list 3 4 5 6 7)) (func1 3.14) ; Here's a corresonding function with 3 params (rather than one list): (define/match (func3 x y z) [(a b a) (+ a b)] [(a b c) (* (+ a b) c)]) (func3 1 2 3) (func3 1 2 1) ; Minor note: there is also an anonymous-function version: ; (define func1a (match-lambda [(list a b a) (+ a b)] [(list a b c) (* (+ a b) c)])) (func1a (list 1 2 3)) (func1a (list 1 2 1)) ; `match` can be de-structure structs, as well as lists: ; (define-struct truck (x-pos y-pos speed)) (define lorry (make-truck 4 7 2)) (match-define (struct truck (a b c)) lorry) (check-expect (+ a b c) (+ 4 7 2)) ; we can combine matching, with the actual-arguments passed to a function: (define/match (move-truck1 tr) [((truck x y s)) (make-truck (+ x s) y s)]) ; Note that define/match can "overload" to try many different input-patterns, ; as `match` above, which is why it has the square-brackets. (We try only one pattern.) (check-expect (move-truck1 (make-truck 1 2 3)) (make-truck 4 2 3)) ; the square-brackets required reminiscent of `match` (and we could have further ; square-bracket-pairs if we wante3d to) ; The extra parens around `(truck x y s)` is for a list of all our arguments; ; since we only had one argument `tr` we only have one thing inside the parens. ; The name `tr` is of course bound to the entire actual-argument; we just never used it. ; ; Btw, in full-racket the name of the struct and the name of the constructor ; coincide (unlike student-langs, which use `make-` for constructor). ; Fwiw, the above define/match is the same as using `match` or `match-let` inside: ; (define (move-truck2 tr) (match tr [(truck x y s) (make-truck (+ x s) y s)])) (define (move-truck3 tr) (match-let {[(truck x y s) tr]} (make-truck (+ x s) y s))) (check-expect (move-truck2 (make-truck 1 2 3)) (make-truck 4 2 3)) (check-expect (move-truck3 (make-truck 1 2 3)) (make-truck 4 2 3)) ; None of these, though, do pattern-matching on the function-argument "directly". ; python2 used to do that, but it was removed in python3 as a "misfeature". ; Finally, you can also de-structure vectors, strings (via regular-expressions), etc. ; Search docs for 'match-define': http://docs.racket-lang.org/reference/match.html?q=define%2Fmatch#%28form._%28%28lib._racket%2Fmatch..rkt%29._define%2Fmatch%29%29 ; I find the names `match-define` and `define/match` too similar, and often forget which is which. ; Mnemonic: the order makes sense: ; match-define first matches, and then it defines vars; ; define/match makes a func-definition which does matching when called. ; Note: using 'quote' in the match-pattern will quote symbols; ; you must use quasiquote ; to unquote any variables you want matched.