【问题标题】:Comparing each neighboring pairs in clojure vector比较clojure向量中的每个相邻对
【发布时间】:2021-06-07 09:33:42
【问题描述】:

我正在学习 Clojure。我发现一些练习需要为数组中的值查找索引,例如,低于下一个值。在Java中我会写

    for (int i = 1; ...)
       if (a[i-1] < a[i]) {result.add(i-1)}

在 Clojure 中,我发现 keep-indexed 很有用:

    (defn with-keep-indexed [v]
      (keep-indexed #(if (> %2 (get v %1)) %1) (rest v)))

好像没问题,但是

  1. 有更好的方法吗?
  2. 这种方法应该适用于“查找所有值”或“查找第一个值”(包含在first 中)。但是如果我需要“找到最后一个”怎么办。然后我必须(with-keep-indexed (reverse v))(last (with-keep-indexed v))。有没有更好的办法?

编辑:示例:for [1 1 2 2 1 2]

(with-keep-indexed [1 1 2 2 1 2])
;;=> (1 4)

【问题讨论】:

  • 你可能想看看这个函数partition

标签: clojure


【解决方案1】:

使用partition 将向量转换为连续对的序列。然后使用keep-indexed添加索引并过滤它们:

(defn indices< [xs]
  (keep-indexed (fn [i ys]
                  (when (apply < ys) i))
                (partition 2 1 xs)))
(indices< [1 1 2 2 1 2]) ;; => (1 4)

要查找最后一个这样的索引,请在此结果上使用last。虽然可以在输入上使用reverse,但它不会为这个问题提供任何性能优势。

【讨论】:

  • 我认为他想要所有这样的索引,结果是[1 4]。另外,我不明白使用reverse 的好处。
  • @AlanThompson 我的示例函数 indices&lt; 按照 OP 的要求给出了答案 (1 4),它与他们的示例相匹配。至于reverse,我承认使用last 没有任何好处。我想我会将它与 rseq 混淆了,如果适用的话,它对于大型输入集合具有更好的性能。
  • 我使用reverse 删除了我的示例函数,因为它没有帮助。
  • 我喜欢(apply &lt; ys)。我忘了你不需要为这么简单的事情进行解构。
【解决方案2】:

形成一对数字并将每个数字与序列中的下一个数字进行比较的逻辑可以在transducer 中分解出来,不关心您是否希望表格中的结果具有所有索引或仅最后一个索引的向量。正如其他答案中已经建议的那样,可以使用partition 来形成对,但我没有找到该功能的转换器实现,这将大大方便。这是一种使用映射转换器和一些可变状态的解决方法。

(defn indexed-pairs []
  (let [s (atom [-2 nil nil])]
    (comp (map #(swap! s (fn [[i a b]] [(inc i) b %])))
          (remove (comp neg? first)))))

(defn indices-of-pairs-such-that [f]
  (comp (indexed-pairs)
        (filter (fn [[i a b]] (f a b)))
        (map first)))

在这段代码中,函数indices-of-pairs-such-that 将返回一个转换器,我们可以通过多种方式使用它,例如使用into 来生成一个索引向量:

(into [] (indices-of-pairs-such-that <) [1 1 2 2 1 2])
;; => [1 4]

或者,正如问题中所问的那样,我们可以使用 tranduce 以及一个缩减函数,如果我们只想要最后一个索引,则该函数总是选择第二个参数:

(transduce (indices-of-pairs-such-that <) (completing (fn [a b] b)) nil [1 1 2 2 1 2])
;; => 4

这就是传感器的力量:它们将序列算法与这些算法的结果分离。函数indices-of-pairs-such-that 对序列算法进行编码,但不必知道我们想要所有索引还是只需要最后一个索引。

【讨论】:

    【解决方案3】:

    一般问题可以用...解决

    (defn indexes-of-pairs [p coll]
      (let [check-list (map (fn [i x rx] (when (p x rx) i)) (range) coll (rest coll))]
        (filter identity check-list)))
    

    ... 它返回由谓词p 关联的序列coll 的相邻对的索引。例如,

    (indexes-of-pairs < [1 1 2 2 1 2])
    => (1 4)
    

    对于您的示例,您可以定义

    (def with-keep-indexed (partial indexes-of-pairs <))
    

    然后

    (with-keep-indexed [1 1 2 2 1 2])
    => (1 4)
    

    【讨论】:

      【解决方案4】:

      有很多方法可以解决问题。这里有两种选择,包括使用my favorite template project 的单元测试。第一个以命令式风格对第一个 (N-1) 索引使用循环,与您在 Java 中编写的风格没有太大区别:

      (ns tst.demo.core
        (:use tupelo.core tupelo.test))
      
      (defn step-up-index-loopy
        [xs]    ; a sequence of "x" values
        (let-spy
          [xs    (vec xs) ; coerce to vector in case we get a list (faster)
           accum (atom []) ; an accumulator
           N     (count xs)]
          (dotimes [i (dec N)] ; loop starting at i=0
            (let-spy [j    (inc i)
                      ival (get xs i)
                      jval (get xs j)]
              (when (< ival jval)
                (swap! accum conj i))))
          @accum))
      

      运行时,它会产生以下输出:

      
      calling step-up-index-loopy
      xs => [1 1 2 2 1 2]
      accum => #object[clojure.lang.Atom 0x4e4dcf7c {:status :ready, :val []}]
      N => 6
      j => 1
      ival => 1
      jval => 1
      j => 2
      ival => 1
      jval => 2
      j => 3
      ival => 2
      jval => 2
      j => 4
      ival => 2
      jval => 1
      j => 5
      ival => 1
      jval => 2
      

      第二个使用更“实用”的样式,避免直接索引。有时这会使事情变得更简单,但有时它会显得更复杂。你来评判:

      (defn step-up-index
        [xs]    ; a sequence of "x" values
        (let-spy-pretty
          [pairs         (partition 2 1 xs)
           pairs-indexed (indexed pairs) ; append index # [0 1 2 ...] to beginning of each pair
           reducer-fn    (fn [accum pair-indexed]
                           ; destructure `pair-indexed`
                           (let-spy [[idx [ival jval]] pair-indexed]
                             (if (< ival jval)
                               (conj accum idx)
                               accum)))
           result        (reduce reducer-fn
                           [] ; initial state for `accum`
                           pairs-indexed)]
          result))
      

      函数indexed 来自the Tupelo Clojure library。 当你运行代码时,你会看到:

      calling step-up-index
      pairs => 
      ((1 1) (1 2) (2 2) (2 1) (1 2))
      pairs-indexed => 
      ([0 (1 1)] [1 (1 2)] [2 (2 2)] [3 (2 1)] [4 (1 2)])
      reducer-fn => 
      #object[tst.demo.core$step_up_index$reducer_fn__21389 0x108aaf1f "tst.demo.core$step_up_index$reducer_fn__21389@108aaf1f"]
      [idx [ival jval]] => [0 [1 1]]
      [idx [ival jval]] => [1 [1 2]]
      [idx [ival jval]] => [2 [2 2]]
      [idx [ival jval]] => [3 [2 1]]
      [idx [ival jval]] => [4 [1 2]]
      result => 
      [1 4]
      

      两者都有效:

      (dotest
        (newline)
        (println "calling step-up-index-loopy")
        (is= [1 4]
          (step-up-index-loopy [1 1 2 2 1 2]))
        (newline)
        (println "calling step-up-index")
        (is= [1 4]
          (step-up-index [1 1 2 2 1 2])))
      

      有结果:

      -----------------------------------
         Clojure 1.10.3    Java 15.0.2
      -----------------------------------
      
      Testing tst.demo.core
      
      Ran 2 tests containing 2 assertions.
      0 failures, 0 errors.
      

      let-spy 的形式来自 Tupelo Clojure 库,使编写和调试事情变得更容易。有关更多信息,请参阅文档。当满意一切正常时,替换为

      let-spy => let

      还请务必研究模板项目中包含的list of documentation sources,尤其是 Clojure CheatSheet。


      使用keep-indexed 的另一个解决方案非常简短:

      (defn step-up-index
        [xs]
        (let [pairs  (partition 2 1 xs)
              result (vec
                       (keep-indexed
                         (fn [idx pair]
                           (let [[ival jval] pair]
                             (when (< ival jval)
                               idx)))
                         pairs))]
          result))
      
      (dotest
        (is= [1 4] (step-up-index [1 1 2 2 1 2])))
      

      【讨论】:

        猜你喜欢
        • 2011-10-20
        • 2014-10-21
        • 2019-10-09
        • 1970-01-01
        • 1970-01-01
        • 2015-09-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多