остановить и разделить сгенерированную последовательность на повторах - clojure

Я пытаюсь создать последовательность, которая будет генерировать значения только до тех пор, пока не найдет следующие условия и не вернет перечисленные результаты:

головка корпуса =

  • 0 - вернуть {:origin [все сгенерировано, кроме 0] :pattern 0}
  • 1 — вернуть {:origin nil :pattern [все сгенерированные значения] }
  • повторяющееся значение - {:origin [значения-до-повторения] :pattern [значения-после-повторения]

{

; n = int
; x = int
; hist - all generated values

; Keeps the head below x 
(defn trim-head [head x]
  (loop [head head]
    (if (> head x)
      (recur (- head x))
      head)))

; Generates the next head
(defn next-head [head x n]
  (trim-head (* head n) x))

(defn row [x n]
   (iterate #(next-head % x n) n))

; Generates a whole row - 
; Rows are a max of x - 1.
(take (- x 1) (row 11 3))

Примеры случаев остановки до достижения конца строки:

[9 8 4 5 6 7 4] – повторяется "4", поэтому СТОП. Вернуть предыдущий как источник и остальное как образец.

{:origin [9 8] :pattern [4 5 6 7]}

[4 5 6 1] - нашел '1', поэтому СТОП, поэтому верните все как шаблон

{:origin nil :pattern [4 5 6 1]}

[3 0] — найдено '0', поэтому СТОП

{:origin [3] :pattern [0]}

:else, если последовательность достигает длины x - 1:

{:origin [all values generated] :pattern nil}

Эта проблема

Я использовал partition-by с некоторым успехом, чтобы разделить группы в точке, где найдено повторяющееся значение, но хотел бы делать это лениво. Можно ли каким-то образом использовать take-while, condp или предложение :while цикла for, чтобы создать условие, которое разделяет при обнаружении повторов?

Некоторые попытки

(take 2 (partition-by #(= 1 %) (row 11 4)))

(for [p (partition-by #(stop-match? %) head) (iterate #(next-head % x n) n)
        :while (or (not= (last p) (or 1 0 n) (nil? (rest p))]
  {:origin (first p) :pattern (concat (second p) (last p))}))

# Обновления

Что я действительно хочу сделать, так это узнать, повторяется ли значение, и разделить последовательность без использования индекса. Это возможно? Что-то вроде этого -

{

(defn row [x n]
  (loop [hist [n]
         head (gen-next-head (first hist) x n)
         steps 1]
    (if (>= (- x 1) steps)
      (case head
        0 {:origin [hist] :pattern [0]}
        1 {:origin nil :pattern (conj hist head)}
        ; Speculative from here on out 
        (let [p (partition-by #(apply distinct? %) (conj hist head))]
          (if-not (nil? (next p)) ; One partition if no repeats.
            {:origin (first p) :pattern (concat (second p) (nth 3 p))}
            (recur (conj hist head) (gen-next-head head x n) (inc steps)))))
      {:origin hist :pattern nil})))

}


person Community    schedule 26.10.2012    source источник


Ответы (2)


Невозможна большая лень: вы можете лениво потреблять новые элементы, но вы должны цепляться за все старые элементы, чтобы использовать их в качестве шаблона, поэтому в такой последовательности, как (iterate inc 2), вы должны использовать всю доступную память. Кроме того, for позволяет вам одновременно просматривать только один элемент, поэтому он плохо подходит для этой задачи. Однако написать его как цикл/повторение хоть и немного утомительно, но несложно. Вы не указали, что возвращать, если последовательность заканчивается до повтора, 1 или 0, так что я просто догадался.

Кроме того, ваш первый пример вывода неверен: он должен останавливаться на 1, а не на 4, поэтому я скорректировал ваш ввод. Помимо этого, однако, вопрос хорошо задан: спасибо за четкое указание проблемы и описание того, с чем у вас возникли проблемы, а также то, что вы пробовали.

(defn trim-head [coll]                                                      
  (loop [so-far [], indexes {}, index 0, coll (seq coll)]                   
    (if-not coll                                                            
      {:origin nil, :pattern so-far} ;; ?? not specified in question        
      (let [x (first coll), xs (rest coll)]                                 
        (if (contains? indexes x)                                           
          {:origin (subvec so-far 0 (indexes x))                            
           :pattern (subvec so-far (indexes x))}                            
          (case x                                                           
            0 {:origin so-far, :pattern [x]}                                
            1 {:origin nil, :pattern (conj so-far x)}                       
            (recur (conj so-far x) (assoc indexes x index) (inc index) (seq xs))))))))

user> (map trim-head [[9 8 2 4 5 6 7 4] [4 5 6 1] [3 0]])                       
({:origin [9 8 2], :pattern [4 5 6 7]}
 {:origin nil, :pattern [4 5 6 1]} 
 {:origin [3], :pattern [0]})
person amalloy    schedule 26.10.2012
comment
Спасибо за быстрый ответ. Я поместил зацикленную версию в обновления с большим количеством предположений, но я все еще не понимаю, почему разбиение нельзя использовать с разбиением на любое значение, уже найденное в последовательности. Буду вносить изменения, используя индекс, так как я не могу придумать другого способа. - person ; 26.10.2012

Что я действительно хочу сделать, так это узнать, повторяется ли значение, и разделить последовательность без использования индекса. Это возможно?

Я сразу реализовал ваше обновленное требование. В этом случае split-with предпочтительнее partition-by.

;;; find out if a value has repeated, but considering zero and one.
(defn- generate
  "Returns a vector of [duplicate-value values-until-duplicate].
   duplicate-value might be zero or one."
  [s]
  (->> [s [] #{0 1}]
       (iterate (fn [[[head & more] generated idx]]
                  [more (conj generated head) (conj idx head)]))
       (take-while (comp seq first))
       (drop-while (fn [[[head & _] _ idx]]
                     (nil? (idx head))))
       first
       ((juxt ffirst second))))

;;; partition the seq without using the index.
(defn partition-by-duplicate
  [s]
  (let [[pivot generated-values] (generate s)]
    (cond (= 0 pivot) {:origin generated-values, :pattern [0]}
          (= 1 pivot) {:origin nil, :pattern (conj generated-values 1)}
          pivot (->> generated-values
                     (split-with (partial not= pivot))
                     (interleave [:pattern :origin])
                     (apply hash-map))
          :else {:origin s, :pattern nil})))

Пример:

user> (map generate [[9 8 2 4 5 6 7 4] [4 5 6 1] [3 0]])
([4 [9 8 2 4 5 6 7]]
 [1 [4 5 6]]
 [0 [3]])

user> (map partition-by-duplicate [[9 8 2 4 5 6 7 4] [4 5 6 1] [3 0]])
({:pattern (9 8 2), :origin (4 5 6 7)}
 {:origin nil, :pattern [4 5 6 1]}
 {:origin [3], :pattern [0]})
person tnoda    schedule 28.10.2012
comment
Это отличное решение. Я понимаю, что должен был привести примеры с использованием генераторов. Я многому научился из вашего решения. Никогда не знал, что макрос потоков можно использовать таким образом. - person ; 29.10.2012