[英]Clojure simple pattern matching
As a follow up to my previous question , I am trying to implement a simple pattern matching in Clojure.作为我上一个问题的后续,我试图在 Clojure 中实现一个简单的模式匹配。
I would like something like the following:我想要以下内容:
(match target
[ub] expr1 ; ub should be bound to actual value in expr1
['< ub] expr2 ; match the literal less-than symbol
; and ub should be bound to actual value in expr2
[lb ub] expr3 ; lb and ub should be bound to actual values in expr3
:else expr4 ; default case if none match
)
Usage:用法:
(match [< 5.0] ...)
should arrange to execute expr2
at runtime.应该安排在运行时执行
expr2
。
I would like to write a macro, but I am not sure of the expansion.我想写一个宏,但我不确定扩展。
I am considering having each case-and-clause expand to a let
with bindings to internal variables and checks that the literal symbols ( '<
) actually matches the pattern.我正在考虑让每个 case-and-clause 扩展为一个
let
并绑定到内部变量,并检查文字符号 ( '<
) 是否实际匹配模式。 Maybe for the second pattern ( ['< ub]
):也许对于第二种模式(
['< ub]
):
(let [[sym1 ub] pattern]
(if (= '< sym1)
expr1)
Do I need to use (gensym)
for the bindings?我需要使用
(gensym)
进行绑定吗? How?如何?
Bigger picture:大图:
(range-case target
[0.0 < 1.0] :greatly-disagree
[< 2.0] :disagree
[< 3.0] :neutral
[< 4.0] :agree
[5.0] :strongly-agree
42 :the-answer
:else :do-not-care)
I am trying to match the [...]
patterns and convert them to the following:我正在尝试匹配
[...]
模式并将它们转换为以下内容:
[ub] (if previous-ub `(and (<= ~previous-ub ~target) (<= ~target ~ub))
`(< ~target ~ub))
['< ub] (if previous-ub `(and (<= ~previous-ub ~target) (< ~target ~ub))
`(< ~target ~ub))
[lb ub] `(and (<= ~lb ~target) (<= ~target ~ub))
['< lb ub] `(and (< ~lb ~target) (<= ~target ~ub))
[lb '< ub] `(and (<= ~lb ~target) (< ~target ~ub))
['< lb '< ub] `(and (< ~lb ~target) (< ~target ~ub))
I have a cond
that checks that the case part is a vector.我有一个
cond
案例部分是否为向量的条件。 This pattern match should occur inside that case.这种模式匹配应该发生在这种情况下。
My first idea was basically the same: Bind stuff to internal locals and test on their contents in a big and
.我的第一个想法基本上是一样的:将东西绑定到内部本地人并在一个大的
and
. For literals the value is bound to a generated local;对于文字,该值绑定到生成的本地; symbols are used directly in the binding.
符号直接在绑定中使用。
I also added a check that the spec vector matches the length of the target vector.我还添加了检查规范向量是否匹配目标向量的长度。 Otherwise you can't have
[ub]
as well as [lb ub]
since neither contains a check which could fail.否则你不能拥有
[ub]
和[lb ub]
因为两者都不包含可能失败的检查。 So always the first would be selected.所以总是会选择第一个。
Here is the code:这是代码:
(defn make-clause
[expr-g [spec expr & more :as clause]]
(when (seq clause)
(let [tests-and-bindings (map (fn [x]
(if-not (symbol? x)
(let [x-g (gensym "x")]
[`(= ~x ~x-g) x-g])
[nil x]))
spec)
tests (keep first tests-and-bindings)
bindings (map second tests-and-bindings)]
`(let [[~@bindings] ~expr-g]
(if (and (= (count ~expr-g) ~(count spec)) ~@tests)
~expr
~(make-clause expr-g more))))))
(defmacro match
[expr & clauses]
(let [expr-g (gensym "expr")]
`(let ~[expr-g expr]
~(make-clause expr-g clauses))))
And an example expansion.和一个示例扩展。 I didn't use syntax-quote in the example to reduce the noise in the expansion, but you should get the idea.
我没有在示例中使用语法引用来减少扩展中的噪音,但您应该明白这一点。
(let [expr98 [(quote <) 3.0]]
(let [[ub] expr98]
(if (and (= (count expr98) 1))
(if previous-ub
(and (<= previous-ub target) (<= target ub))
(< target ub))
(let [[x99 ub] expr98]
(if (and (= (count expr98) 2) (= (quote <) x99))
(if previous-ub
(and (<= previous-ub target) (< target ub))
(< target ub))
(let [[lb ub] expr98]
(if (and (= (count expr98) 2))
(and (<= lb target) (<= target ub))
nil)))))))
The invokation was:调用是:
(match ['< 3.0]
[ub] (if previous-ub
(and (<= previous-ub target) (<= target ub))
(< target ub))
['< ub] (if previous-ub
(and (<= previous-ub target) (< target ub))
(< target ub))
[lb ub] (and (<= lb target) (<= target ub))))
Hope that helps you get started.希望能帮助您入门。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.