2010-03-07 1 views
5

Я только начал играть с Clojure, и я написал небольшой скрипт, чтобы помочь мне понять некоторые функции. Она начинается так:Confused by «let» in Clojure

(def *exprs-to-test* [ 
    "(filter #(< % 3) '(1 2 3 4 3 2 1))" 
    "(remove #(< % 3) '(1 2 3 4 3 2 1))" 
    "(distinct '(1 2 3 4 3 2 1))" 
]) 

Затем он проходит через *exprs-to-test*, оценивает их всех, и печатает вывод так:

(doseq [exstr *exprs-to-test*] 
    (do 
     (println "===" (first (read-string exstr)) "=========================") 
     (println "Code: " exstr) 
     (println "Eval: " (eval (read-string exstr))) 
    ) 
) 

Приведенный выше код все работает нормально. Однако, (read-string exstr) повторяется, так что я пытался использовать let устранить повторение так:

(doseq [exstr *exprs-to-test*] 
    (let [ex (read-string exstr)] (
     (do 
      (println "===" (first ex) "=========================") 
      (println "Code: " exstr) 
      (println "Eval: " (eval ex)) 
     ) 
    )) 
) 

Но это работает один раз для первого элемента в *exprs-to-test*, а затем падает с NullPointerException. Почему добавление let вызывает крушение?

ответ

7

У вас есть дополнительный набор круглых скобок вокруг формы do. Ваш код делает это:

((do ...)) 

Он пытается выполнить (как вызов функции) значение всей do формы, но do возвращается nil, потому что в do формы последнего println возвращает nil.

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

user> (doseq [exstr *exprs-to-test*] 
     (let [ex (read-string exstr)] 
      (println "===" (first ex) "=========================") 
      (println "Code: " exstr) 
      (println "Eval: " (eval ex)))) 
=== filter ========================= 
Code: (filter #(< % 3) '(1 2 3 4 3 2 1)) 
Eval: (1 2 2 1) 
=== remove ========================= 
Code: (remove #(< % 3) '(1 2 3 4 3 2 1)) 
Eval: (3 4 3) 
=== distinct ========================= 
Code: (distinct '(1 2 3 4 3 2 1)) 
Eval: (1 2 3 4) 
+0

Это исправлено. Спасибо за отступы в стиле. –

1

Брайан уже ответил на ваш вопрос, так что я просто хочу дать вам некоторые общие указатели для LET-формы:

4

Я думаю, что другие ответы игнорируют слона в комнате: почему вы это делаете? Есть много вещей в вашем коде, которые заставляют меня беспокоиться вы ковкой на неверном пути через обучение Clojure:

  • Использование глобальных привязок (exprs к испытанию)
  • Использование doseq/Println к попробовать код в последовательности
  • Использование Eval

Самый лучший способ узнать о API, Clojure это через РЕПЛ. Вы должны настроить свою среду, будь то Vim, Emacs или IDE, чтобы вы могли легко перемещаться между статическим кодом в текстовых файлах и интерактивным REPL. Here is a good breakdown of a number of Clojure IDEs.

Теперь, насколько ваш код идет, нужно запомнить несколько вещей. Во-первых, почти никогда не было хорошей причины использовать eval. Если вы это сделаете, спросите себя, действительно ли это необходимо. Во-вторых, помните, Clojure - это функциональный язык, и обычно вам не нужно использовать набор макросов «do».Макросы «do» полезны, когда вам нужно иметь побочные эффекты (в вашем примере побочный эффект - это println to * out *) Наконец, следует избегать использования глобальных vars. Если вам нужно использовать vars, вам следует использовать макрос привязок, чтобы привязать vars локально к потоку к неизменяемым значениям, чтобы не возникало проблем с параллелизмом.

Я определенно рекомендую вам занять время, чтобы забрать Программу Clojure или другую более глубокую ссылку на LISP, чтобы по-настоящему понять сдвиг, необходимый в том, как вы думаете о программировании, чтобы эффективно использовать Clojure. Ваш небольшой образец здесь заставляет меня чувствовать, что вы пытаетесь написать императивный код в Clojure, который совсем не будет работать.

+0

Я знаю, что глобальные переменные просто вышли из-под контроля в этом 10-строчном скрипте, и я больше не буду использовать макросы «make» и eval даже в тех ситуациях, где они действительно необходимы. Я также расскажу вам о REPL, потому что у меня есть безупречная память о языках, которые я использовал только 10 минут, и мне не нужно сохранять мою работу для последующего использования. Как опытный профессионал Clojure, ваш праведный не-ответ наполняет меня стыдом. В следующий раз я постараюсь сделать больше. –

+0

Какой отвратительный смешной и саркастический комментарий. Я пытался помочь. Позор тебе. –

+0

Я не думаю, что уместно игнорировать вопрос и говорить о том, как код новичка не идеален. Чувствовал себя больше, как будто ты дул в свою трубу, чем пытался помочь. –