如何在匹配子句中重用匹配模式?
How to reuse matching patterns in match clauses?
我需要匹配两种元组并从中生成映射。
两者都有关键字和字符串。可以有第三项(语言代码)。
[<key> <value>] ~> {:type <key> :value <value>}
[<key> <value> <lang>] ~> {:type <key> :value <value> :lang <lang>}
我只需要匹配关键字为 :foo
或 :bar
的关键字,并决定使用 clojure.core.match
:
(ns so.example
(:require
[clojure.core.match :refer [match]]))
(defn example-1 [ast]
(let [l10n-key #{:foo :bar}]
(match ast
[(k :guard l10n-key) v lang] {:type k :value v :lang lang}
[(k :guard l10n-key) v] {:type k :value v})))
(example-1 [:foo 10])
;=> {:type :foo, :value 10}
(example-1 [:bar 20 "en"])
;=> {:type :bar, :value 20, :lang "en"}
可行,但我想在不同的子句中重复使用匹配模式 :guard l10n-key
。所以我想我可以使用一些语法引用和取消引用拼接:
(defn example-2 [ast]
(let [l10n-key-match [:guard #{:foo :bar}]]
(match ast
[`(k ~@l10n-key-match) v lang] {:type k :value v :lang lang}
[`(k ~@l10n-key-match) v] {:type k :value v})))
但是 defn
表达式崩溃:
Unexpected error (AssertionError) macroexpanding match at (form-init11111096422056977084.clj:3:5).
Invalid list syntax (clojure.core/concat (clojure.core/list (quote so.example/k)) l10n-key-match) in (clojure.core/seq (clojure.core/concat (clojure.core/list (quote so.example/k)) l10n-key-match)). Valid syntax: [[:default :guard] [:or :default] [:default :only] [:default :seq] [:default :when] [:default :as] [:default :<<] [:default :clojure.core.match/vector]]
我做错了什么?
没有解决 clojure.core.match
的问题,但对于这么简单的事情你真的不需要它:
(ns tst.demo.core
(:use tupelo.core tupelo.test))
(dotest
(let [data [[:foo 10]
[:bar 20 "en"]
[:fizz 10]
[:buzz 20 "en"]]
keep-tags #{:foo :bar}
data-keep (filterv #(contains? keep-tags (first %)) data)
result (forv [tuple data-keep]
(zipmap [:type :value :lang] tuple))]
(is= result [{:type :foo, :value 10}
{:type :bar, :value 20, :lang "en"}])))
您可能还对这些辅助函数感兴趣:
- tupelo.core/matches?
clojure.core.match
更好的界面
- tupelo.core/submatch?
- tupelo.core/wild-match?
- tupelo.core/wild-submatch?
这不就是 Clojure 附带的 spec 吗?你会像
这样定义你的模式
(ns playground.catspec
(:require [clojure.spec.alpha :as spec]))
(spec/def ::type #{:foo :bar})
(spec/def ::value number?)
(spec/def ::lang #{"en" "sv" "fr"})
(spec/def ::key-value-lang (spec/cat :type ::type
:value ::value
:lang (spec/? ::lang)))
我们使用 spec/def
定义规范,spec/cat
连接规范,spec/?
定义可选规范。
然后我们使用conform
解析元组:
(spec/conform ::key-value-lang [:foo 10])
;; => {:type :foo, :value 10}
(spec/conform ::key-value-lang [:bar 20 "en"])
;; => {:type :bar, :value 20, :lang "en"}
(spec/conform ::key-value-lang [:bar 119 "fr"])
;; => {:type :bar, :value 119, :lang "fr"}
(spec/conform ::key-value-lang [119 :foo])
;; => :clojure.spec.alpha/invalid
(spec/conform ::key-value-lang [:bar 119 "uj"])
;; => :clojure.spec.alpha/invalid
(spec/conform ::key-value-lang [:bar])
;; => :clojure.spec.alpha/invalid
(spec/conform ::key-value-lang [:bar 119 "fr" :asdfasdf])
;; => :clojure.spec.alpha/invalid
(spec/conform ::key-value-lang {:a 1 :b 4})
;; => :clojure.spec.alpha/invalid
我需要匹配两种元组并从中生成映射。
两者都有关键字和字符串。可以有第三项(语言代码)。
[<key> <value>] ~> {:type <key> :value <value>}
[<key> <value> <lang>] ~> {:type <key> :value <value> :lang <lang>}
我只需要匹配关键字为 :foo
或 :bar
的关键字,并决定使用 clojure.core.match
:
(ns so.example
(:require
[clojure.core.match :refer [match]]))
(defn example-1 [ast]
(let [l10n-key #{:foo :bar}]
(match ast
[(k :guard l10n-key) v lang] {:type k :value v :lang lang}
[(k :guard l10n-key) v] {:type k :value v})))
(example-1 [:foo 10])
;=> {:type :foo, :value 10}
(example-1 [:bar 20 "en"])
;=> {:type :bar, :value 20, :lang "en"}
可行,但我想在不同的子句中重复使用匹配模式 :guard l10n-key
。所以我想我可以使用一些语法引用和取消引用拼接:
(defn example-2 [ast]
(let [l10n-key-match [:guard #{:foo :bar}]]
(match ast
[`(k ~@l10n-key-match) v lang] {:type k :value v :lang lang}
[`(k ~@l10n-key-match) v] {:type k :value v})))
但是 defn
表达式崩溃:
Unexpected error (AssertionError) macroexpanding match at (form-init11111096422056977084.clj:3:5).
Invalid list syntax (clojure.core/concat (clojure.core/list (quote so.example/k)) l10n-key-match) in (clojure.core/seq (clojure.core/concat (clojure.core/list (quote so.example/k)) l10n-key-match)). Valid syntax: [[:default :guard] [:or :default] [:default :only] [:default :seq] [:default :when] [:default :as] [:default :<<] [:default :clojure.core.match/vector]]
我做错了什么?
没有解决 clojure.core.match
的问题,但对于这么简单的事情你真的不需要它:
(ns tst.demo.core
(:use tupelo.core tupelo.test))
(dotest
(let [data [[:foo 10]
[:bar 20 "en"]
[:fizz 10]
[:buzz 20 "en"]]
keep-tags #{:foo :bar}
data-keep (filterv #(contains? keep-tags (first %)) data)
result (forv [tuple data-keep]
(zipmap [:type :value :lang] tuple))]
(is= result [{:type :foo, :value 10}
{:type :bar, :value 20, :lang "en"}])))
您可能还对这些辅助函数感兴趣:
- tupelo.core/matches?
clojure.core.match
更好的界面
- tupelo.core/submatch?
- tupelo.core/wild-match?
- tupelo.core/wild-submatch?
这不就是 Clojure 附带的 spec 吗?你会像
这样定义你的模式(ns playground.catspec
(:require [clojure.spec.alpha :as spec]))
(spec/def ::type #{:foo :bar})
(spec/def ::value number?)
(spec/def ::lang #{"en" "sv" "fr"})
(spec/def ::key-value-lang (spec/cat :type ::type
:value ::value
:lang (spec/? ::lang)))
我们使用 spec/def
定义规范,spec/cat
连接规范,spec/?
定义可选规范。
然后我们使用conform
解析元组:
(spec/conform ::key-value-lang [:foo 10])
;; => {:type :foo, :value 10}
(spec/conform ::key-value-lang [:bar 20 "en"])
;; => {:type :bar, :value 20, :lang "en"}
(spec/conform ::key-value-lang [:bar 119 "fr"])
;; => {:type :bar, :value 119, :lang "fr"}
(spec/conform ::key-value-lang [119 :foo])
;; => :clojure.spec.alpha/invalid
(spec/conform ::key-value-lang [:bar 119 "uj"])
;; => :clojure.spec.alpha/invalid
(spec/conform ::key-value-lang [:bar])
;; => :clojure.spec.alpha/invalid
(spec/conform ::key-value-lang [:bar 119 "fr" :asdfasdf])
;; => :clojure.spec.alpha/invalid
(spec/conform ::key-value-lang {:a 1 :b 4})
;; => :clojure.spec.alpha/invalid