Vector Clojure-在向量中查找相同值及其索引的最长条纹

Vector Clojure-在向量中查找相同值及其索引的最长条纹,vector,indexing,clojure,find,Vector,Indexing,Clojure,Find,在向量中,我想找到某个值的最长条纹和该条纹的起始索引 示例:1[0 0 1 0 1 0]的最长条纹应返回{:cnt 3:at 2}。 我发现的两种解决方案对我来说并不十分简单——我还在学习,所以请耐心等待。 任何提供更优雅解决方案的答案都是受欢迎的 这是我的第一次尝试: (defn longest-streak-of "Returns map with :cnt (highest count of successive n's) and :at (place in arr)&quo

在向量中,我想找到某个值的最长条纹和该条纹的起始索引

示例:1[0 0 1 0 1 0]的最长条纹应返回{:cnt 3:at 2}。 我发现的两种解决方案对我来说并不十分简单——我还在学习,所以请耐心等待。 任何提供更优雅解决方案的答案都是受欢迎的

这是我的第一次尝试:

(defn longest-streak-of
  "Returns map with :cnt (highest count of successive n's) and :at (place in arr)"
  [n arr]
  (loop [arr arr streak 0 oldstreak 0 arrcnt 0 place 0]
    (if (and (not-empty arr) (some #(= n %) arr))
      (if (= (first arr) n)
        (recur (rest arr) (+ streak 1) oldstreak (inc arrcnt) place)
        (recur (rest arr) 0 (if (> streak oldstreak)
                              streak oldstreak)
               (inc arrcnt) (if (> streak oldstreak)
                              (- arrcnt streak) place)))
      (if (> streak oldstreak) {:cnt streak :at (- arrcnt streak)}
          {:cnt oldstreak :at place}))))
第二个解决方案使用clojure.string,但比上面的解决方案慢,我对两个函数都计时,这需要两倍的时间。我更喜欢这样的东西,希望不使用字符串库,因为我认为它更容易阅读和理解:

(ns lso.core
  (:require [clojure.string :as s])
  (:gen-class))

(defn lso2 [n arr]
  (let [maxn (apply max (map count (filter #(= (first %) n) (partition-by #(= n %) arr))))]
    {:cnt maxn :at (s/index-of (s/join "" arr) (s/join (repeat maxn (str n))))}))
提前感谢您的任何见解

读了艾伦的答案后的新版本:

(defn lso3
;; This seems to be the best solution yet 
  [n arr]
  (if (some #(= n %) arr)
    (let [parts (partition-by #(= n %) arr)
          maxn (apply max (map count (filter #(= (first %) n) parts)))]
      (loop [parts parts idx 0]
        (if-not (and (= maxn (count (first parts))) (= n (first (first parts))))
          (recur (rest parts) (+ idx (count (first parts))))
          {:cnt maxn :at idx})))
    {:cnt 0 :at 0}))
请特别注意。您正在寻找使用拆分的函数

更好的答案 我认为这个版本使用helper函数索引到数组中比我最初的答案更简单,因为我的答案是分割集合:

(ns tst.demo.core
  (:use tupelo.core tupelo.test)
  (:require
    [schema.core :as s]
    [tupelo.schema :as tsk]))

(s/defn streak-info :- [tsk/KeyMap]
  [coll :- tsk/List]
  (let [coll          (vec coll)
        N             (count coll)

        streak-start? (s/fn streak-start? :- s/Bool
                        [idx :- s/Num]
                        (assert (and (<= 0 idx) (< idx N)))
                        (if (zero? idx)
                          true
                          (not= (nth coll (dec idx)) (nth coll idx))))

        result        (reduce
                        (fn [accum idx]
                          (if-not (streak-start? idx)
                            accum
                            (let [coll-remaining (subvec coll idx)
                                  streak-val     (first coll-remaining)
                                  streak-vals    (take-while #(= streak-val %) coll-remaining)
                                  streak-len     (count streak-vals)
                                  accum-next     (append accum {:streak-idx idx
                                                                :streak-len streak-len
                                                                :streak-val streak-val})]
                              accum-next)))
                        []
                        (range N))]
    result))
然后,我们只需丢弃所有没有期望值1的条纹,然后通过“最大关键点”找到最长的条纹

请注意,在平局的情况下,max key使用“最后一个胜出”技术

原始答案 要开始,请删除任何前导的0元素。然后,在遇到下一个0时,使用“拆分带”分割序列。计算找到的1个元素,并与索引一起保存

以上内容需要包装成循环/重现、减少或类似形式

你说,如何跟踪索引?最简单的方法是将您的值序列转换为一个len-2向量对序列,其中每对向量的第一项是索引。一种简单的方法是使用索引函数:

这简化为

(defn indexed [vals]
  (mapv vector (range) vals))
因此,我们有一个例子:

(indexed [0 0 1 1 0]) =>
    [[0 0]
     [1 0]
     [2 1]
     [3 1]
     [4 0]]
单元测试的样品溶液: 上面显示了通过辅助函数测试零。下面是我们如何找到和分析索引对序列中的第一条条纹:

(defn count-streak
  [pairs]
  (let [v1        (drop-while zero-val? pairs)
        [one-pairs remaining-pairs] (split-with #(not (zero-val? %)) v1)
        ones-cnt  (count one-pairs)
        first-pair (first one-pairs)
        idx-begin (first first-pair)]
    ; create a map like
    ;   {:remaining-pairs remaining-pairs
    ;    :ones-cnt        ones-cnt
    ;    :idx-begin       idx-begin}
    (t/vals->map remaining-pairs ones-cnt idx-begin)))

(dotest
  (is= (count-streak (indexed [0 0 1 1 0]))
    {:idx-begin       2
     :ones-cnt        2
     :remaining-pairs [[4 0]]}))
然后使用“循环/重复”查找最长的条纹

(defn max-streak
  [vals]
  (loop [idx-pairs   (indexed vals)
         best-streak {:best-len -1 :best-idx nil}]
    (if (empty? idx-pairs)
      (if (nil? (grab :best-idx best-streak))
        (throw (ex-info "No streak of 1's found" (vals->map best-streak idx-pairs)))
        best-streak)
      (let [curr-streak (count-streak idx-pairs)]
        (t/with-map-vals curr-streak [remaining-pairs ones-cnt idx-begin]
          (t/with-map-vals best-streak [best-len best-idx]
            (if (< best-len ones-cnt)
              (recur remaining-pairs {:best-len ones-cnt :best-idx idx-begin})
              (recur remaining-pairs best-streak))))))))

(dotest
  (throws? (max-streak [0 0 0]) )
  (is= (max-streak [0 0 1 1 0]) {:best-len 2, :best-idx 2})
  (is= (max-streak [0 0 1 1 0 1 0]) {:best-len 2, :best-idx 2})
  (is= (max-streak [0 1 0 1 1 0]) {:best-len 2, :best-idx 3})
  (is= (max-streak [0 1 1 0 1 1 1 0]) {:best-len 3, :best-idx 4}))
请特别注意。您正在寻找使用拆分的函数

更好的答案 我认为这个版本使用helper函数索引到数组中比我最初的答案更简单,因为我的答案是分割集合:

(ns tst.demo.core
  (:use tupelo.core tupelo.test)
  (:require
    [schema.core :as s]
    [tupelo.schema :as tsk]))

(s/defn streak-info :- [tsk/KeyMap]
  [coll :- tsk/List]
  (let [coll          (vec coll)
        N             (count coll)

        streak-start? (s/fn streak-start? :- s/Bool
                        [idx :- s/Num]
                        (assert (and (<= 0 idx) (< idx N)))
                        (if (zero? idx)
                          true
                          (not= (nth coll (dec idx)) (nth coll idx))))

        result        (reduce
                        (fn [accum idx]
                          (if-not (streak-start? idx)
                            accum
                            (let [coll-remaining (subvec coll idx)
                                  streak-val     (first coll-remaining)
                                  streak-vals    (take-while #(= streak-val %) coll-remaining)
                                  streak-len     (count streak-vals)
                                  accum-next     (append accum {:streak-idx idx
                                                                :streak-len streak-len
                                                                :streak-val streak-val})]
                              accum-next)))
                        []
                        (range N))]
    result))
然后,我们只需丢弃所有没有期望值1的条纹,然后通过“最大关键点”找到最长的条纹

请注意,在平局的情况下,max key使用“最后一个胜出”技术

原始答案 要开始,请删除任何前导的0元素。然后,在遇到下一个0时,使用“拆分带”分割序列。计算找到的1个元素,并与索引一起保存

以上内容需要包装成循环/重现、减少或类似形式

你说,如何跟踪索引?最简单的方法是将您的值序列转换为一个len-2向量对序列,其中每对向量的第一项是索引。一种简单的方法是使用索引函数:

这简化为

(defn indexed [vals]
  (mapv vector (range) vals))
因此,我们有一个例子:

(indexed [0 0 1 1 0]) =>
    [[0 0]
     [1 0]
     [2 1]
     [3 1]
     [4 0]]
单元测试的样品溶液: 上面显示了通过辅助函数测试零。下面是我们如何找到和分析索引对序列中的第一条条纹:

(defn count-streak
  [pairs]
  (let [v1        (drop-while zero-val? pairs)
        [one-pairs remaining-pairs] (split-with #(not (zero-val? %)) v1)
        ones-cnt  (count one-pairs)
        first-pair (first one-pairs)
        idx-begin (first first-pair)]
    ; create a map like
    ;   {:remaining-pairs remaining-pairs
    ;    :ones-cnt        ones-cnt
    ;    :idx-begin       idx-begin}
    (t/vals->map remaining-pairs ones-cnt idx-begin)))

(dotest
  (is= (count-streak (indexed [0 0 1 1 0]))
    {:idx-begin       2
     :ones-cnt        2
     :remaining-pairs [[4 0]]}))
然后使用“循环/重复”查找最长的条纹

(defn max-streak
  [vals]
  (loop [idx-pairs   (indexed vals)
         best-streak {:best-len -1 :best-idx nil}]
    (if (empty? idx-pairs)
      (if (nil? (grab :best-idx best-streak))
        (throw (ex-info "No streak of 1's found" (vals->map best-streak idx-pairs)))
        best-streak)
      (let [curr-streak (count-streak idx-pairs)]
        (t/with-map-vals curr-streak [remaining-pairs ones-cnt idx-begin]
          (t/with-map-vals best-streak [best-len best-idx]
            (if (< best-len ones-cnt)
              (recur remaining-pairs {:best-len ones-cnt :best-idx idx-begin})
              (recur remaining-pairs best-streak))))))))

(dotest
  (throws? (max-streak [0 0 0]) )
  (is= (max-streak [0 0 1 1 0]) {:best-len 2, :best-idx 2})
  (is= (max-streak [0 0 1 1 0 1 0]) {:best-len 2, :best-idx 2})
  (is= (max-streak [0 1 0 1 1 0]) {:best-len 2, :best-idx 3})
  (is= (max-streak [0 1 1 0 1 1 1 0]) {:best-len 3, :best-idx 4}))

这是我的建议:

user> (->> [0 0 1 1 1 0 1 0]
           (map-indexed vector)               ;; ([0 0] [1 0] [2 1] [3 1] [4 1] [5 0] [6 1] [7 0])
           (partition-by second)              ;; (([0 0] [1 0]) ([2 1] [3 1] [4 1]) ([5 0]) ([6 1]) ([7 0]))
           (filter (comp #{1} second first))  ;; (([2 1] [3 1] [4 1]) ([6 1]))
           (map (juxt ffirst count))          ;; ([2 3] [6 1])
           (apply max-key second)             ;; [2 3]
           (zipmap [:at :cnt]))               ;; {:at 2, :cnt 3}

;; {:at 2, :cnt 3}
或者将其包装到函数中:

(defn longest-run [item data]
  (when (seq data)  ;; to prevent exception on apply for empty data
    (->> data
         (map-indexed vector)
         (partition-by second)
         (filter (comp #{item} second first))
         (map (juxt ffirst count))
         (apply max-key second)
         (zipmap [:at :cnt]))))

user> (longest-run 1 [1 1 1 2 2 1 2 2 2 2 2])
;;=> {:at 0, :cnt 3}
更新

这将防止应用错误时出现空seq:


这是我的建议:

user> (->> [0 0 1 1 1 0 1 0]
           (map-indexed vector)               ;; ([0 0] [1 0] [2 1] [3 1] [4 1] [5 0] [6 1] [7 0])
           (partition-by second)              ;; (([0 0] [1 0]) ([2 1] [3 1] [4 1]) ([5 0]) ([6 1]) ([7 0]))
           (filter (comp #{1} second first))  ;; (([2 1] [3 1] [4 1]) ([6 1]))
           (map (juxt ffirst count))          ;; ([2 3] [6 1])
           (apply max-key second)             ;; [2 3]
           (zipmap [:at :cnt]))               ;; {:at 2, :cnt 3}

;; {:at 2, :cnt 3}
或者将其包装到函数中:

(defn longest-run [item data]
  (when (seq data)  ;; to prevent exception on apply for empty data
    (->> data
         (map-indexed vector)
         (partition-by second)
         (filter (comp #{item} second first))
         (map (juxt ffirst count))
         (apply max-key second)
         (zipmap [:at :cnt]))))

user> (longest-run 1 [1 1 1 2 2 1 2 2 2 2 2])
;;=> {:at 0, :cnt 3}
更新

这将防止应用错误时出现空seq:


可在无明显循环的情况下完成:

user> (defn longest-streak-of [v]
        (->> (map vector v (range)) 
             (partition-by first) 
             (map (fn [r] {:at (second (first r)) :cnt (count r)})) 
             (apply max-key :cnt)))
#'user/longest-streak-of
user> (longest-streak-of [0 0 1 1 1 0 1 0])
{:at 2, :cnt 3}
第一步将每个成员与其位置配对。然后通过忽略位置的值对向量进行分块;因此,我们可以捕获起始位置和长度


我想,通过颠倒最后两个步骤,也就是说,使用count执行max key并仅在最后形成{:at,:cnt}摘要,可以提高效率。

无需公开循环:

user> (defn longest-streak-of [v]
        (->> (map vector v (range)) 
             (partition-by first) 
             (map (fn [r] {:at (second (first r)) :cnt (count r)})) 
             (apply max-key :cnt)))
#'user/longest-streak-of
user> (longest-streak-of [0 0 1 1 1 0 1 0])
{:at 2, :cnt 3}
第一步将每个成员与其位置配对。然后通过忽略位置的值对向量进行分块;因此,我们可以捕获起始位置和长度


我想,通过颠倒最后两个步骤,也就是说,使用count执行max key并仅在最后形成{:at,:cnt}摘要,可以提高效率。

谢谢-显然,我不会使用循环。在我看来,split with与partition by正好相反,我在第二次尝试中已经使用了它。所以这一切都是关于获取条纹的索引。你可以将循环/重现重构为reduce,它在内部使用循环/重现。谢谢你-显然我不会到处使用循环。在我看来,split with与partition by正好相反,我在第二次尝试中已经使用了它。所以这一切都是关于获取条纹的索引。您可以将循环/重现重构为reduce,它使用循环/重现intern
这是我一直在寻找的答案,非常像Clojur。但是,如果该项根本不在数据中,则会触发一个异常,这可以很容易地修复。love solutionpartition by是这里使用的一个很好的函数。其他部分事后很难阅读。例如,map juxt ffirst count让我想到什么。。。???我不明白人们是如何理解这个线程化表单的,除非你在repl上用一行一行的样本数据开发它。这是我一直在寻找的答案类型,非常Clojur式。但是,如果该项根本不在数据中,则会触发一个异常,这可以很容易地修复。love solutionpartition by是这里使用的一个很好的函数。其他部分事后很难阅读。例如,map juxt ffirst count让我想到什么。。。???我不明白人们是如何理解这个线程形式的,除非你在repl上用一行一行的样本数据开发它。这只会给我任何东西中最长的条纹,但正如我在开始时所说的,我需要一个给定的值,这样我就可以在[0 2 2 0 1 1 1 1]中获得最长的条纹2s。无论如何,谢谢你们,我向你们所有人学习。这只会给我任何东西中最长的条纹,但正如我在开始时所说的,我需要一个给定的值,这样我就可以在[0 2 0 1 1 1]中获得2的最长条纹。无论如何,谢谢你们,我向你们大家学习。