在clojure中,如何调用x的所有方法然后返回最后一个表单执行的值?

时间:2013-08-04 11:25:29

标签: clojure

我正在寻找doto之类的内容,但我想返回最后一个调用值,而不是返回x

例如,而不是:

(.size (doto (java.util.ArrayList.) 
             (.add 2)
             (.add 3)))

我想写:

(mydoto (java.util.ArrayList.) 
             (.add 2)
             (.add 3)
             .size)

实现这一结果的惯用方法是什么?

3 个答案:

答案 0 :(得分:11)

doto返回它的第一个参数。 ->->>返回评估最后一个表单的结果(在线程化第一个表达式之后,请参阅(clojure.repl/doc ->)。宏doto将扩展您的代码:

(let [r (java.util.ArrayList.)]
  (.add r 2)
  (.add r 3)
  r)

当您使用.size时,它不会影响您的数组列表,您只对该方法的返回值感兴趣(与.add不同,它返回一个布尔值并更改状态您感兴趣的ArrayList)。

你不能像mydoto那样混合这些方法,因为你需要的是不同类型的返回值和输入值。如果你想将最终大小乘以3,这将是惯用的:

(-> (doto (java.util.ArrayList.) 
          (.add 2)
          (.add 3))
    .size
    (* 3))

甚至

(-> (java.util.ArrayList.)
    (doto (.add 2) (.add 3))
    .size
    (* 3))

这些宏应该使代码更容易阅读,这取决于我以最有意义的方式编写它们的上下文。

答案 1 :(得分:3)

Clojure宏可以让你做你想做的事情,实际上它只对现有的doto宏进行了1个符号的改变(在emacs中使用Meta-。来查看core.clj中的定义)。

(defmacro mydoto 
   "Like doto but returns the result of the last form 
    instead to the first argument"
  [x & forms]
  (let [gx (gensym)]
  `(let [~gx ~x]
     ~@(map (fn [f]
              (if (seq? f)
                `(~(first f) ~gx ~@(next f))
                `(~f ~gx)))
            forms))))

这将允许您编写以下代码:

(mydoto (java.util.ArrayList.) 
         (.add 2)
         (.add 3)
         .size)
;; => 2

答案 2 :(得分:1)

如@lgrapenthin所述,可以使用mydoto撰写doto

(defmacro mydoto 
    "Like doto but returns the result of the last form instead 
     of the first argument"
    [& args]
    `(-> (doto ~@(butlast args))
         ~(last args)))