2013-02-18 3 views
2

Я пытаюсь решить проблему, установленную на 4clojure.org. Я нахожусь на номер 23 (переверните строку, не используя функцию «reverse»). Я получаю эту ошибку:реверсивные последовательности без стандартных библиотек в Clojure

Don't know how to create ISeq from: java.lang.Long

вот мой код:

(fn rev [coll] 
    (if (=() coll) 
    nil 
    ((cons(rev (rest coll))(first coll))))) 

отредактирован теперь это:

(fn rev [coll] 
    (if (empty? coll) 
    coll 
    (concat(rev (rest coll))((list first coll))))) 

предположительно это от попыток минусов глава последовательности конец остальной последовательности.

Каков правильный способ сделать это?

ответ

3

Эта ошибка возникает из-за того, что вы пытаетесь использовать cons a seq элементу, а не элементу, на seq. Другими словами, ваши аргументы в cons находятся в неправильном порядке. Но, если вы скорректировали заказ, вы просто собираете список вместе в том же порядке.

Используя ту же общую идею у вас есть, вы можете превратить второй аргумент в список, обернув его в (list ...), а затем concat два списка вместе:

(fn rev [coll] 
    (if (empty? coll) 
    coll 
    (concat 
     (rev (rest coll)) 
     (list (first coll))))) 

Вы откроете для себя более лаконичных решений, как вам идти вперед.

+0

Я не могу заставить это запустить: java.lang.ClassCastException: clojure.lang.PersistentList не может быть отброшен в clojure.lang.IFn – Ramy

+0

@Ramy В вашем исходном коде у вас есть посторонний набор круглых скобок вокруг последнего line: '((cons ...))' -> '(cons ...)'. Убедитесь, что вы разделили их, если вы конвертируете свой код, как указано выше. Копирование моего дословного, я не вижу проблем для тестов 4Clojure. –

3

Второй аргумент cons в clojure должен быть последовательностью, однако (first coll) не является последовательностью, а элементом коллекции. Возможно, вы передаете коллекцию чисел, поэтому (first coll) плевать число (длинное) и clojure не может создать ISeq из числа.

user=> (doc cons) 
------------------------- 
clojure.core/cons 
([x seq]) 
    Returns a new seq where x is the first element and seq is 
    the rest. 

Вы можете осуществить обратное просто, как следующее:

(fn rev [coll] 
    (reduce conj() coll)) 

проверить код выше и он прошел все три теста на 4clojure сайте.

+0

Это замечательно, но слишком плотно для новичка, подобного мне. – Ramy

1

Как О.П., я разочаровываюсь, пытаясь построить рекурсивные решения с when-let и if, next и last, и т.д., и т.д. Так что теперь я пытаюсь понять @ntalbs ответа и посмотреть, смогу ли я принять это дальше , изучая документацию Clojure по адресу http://clojuredocs.org/quickref/Clojure%20Core.

Часть # 1 - Замечания:
cons является функцией списков, в то время как conj основная функция коллекций, а concat указана в разделе «Использование (модификация)» последовательностей, но он возвращает ленивую Последовательность. Итак, как эти вещи различаются на практике?

  1. порядок имеет аргумент: вы не можете conj значение и последовательность, только последовательность и значение. Для cons это наоборот. concat выглядит более прощающим.

    user=> (type (conj 1 '(2 3 4 5))) 
    ClassCastException java.lang.Long cannot be cast to clojure.lang.IPersistentCollection clojure.core/conj (core.clj:83) 
    user=> (type (cons '(2 3 4 5) 1)) 
    IllegalArgumentException Don't know how to create ISeq from: java.lang.Long clojure.lang.RT.seqFrom (RT.java:505) 
    
  2. Три различных типа возврата: Cons, PersistentList или LazySeq

    user=> (type (cons 1 '(2 3 4 5))) 
    clojure.lang.Cons 
    user=> (type (conj '(2 3 4 5) 1)) 
    clojure.lang.PersistentList 
    user=> (type (concat 1 '(2 3 4 5))) 
    clojure.lang.LazySeq 
    
  3. различное поведение для различных типов коллекций:

    user=> (cons 3 (sorted-set 5 7 2 7)) 
    (3 2 5 7)  ; type = Cons, 3 is just appended to the list, 
    user=> (conj (sorted-set 5 7 2 7) 3) 
    #{2 3 5 7} ; type = PersistentTreeSet, with 3 in the correct position. 
    user=> (concat 3 (sorted-set 5 7 2 7)) ; LazySeq can't be directly returned, so...(order doesn't matter) 
    IllegalArgumentException Don't know how to create ISeq from: java.lang.Long clojure.lang.RT.seqFrom (RT.java:505) 
    

На мой взгляд, conj имеет самое простое поведение, поэтому я буду использовать это по предпочтению, если я действительно не хочу ленивой последовательности или конкретно списка.

Часть 2 - @ Рами 'слишком плотный для меня' Понимание решения @ ntalbs.

Вышеприведенное описание предполагает, что conj является наиболее подходящим методом для добавления вещей в коллекцию, что и делает решение @ ntalbs. Использование для reduce составляет http://clojuredocs.org/clojure_core/clojure.core/reduce Это эффективный способ применения функции при сборе значений в коллекции.

(reduce f val coll) 

Так, reduce будет применять f для каждого члена коллекции, начиная с val. Таким образом, звонок (reduce conj() coll) берет коллекцию и сначала применяет (conj() (first coll)). Затем он применяет (conj result (second coll)) и (conj result (third coll)) и т. Д., Где result является результатом предыдущего шага.

reduce выглядит как очень мощная команда.

Часть # 3 - еще одно решение

(fn rev [coll] 
    (into() coll)) 

Из документации, into, как представляется, синтаксический сахар для (reduce conj to-coll from-coll). Я не уверен, что это элегантно или просто плотно. Тем не менее, он работает с минимальными нажатиями клавиш.

+0

+1 для подробного объяснения и решения # 1. Это удобно. Фактически 'in' является функцией, и вы можете увидеть исходный код' (source in) 'в replia clojure. – ntalbs