是否有合理的方法让def
语句与let
相同的方式发生多个(let [[rtgs pcts] (->> (sort-by second row)
(apply map vector))]
.....)
语句?例如:
(defs [rtgs pcts] (->> (sort-by second row)
(apply map vector)))
我想要的是:
(defmacro def2 [[name1 name2] form]
`(let [[ret1# ret2#] ~form]
(do (def ~name1 ret1#)
(def ~name2 ret2#))))
这在REPL,笔记本和调试时出现了很多。认真地感觉就像一个缺失的功能,所以我想要一个:
的指导一个超短的实验给我一些类似的东西:
(def2 [three five] ((juxt dec inc) 4))
three ;; => 3
five ;; => 5
这就像:
csv
当然,该宏的“工业实力”版本可能是:
答案 0 :(得分:4)
虽然我同意Josh的说法,你可能不应该让它在制作中运行,但我认为将它作为一个方便的副本并没有任何伤害(事实上我认为我会这样做)将其复制到我的debug-repl kitchen-sink库中)。
我喜欢编写宏(尽管通常不需要它们)所以我掀起了一个实现。它接受任何绑定表单,例如let
。
(我首先编写了这个规范,但是如果您使用clojure< 1.9.0-alpha17,您可以删除规范内容并且它的工作方式相同。)
(ns macro-fun
(:require
[clojure.spec.alpha :as s]
[clojure.core.specs.alpha :as core-specs]))
(s/fdef syms-in-binding
:args (s/cat :b ::core-specs/binding-form)
:ret (s/coll-of simple-symbol? :kind vector?))
(defn syms-in-binding
"Returns a vector of all symbols in a binding form."
[b]
(letfn [(step [acc coll]
(reduce (fn [acc x]
(cond (coll? x) (step acc x)
(symbol? x) (conj acc x)
:else acc))
acc, coll))]
(if (symbol? b) [b] (step [] b))))
(s/fdef defs
:args (s/cat :binding ::core-specs/binding-form, :body any?))
(defmacro defs
"Like def, but can take a binding form instead of a symbol to
destructure the results of the body.
Doesn't support docstrings or other metadata."
[binding body]
`(let [~binding ~body]
~@(for [sym (syms-in-binding binding)]
`(def ~sym ~sym))))
;; Usage
(defs {:keys [foo bar]} {:foo 42 :bar 36})
foo ;=> 42
bar ;=> 36
(defs [a b [c d]] [1 2 [3 4]])
[a b c d] ;=> [1 2 3 4]
(defs baz 42)
baz ;=> 42
关于您的REPL驱动的开发评论:
我没有Ipython的任何经验,但我会简要解释一下我的REPL工作流程,你可以评论一下与Ipython的任何比较/对比。
我从不像终端那样使用我的repl,输入命令并等待回复。我的编辑器支持(emacs,但任何clojure编辑器应该这样做)将光标放在任何s表达式的末尾并将其发送到repl,"打印"光标后的结果。
我通常在我开始工作的文件中有一个comment
块,只需键入任何内容并进行评估。然后,当我对结果感到满意时,我将其从" repl-area"进入"真实代码"。
(ns stuff.core)
;; Real code is here.
;; I make sure that this part always basically works,
;; ie. doesn't blow up when I evaluate the whole file
(defn foo-fn [x]
,,,)
(comment
;; Random experiments.
;; I usually delete this when I'm done with a coding session,
;; but I copy some forms into tests.
;; Sometimes I leave it for posterity though,
;; if I think it explains something well.
(def some-data [,,,])
;; Trying out foo-fn, maybe copy this into a test when I'm done.
(foo-fn some-data)
;; Half-finished other stuff.
(defn bar-fn [x] ,,,)
(keys 42) ; I wonder what happens if...
)
您可以在clojure core source code中找到相关示例。
答案 1 :(得分:3)
任何一件clojure所具有的def
的数量因项目而异,但我说一般来说,def
s通常不是某些计算的结果,更不用说需要解构的计算结果了。更常见的是def
s是的起点,用于稍后计算,这取决于此值。
通常函数更适合计算值;如果计算成本很高,那么你可以记住这个功能。如果你觉得你真的需要这个功能,那么一定要使用你的宏 - 这是clojure的销售点之一,即可扩展性!但总的来说,如果你觉得自己需要这种结构,那就考虑一下你过度依赖全球状态的可能性。
为了给出一些真实的例子,我刚刚在大约20个命名空间中引用了我的主要项目,这可能是2K-3K行的clojure。我们有大约20 def
s,其中大多数都标记为私有,其中没有一个实际上是计算任何东西。我们有类似的事情:
(def path-prefix "/some-path")
(def zk-conn (atom nil))
(def success? #{200})
(def compile* (clojure.core.memoize/ttl compiler {} ...)))
(def ^:private nashorn-factory (NashornScriptEngineFactory.))
(def ^:private read-json (comp json/read-str ... ))
定义函数(使用comp
和memoize
),枚举,通过atom
状态 - 但没有真正的计算。
所以我要说,根据你的要点,这大概介于2到3之间:它绝对不是一个需要的常见用例(你是'我曾经听说过第一个想要这个的人,所以这对我来说并不常见);它不常见的原因是因为我上面所说的,即它可能是一种代码气味,表明依赖于过多的全球状态,因此,不会是非常惯用的。
我的大部分代码都有一个试金石:如果我把这个函数从这个命名空间中拉出来并粘贴到另一个中,它还能用吗?删除对外部变量的依赖性允许更容易的测试和更模块化的代码。有时我们需要它,所以看看你的要求是什么,并相应地进行。祝你好运!