Подтвердить что ты не робот

Как вы возвращаетесь из функции в начале Clojure?

Общий Lisp имеет return-from; есть ли какой-либо return в Clojure, если вы хотите вернуться раньше от функции?

4b9b3361

Ответ 1

В clojure нет явного оператора return. Вы можете взломать что-то вместе, используя комбинацию catch/throw, если хотите, но поскольку clojure намного более функциональный, чем обычный lisp, вероятность того, что вам действительно нужен ранний возврат прямо в середине некоторого вложенного блока, намного меньше чем в CL. Единственная "хорошая" причина, которую я могу видеть для операторов return, - это когда вы имеете дело с изменяемыми объектами таким образом, чтобы это не было идиоматическим в clojure.

Я бы не сказал, что он никогда не бывает полезен, но я думаю, что в Clojure, если вашему алгоритму нужен оператор return, это основной запах кода.

Ответ 2

Когда вам нужно рано вырваться из вычисления, вам нужен способ сделать это, а не аргумент от пуристов. Обычно вам это нужно, когда вы уменьшаете большую коллекцию, и определенное значение указывает, что нет смысла в дальнейшей обработке коллекции. С этой целью вечно практический Clojure предоставляет функцию reduced.

Простым примером для иллюстрации является то, что при умножении последовательности чисел, если вы столкнулись с нулем, вы уже знаете, что конечный результат будет равен нулю, поэтому вам не нужно смотреть на остальную часть последовательности. Вот как вы кодируете это с помощью reduced:

(defn product [nums]
  (reduce #(if (zero? %2)
               (reduced 0.0)
               (* %1 %2))
          1.0
          nums))

reduced обертывает значение, которое вы даете ему в структуре данных часового, чтобы reduce знал, чтобы прекратить чтение из коллекции и просто вернуть значение reduced прямо сейчас. Эй, это чисто функциональный, даже!

Вы можете видеть, что происходит, если вы оберните выше if в do с помощью (println %1 %2):

user=> (product [21.0 22.0 0.0 23.0 24.0 25.0 26.0])
1.0 21.0
21.0 22.0
462.0 0.0
0.0

user=> (product [21.0 22.0 23.0 24.0 25.0 26.0])
1.0 21.0
21.0 22.0
462.0 23.0
10626.0 24.0
255024.0 25.0
6375600.0 26.0
1.657656E8

Ответ 3

Если вы не написали какой-нибудь действительно надуманный код, единственная причина, по которой вы хотели бы вернуться раньше, - это если какое-то условие выполнено. Но так как функции всегда возвращают результат вычисления последней формы, if уже является этой функцией - просто поместите значение, которое вы хотите вернуть в теле if, и оно вернет это значение, если условие выполнено.

Ответ 4

Я не эксперт в Clojure, но, похоже, у этого нет такой конструкции, чтобы попытаться быть более функциональным. Взгляните на то, что Стюарт Хэллоуэй говорит здесь:

Common Lisp также поддерживает макрос возврата из макроса в "return" из середина функции. Это поощряет императивный стиль программирование, которое Clojure обескураживает.

Однако вы можете решать одни и те же проблемы по-другому. Здесь обратный пример, переписанный в функциональном стиле, так что нет требуется возврат:

(defn pair-with-product-greater-than [n]
 (take 1 (for [i (range 10) j (range 10) :when (> (* i j) n)] [i j])))

То есть, используйте ленивые последовательности и возвращающие значения на основе условий.

Ответ 5

Опция if, уже заданная, вероятно, является лучшим выбором, и обратите внимание, поскольку карты просты, вы всегда можете вернуть {:error "blah"} в условие ошибки и {result: x} в правильное условие.

Ответ 6

В Clojure нет оператора return. Даже если вы решили не выполнять какой-либо код, используя конструкцию потока, такую ​​как if или when, функция всегда будет возвращать что-то, в этих случаях nil. Единственный выход - это выбросить исключение, но даже тогда он либо полностью опустится, либо уничтожит ваш поток, либо поймается функцией, которая вернет значение.

Ответ 7

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

(defn fact [x]
  (cond
    (< x 0) (do (println (str x " is negative number"))
                (throw (IllegalArgumentException. "x should be 0 or higher")))
    (<= x 1) 1
    :else (* x (fact (- x 1)))))

Ответ 8

Короче говоря, нет. Если это реальная проблема для вас, вы можете обойти ее с помощью " возможно, монада" Монады имеют высокие интеллектуальные накладные расходы, поэтому для многих случаев clojurians обычно избегают "если неудачный возврат" "стиль программирования.

Это помогает разбить функцию на более мелкие функции, чтобы уменьшить трение от этого.