Julebonus: ML-style Patternmatching i Clojure


tirsdag 20. desember 2011 Julekalender Clojure Pattern matching

I tilfelle du ikke synes det er nok med én blogpost hver dag har jeg her en knøttliten bonus til deg. Jeg har jo nå vist deg Euler 1 løst på forskjellige måter i et utall språk. Blant annet har jeg vist hvordan oppgaven kan løses ved hjelp av pattern matching – dette gjorde jeg i F# og i Nemerle, og Kråkelefse delte også en lignende løsning i Haskell.

Når jeg da nylig kom over et matching-biblotek for Clojure måtte jeg nesten teste denne muligheten ut også i det språket. Måtte leke meg litt. Man kan trygt si at multiplene av 3 og 5 har gjort meg litt ensporet. Anyways, here goes...

10 (ns euler1CljMatch.core
11     (:use [clojure.core.match :only [match]]))
12 
13 (let [mod-zero? #(zero? (mod %2 %1))]
14   (def mod-zero-3? (partial mod-zero? 3))
15   (def mod-zero-5? (partial mod-zero? 5)))
16 
17 (defn mult3or5? [x]
18   (match [x]
19     [(_ :when mod-zero-3?)] true
20     [(_ :when mod-zero-5?)] true
21     :else                   false))
22 
23 (defn euler-1 []
24   (loop [n 0, sum 0]
25     (match [n]
26       [ 1000 ]              sum
27       [(_ :when mult3or5?)] (recur (inc n) (+ sum n))
28       [ _    ]              (recur (inc n)    sum))))
29 
30 (println (euler-1))

Det er langt fra optimalt å bruke match til dette her, og det illustrerer heller ikke hva pattern matching er godt for. Men jeg måtte bare få det ut! Viktig å test nye muligheter.

For ordens skyld: I slike situasjoner (som det jeg har rotet meg opp i ved å forsøke å bruke match) foretrekker Lisp'ere å bruke en makro som heter cond. I dette tilfellet gir det mye renere syntaks:

17 (defn mult3or5? [x]
18   (cond (mod-zero-3? x) true
19         (mod-zero-5? x) true
20         :else           false))
21 
22 (defn euler-1 []
23   (loop [n 0, sum 0]
24     (cond (= n 1000)    sum
25           (mult3or5? n) (recur (inc n) (+ sum n))
26           :else         (recur (inc n)    sum))))

Og så var det fire dager igjen til Jul :D


comments powered by Disqus