Clojure 在中解析符号:要求';d名称空间

Clojure 在中解析符号:要求';d名称空间,clojure,Clojure,我正在编写一个小应用程序,其中包括一个密码更改功能,带有密码质量验证程序。目前,验证程序在映射中指定,如下所示: (def验证) {:最小长度6 :最大长度32}) validations映射是在validations命名空间中定义的,但我计划稍后将其移动到配置命名空间中。我决定以这种方式使用map是为了让非程序员直接进行配置 validations命名空间中有许多验证函数,通常采用以下形式: (定义最小长度[ns] {:req(str“长度至少为n个字符”) :通过?(>=(.length(

我正在编写一个小应用程序,其中包括一个密码更改功能,带有密码质量验证程序。目前,验证程序在映射中指定,如下所示:

(def验证)
{:最小长度6
:最大长度32})
validations映射是在validations命名空间中定义的,但我计划稍后将其移动到配置命名空间中。我决定以这种方式使用map是为了让非程序员直接进行配置

validations命名空间中有许多验证函数,通常采用以下形式:

(定义最小长度[ns]
{:req(str“长度至少为n个字符”)
:通过?(>=(.length(或s“”)n)})
因此,使用上面的函数
(最小长度3“clojure”)
将返回
{:req“至少3个字符长”,:pass?true}

我可以使用此函数在验证命名空间中使用此函数验证密码:

(defn验证新密码[密码]
(进入{}(用于[[k v]验证]
[k(eval(列表(->k名称符号)v密码)])
结果是:

>(验证新密码“clojure”)
{:min-length{:req“长度至少为6个字符,:pass?true},
:max length{:req“长度不得超过32个字符”,:pass?true},
:min位{:req“至少有1位”,:pass?false},
:允许空白{:req“不包含空格或制表符”,:pass?true},
:allow dict words{:req“not be a dictionary word”,:pass?false}
当从验证命名空间外部调用validate new password函数时,解决验证函数的最实用的方法是什么

在过去的几周里,我尝试了很多方法,但我对结果的形式一直不满意(而且没有一种有效!)

一般来说,我想问题是“当一个:required名称空间中的函数调用该名称空间时,如何解析该名称空间中的符号?”


我还对有关我的实施的任何一般性评论感兴趣。

没有必要进行评估,因为98%的案例都需要评估

(defn validate-new-password
  [password]
  (into {} (for [[k v] validations]
             [k ((->> k name (symbol "your.name.space") resolve) v password)])))

要回答一般性问题(从问题的倒数第二段开始),符号通常在编译时解析——您必须特意(即使用
resolve
1)将解析推迟到运行时

这就是说,在这种情况下,您可能不需要推迟解决,您只需要
:在validations名称空间中要求
配置名称空间,并在相关位置参考
config/validations

如果您希望您的配置名称空间依赖于验证名称空间(即
:require
:使用它本身,或者使用依赖它的另一个名称空间),则会出现一个小的复杂情况。在这种情况下,您可以在validations命名空间中提供一些配置设置器,并使用config中的设置器。例如:

(def validations (atom default-validations))
(defn set-validations! [vs]   (reset! validations vs))
(defn add-validation!  [k v]  (swap! validations assoc k v))
然后将
@validations
放在
验证新密码
中的
验证
位置

或者,您可以将默认验证放在一个Var中,并提供函数来重新绑定Var(这可能只需要发生一次,或者很少发生,因此使用Var应该不会有问题):

现在,如果在编写代码时,您实际上无法预测需要在哪个名称空间中解析符号,那么kotarak的答案(使用
resolve
)就是正确的选择



1您可以说,
eval
也允许您这样做,尽管从技术上讲它在运行时执行编译,所以
eval
'd表单中的符号结果仍然在编译时发生。关于它,更重要的是要记住它几乎不需要。

感谢你和迈克尔的回答。是的,评估不太理想。您的解决方案当然有效,尽管我希望避免显式指定名称空间的需要。我相信我理解为什么我的实现需要它,但是有可能删除它吗?我不希望配置在运行时改变,所以我可以在编译时使用宏来编写包装器函数吗?如果是这样,我是否能够在每个验证函数(除字符串输入外的所有参数)上使用分部函数?谢谢感谢您的详细回复。今天,我将尝试你建议的方法,看看我会怎么做。
(def validations default-validations)
(defn set-validations! [vs]  (.bindRoot validations vs))
(defn add-validation!  [k v] (alter-var-root validations assoc k v))