Clojure函数 - 返回在最后一个语句之前计算的值

时间:2013-06-21 12:49:29

标签: function clojure functional-programming

我有一些用clojure编写的测试。这是一个简单的例子:

(defn test1
  []
  (start-server)
  (run-pvt-and-expect "PVT-0")
  (stop-server)
 )

我想返回“run-pvt-and-expect”的结果,但我需要在它之后执行其他函数。我怎么能以功能的方式在clojure中做到这一点(不使用像“let”这样的结构)?感谢。

注意:我阅读了this问题及其答案,但无法将其应用于我的案例。此外,评论要求提供从未给出的示例,因此请将此视为一个示例......

5 个答案:

答案 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)

这就是的用途!只要观察试图避免它时出现的不必要的复杂性。

情况是在返回第二个结果时必须执行三个副作用。典型用例:数据库访问。