Ваша проблема в том, что (= 0 (count str-in))
никогда не меняется, поэтому вы в конечном итоге пытаетесь вызвать first
на nil
. [ РЕДАКТИРОВАТЬ: Я не прав, ваш код на самом деле работает как есть - то, что оператор просто не работает. Надеюсь, вам все равно понравится этот ответ.]
Вместо этого вы должны вызывать next
(не rest
) на list-Rem
в recur
и использовать list-Rem
непосредственно в тесте if
, используя свойство next
, которое возвращает nil для пустой последовательности. Вот как бы я переписал твой код:
(defn first-duplicate [in]
(loop [seen #{}
remain (seq in)]
(when-let [[head & tail] remain]
(if (contains? seen head)
head
(recur (conj seen head) tail)))))
Изменения:
- строчные имена
- нет необходимости вызывать
seq
на выходе next
или rest
- с наборами,
contains?
(проверка на наличие ключа) быстрее, чем some
(запуск предиката для элементов, пока что-то не даст истинное значение)
- установить буквальный
- с использованием
when
для возврата nil
при неудачном тесте
- с использованием
when-let
для деструкции и связывания первого символа и отдыха
Стилистические изменения:
- имена изменены, чтобы быть менее конкретными (например, не ограничиваясь строками)
- изменить порядок локальных циклов так, чтобы recur больше походил на структурную рекурсию
- поместить локальные петли в отдельные строки