2013-12-08 1 views
2

Допустим, у меня есть следующие функции для получения числовых значений из байтового буфера:Идиоматические способ вернуть значение, не последнее выражение в последовательности выражений в Clojure

(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 в другом выражении, пусть, то есть, что в качестве последнего выражения, как так:

(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 идиоматический/«правильный» способ окружающего выражения с некоторым кодом пролога/эпилога, а затем возвращает значение этого выражения?

+1

Смотрите также [это] (http://stackoverflow.com/questions/17235657/clojure-functions-returning- value-computed-before-the-last-statement) и [this] (http://stackoverflow.com/questions/7491360/how-do-you-return-from-a-function-early-in-clojure) previous вопросов. –

ответ

3

Возможно, один из вариантов ниже будет приемлемым.

(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, но я использовал этот стиль раньше в подобных сценариях, и до сих пор я доволен этим.

В противном случае я бы предложил двойной подход, который вы показали.

Надеюсь, это поможет.

+2

Не только это идиоматика, это единственный способ, которым я видел _ used - как привязка для значения, которое будет проигнорировано. Чтобы быть ясным, однако, _ является допустимым символом - '(let [_ 1] (inc _))' -> 2. – noisesmith