在 clojure 中,如何调用 x 的所有方法,然后返回上次表单执行的值



我正在寻找类似doto的东西,但不是返回x,而是返回最后一个调用值。

例如,代替:

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

我想写:

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

达到这个结果的惯用方法是什么?

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

这些宏应该使代码更易于阅读,因此根据上下文,我会以最有意义的方式编写它们。

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
可以使用

doto编写mydoto,如@lgrapenthin所述:

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

最新更新