Ошибка Clojure NullPointerException - PullRequest
       7

Ошибка Clojure NullPointerException

2 голосов
/ 28 сентября 2011

Я новичок в clojure и пытаюсь написать простую функцию, которая получает список чисел и фильтрует только четные числа.

Я хочу сделать это без фильтра или даже?, Только чистый clojure

(defn my-even [ilist]
   (if
    (= (mod (first ilist) 2) 0)
    (concat (list (first ilist)) (my-even (rest ilist)))
    (my-even (rest ilist))
   )
)

Я пытаюсь запустить его:

(my-even '(1,2,3,4,5))

Но получаю ошибку:

#<CompilerException java.lang.NullPointerException (NO_SOURCE_FILE:0)>

Что не так?

Спасибо.

Ответы [ 4 ]

7 голосов
/ 28 сентября 2011

Как сказал Джонас, у вас нет базового случая;в дополнение к этому, это не идиоматический Clojure (или любой другой Лисп), чтобы ставить парены в отдельных строках, а также хранить предикат if в той же строке.

С деструктурой это немного более читабельно:

(defn my-even? [coll]
  (if-let [[first & rest] coll]
    (if (= (mod first 2) 0)
      (cons first (my-even? rest))
      (my-even? rest))))
7 голосов
/ 28 сентября 2011

Ваша рекурсивная функция my-even не имеет базового варианта .Что происходит, когда в списке больше нет элементов?(first ilist) возвращает nil, а (mod nil 2) создает исключение NullPointerException.

Вы должны как-то проверить наличие пустого списка.

3 голосов
/ 28 сентября 2011

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

Получив базовую рекурсивную форму, вы можете превратить ее в идиоматическое Clojure, как правило:

1) используйте хвостовые рекурсивные формы, когда можете (вы уже это сделали)
2) замените прямую рекурсию на вызов recur, чтобы избежать перебора стека. (начиная с рабочего ответа Хамзы)

(defn my-even? [coll]
   (if-let [[first & rest] coll]
    (if (= (mod first 2) 0)
      (cons first (my-even? rest))
      (recur rest))))

recur заставляет компилятор переходить к началу кадра стека вместо выделения нового. без этого он взорвется в стеке.

3) во многих случаях вы можете исключить шаблон (defn [] ... (recur)) с помощью функции более высокого порядка, например map, reduce, filter, for и т. Д. В этом упражнении я вижу, что вы пытаетесь не используйте filter или even, так что, очевидно, вы могли бы написать my-filter и my-even, и это было бы нормально;)

4) извлекает делимые части (строит список, выбирает, что включить) в повторно используемые функции и загружает любые, которые обычно полезны для проекта contrib clojure:)

5) очень внимательно подумайте, если вы обнаружите, что используете (lazy-seq ...), поскольку есть большая вероятность, что вы заново изобретаете колесо.

1 голос
/ 29 сентября 2011

Вот еще одно решение, которое не требует деструктуризации, только основные функции lisp и схемы.

(defn my-even [ilist]                                                                                                                                                                                                                       
  (cond (empty? ilist) '() ;; base case                                                                                                                                                                                                               
        (= (mod (first ilist) 2) 0)                                                                                                                                                                                                         
        (cons (first ilist) (my-even (next ilist)))                                                                                                                                                                                         
        :else (my-even (next ilist))))
...