我有一些用clojure编写的测试。这是一个简单的例子:
(defn test1
[]
(start-server)
(run-pvt-and-expect "PVT-0")
(stop-server)
)
我想返回“run-pvt-and-expect”的结果,但我需要在它之后执行其他函数。我怎么能以功能的方式在clojure中做到这一点(不使用像“let”这样的结构)?感谢。
注意:我阅读了this问题及其答案,但无法将其应用于我的案例。此外,评论要求提供从未给出的示例,因此请将此视为一个示例......
答案 0 :(得分:7)
您可以使用finally块执行此操作。
(try
(+ 2 3)
(finally (+ 6 7)))
产生的结果为5而不是13.
Here is the relevant doc page. 重要的部分是:
任何最终的exprs都会被评估其副作用。
另一种选择是编写一个像with-open这样的宏来启动和停止服务器,同时返回正确的结果。虽然这只是将问题转移到你如何编写宏,因为它需要使用let或上面的try finally构造。
答案 1 :(得分:4)
嗯,我知道你可以使用的任何内置内容(let
除外,你不需要)。但美丽的是你可以自己建造它。
一个想法是创建一个“新”defn
,其中表达式左侧的:return
关键字表示从该表达式返回值,而不是最后一个表达式。这里有一个宏,因为我们将构建一个修改过的(defn ...)
表达式,这需要未经评估的代码。
(defmacro defn-return-middle
[nm arg-vec & body-exprs]
`(defn ~nm ~arg-vec
~(if (some #{:return} body-exprs)
(let [[before _ [to-be-returned & after]]
(partition-by #{:return} body-exprs)]
`(let [ret# (do ~@before ~to-be-returned)]
~@after
ret#))
body-exprs)))
这扩展了类似:
(defn-return-middle f []
a
:return b
c)
类似于:
(defn f []
(let [ret (do a b)]
c
ret))
例如,您现在可以:
(defn-return-middle blah [a]
(+ 1 a)
:return (+ 2 a)
(println "does this work?"))
现在在REPL中:
user> (blah 5)
does this work?
=>7
(耶!)
或者您的例子,您现在可以:
(defn-return-middle test1
[]
(start-server)
:return (run-pvt-and-expect "PVT-0")
(stop-server))
是的,宏使用let
,但它可以自动执行let
扩展,如果您每次都手写它。这意味着现在,当使用此宏时,您将不再手写let
。此外,此宏当前不适用于声明多个arities的函数定义。但要修改它以便为那些人工作也不会太困难。
答案 2 :(得分:3)
我认为你在这里寻找的一般概念是K-combinator。你可以在Fogus' library中看到它的一个例子。您可以像这样使用它:
(defn test2 []
(println "start server")
((K (identity :return-value))
(println "stop server")))
=> (test2)
start server
stop server
:return-value
对于这种情况看起来有点过于复杂,所以也许我们可以简化它(我不确定这个中的匿名函数是否“正式”为k-combinator):
(defn test3 []
(println "start server")
((fn [a b] a)
(identity :return-value)
(println "stop server")))
=> (test3)
start server
stop server
:return-value
由于文字向量不是懒惰的,你也可以先用同样的方式使用:
(defn test4 []
(println "start server")
(first
[(identity :return-value)
(println "stop server")]))
=> (test4)
start server
stop server
:return-value
老实说,在实际代码中,我只是使用let(就像你不想做的那样)因为意图对我来说更清晰:
(defn test5 []
(println "start server")
(let [result (identity :return-value)]
(println "stop server")
result))
=> (test5)
start server
stop server
:return-value
答案 3 :(得分:1)
这是一种“功能”方式,不使用let构造:
(defn apply-get-first [c f] (f) c)
(defn p []
(start
(apply-get-first (compute)
#(end)))
现在使用let:
你可以这样做:(defn p[]
(let [s (start)
r (compute)
e (end)]
r))
答案 4 :(得分:1)
这就是让的用途!只要观察试图避免它时出现的不必要的复杂性。
情况是在返回第二个结果时必须执行三个副作用。典型用例:数据库访问。