【问题标题】:Idiomatic way to return the value of the non-last expression in a sequence of expressions in Clojure在 Clojure 中的表达式序列中返回非最后一个表达式的值的惯用方法
【发布时间】:2013-12-25 10:39:15
【问题描述】:

假设我有以下函数从字节缓冲区获取数值:

(defn get-from-bytebuffer
  ([^ByteBuffer buffer width endianness]
    (let [buffer-endianness (.order buffer)]
      (.order buffer endianness)
      (cond
        (= width 1) (.get buffer)
        (= width 2) (.getShort buffer)
        (= width 4) (.getInt buffer)
        (= width 8) (.getLong buffer))
      (.order buffer buffer-endianness))))

用户可以指定正在读取的数字的字节顺序。为了将副作用降至最低,该函数首先获取缓冲区的当前字节顺序,将其设置为用户指定的字节顺序,然后恢复旧的字节顺序。这样做的问题是let 主体中最后一个表达式的值是let 表达式的值,但我需要cond 的值。更一般地说,我在表达式周围有一些序言/结尾代码,但我希望返回表达式的结果(作为封闭表达式的值。)

我想出的简单解决方法只是将 cond 的值绑定到另一个 let 表达式中,然后将其作为最后一个表达式,如下所示:

(defn get-from-bytebuffer-fix
  ([^ByteBuffer buffer width endianness]
    (let [buffer-endianness (.order buffer)]
      (.order buffer endianness)
      (let [result (cond
                     (= width 1) (.get buffer)
                     (= width 2) (.getShort buffer)
                     (= width 4) (.getInt buffer)
                     (= width 8) (.getLong buffer))]
      (.order buffer buffer-endianness)
      result))))

但这感觉很笨拙。 Clojure 是否有一种惯用/“正确”的方式用一些序言/结尾代码包围表达式,然后返回该表达式的值?

【问题讨论】:

标签: clojure let


【解决方案1】:

也许以下替代方案之一是可以接受的。

(let [buffer-endianness (.order buffer)
      _ (.order buffer endianness)
      result (cond
               (= width 1) (.get buffer)
               (= width 2) (.getShort buffer)
               (= width 4) (.getInt buffer)
               (= width 8) (.getLong buffer))
      _ (.order buffer buffer-endianness)]
  result)

(let [buffer-endianness (.order buffer)]
  (try 
    (.order buffer endianness)
    (cond
      (= width 1) (.get buffer)
      (= width 2) (.getShort buffer)
      (= width 4) (.getInt buffer)
      (= width 8) (.getLong buffer))
    (finally (.order buffer buffer-endianness))))

(defn return-nth [n & exprs]
  (nth exprs n))

(return-nth 1
  'exp-0
  'exp-1
  'exp-2)

(nth ['exp-0 'exp-1 'exp-2] 1)

还要注意

(cond
  (= width 1) (.get buffer)
  (= width 2) (.getShort buffer)
  (= width 4) (.getInt buffer)
  (= width 8) (.getLong buffer))

可以写

(case width
  1 (.get buffer)
  2 (.getShort buffer)
  4 (.getInt buffer)
  8 (.getLong buffer))

【讨论】:

    【解决方案2】:

    你可以这样写:

    (defn get-from-bytebuffer
      ([^ByteBuffer buffer width endianness]
        (let [buffer-endianness (.order buffer)
              _ (.order buffer endianness)
              result (cond
                       (= width 1) (.get buffer)
                       (= width 2) (.getShort buffer)
                       (= width 4) (.getInt buffer)
                       (= width 8) (.getLong buffer))]
          (.order buffer buffer-endianness)
          result)))
    

    注意 let 表单中的 _。它只是忽略了返回值,使您可以在该位置执行几乎所有操作。

    我不会说它是惯用的,并且在某种程度上它是“奇怪的”,因为它在 let 绑定中有副作用,但我以前在类似的场景中使用过这种风格,到目前为止我对它很满意。

    否则我会采用你所展示的双重让方法。

    希望这会有所帮助。

    【讨论】:

    • 这不仅是惯用的,而且是我见过的唯一使用 _ 的方式 - 作为将被忽略的值的绑定。不过要清楚,_ 是一个有效符号 - (let [_ 1] (inc _)) -> 2.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-04
    • 2013-08-29
    • 2020-01-24
    • 2012-06-16
    • 1970-01-01
    • 2019-05-09
    • 1970-01-01
    相关资源
    最近更新 更多