В Clojure, почему вы должны использовать круглые скобки при определении функции и случаи использования - PullRequest
0 голосов
/ 27 января 2020

Я начинаю изучать clojure, и я наткнулся на следующее, когда я обнаружил, что объявляю функцию «сумма» (в учебных целях), я написал следующий код

(def sum (fn [& args] (apply + args)))

Я понял, что я определил символьную сумму как содержащую этот fn, но почему я должен заключать Fn в круглые скобки, разве компилятор не вызывает эту функцию при определении, а не когда кто-то фактически ее вызывает? Может быть, это просто мой императивный мозг.

Кроме того, каковы случаи использования let? Иногда я натыкаюсь на код, который его использует, и другой код, который этого не делает, например, на сайте Clojure, где есть упражнение по использованию функции OpenStream из Java Interop, я написал следующий код:

(defn http-get 
  [url] 
  (let [url-obj (java.net.URL. url)]
    (slurp (.openStream url-obj))))

(http-get "https://www.google.com")

в то время как они написали следующее на сайте clojure в качестве ответа

(defn http-get [url]
  (slurp
    (.openStream
      (java.net.URL. url))))

Опять же, возможно, это просто мой императивный мозг, говорящий о необходимости наличия «переменной» или «объекта» для хранения чего-либо перед использованием это, но я совершенно не понимаю, когда я должен использовать let или когда я не должен.

Ответы [ 2 ]

3 голосов
/ 27 января 2020

Чтобы ответить на оба ваших вопроса:

1.

(def sum (fn [& args] (apply + args)))

Использование def здесь очень неортодоксально. Когда вы определяете функцию, которую вы обычно хотите использовать defn. Но поскольку вы использовали def, вы должны знать, что def связывает имя со значением . Возвращаемое значение fn является функцией . Фактически вы связали имя sum с функцией, возвращаемой , применяя (используя круглые скобки, которые используются для приложения) fn.

Вы могли бы использовать более традиционный (defn sum [& args] (apply + args))

2.

Хотя использование let иногда имеет смысл для удобочитаемости (разделяя этапы вне их вложенного использования), иногда требуется , когда вы хотите что-то сделать один раз и использовать это несколько раз. Он связывает результат с именем в указанном контексте.

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

(let [db-results (query "select * from table")] ;; note: query is not a pure function
    ;; do stuff with db-results
    (f db-results)
    ;; return db-results
    db-results)))

Это просто повторно использует возвращаемое значение (db-results) из функции, которую обычно требуется запускать только один раз - в нескольких местах. Таким образом, let может использоваться для стиля, как пример, который вы дали, но также очень полезен для повторного использования значения в некотором контексте.

1 голос
/ 27 января 2020

И def, и defn определяют символ global , подобный глобальной переменной в Java, et c. Кроме того, (defn xxx ...) является (очень распространенным) сочетанием клавиш для (def xxx (fn ...)). Итак, обе версии будут работать одинаково при запуске программы. Поскольку версия defn короче и более четкая, вы будете делать это в 99% случаев.

Ввод (let [xxx ...] ...) определяет символ local , который не может быть виден кодом вне формы let, как локальная переменная (block-scope) в Java, et c.

Как и Java, это необязательно когда иметь локальную переменную, такую ​​как url-obj. Это не будет иметь никакого значения для работающей программы. Вы должны ответить на вопрос: «Какая версия облегчает чтение и понимание моего кода?» Эта часть ничем не отличается от Java.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...