Как вы возвращаетесь из функции в начале Clojure? - PullRequest
37 голосов
/ 21 сентября 2011

Обычный Лисп имеет return-from;есть ли в Clojure return, когда вы хотите рано вернуться из функции?

Ответы [ 7 ]

33 голосов
/ 07 мая 2016

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

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

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

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

Вы можете увидеть, что происходит, если обернуть выше 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
25 голосов
/ 21 сентября 2011

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

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

17 голосов
/ 21 сентября 2011

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

14 голосов
/ 21 сентября 2011

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

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

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

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

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

0 голосов
/ 03 ноября 2015

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

0 голосов
/ 07 февраля 2014

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

0 голосов
/ 21 сентября 2011

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

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

...