【问题标题】:Clojure, comparing vectors of integers: why "longer" vector is always "greater"? Is there a remedy?Clojure,比较整数向量:为什么“更长”的向量总是“更大”?有补救措施吗?
【发布时间】:2014-11-15 16:50:45
【问题描述】:

它是这样工作的:

pcc.core=> (compare [4] [2 2])
-1
pcc.core=> (compare [4 0] [2 2])
1

我想要一个带有“字符串语义”的向量比较器:

pcc.core=> (compare-like-strings [4] [2 2])
1 ;; or 2, for that matter
pcc.core=> (compare-like-strings [4 0] [2 2])
1

有没有一种轻量级的好方法来获得我想要的东西?

【问题讨论】:

  • 为什么是“字符串语义”?我希望字符串比较具有与向量相同的行为(这就是 Clojure 和 Java 的行为至少)。
  • @AlexMiller,(compare "abc" "ac") => -1,但(compare [1 2 3] [1 3]) => 1。
  • @AlexMiller 传统上按字典顺序比较字符串,Clojure 和 Java 都继承了这一传统。不同之处在于向量在内容之前比较长度,而字符串仅在前 N 项都比较相同时才使用长度。
  • 啊,我明白你的意思了。

标签: vector clojure comparator


【解决方案1】:

怎么样:

(defn compare-like-strings [[x & xs] [y & ys]]
  (let [c (compare x y)]
    (if (and (zero? c) (or xs ys))
      (recur xs ys)
      c)))

【讨论】:

  • 这遗漏了一些边缘情况,因为 xsys 上的 nil-punning 以及未能检查空输入。例如,(compare-like-strings [nil] []) 返回 0。
  • @amalloy 不错。最好将它们与普通的旧循环进行比较。如果你用完了一个并且到目前为止它一直在匹配,则根据用完的一个返回 -1 或 1。
  • 我的经验法则基本上是永远不要使用[x & xs] 解构。这在理论上很有吸引力,但在实践中我总是想更懒一点,或者不能确定至少有一个元素或类似的东西,所以我最终使用seqfirstnextrest.
  • 我最终用countsubvec 修复了它,但代码太丑了,无法发布。关于[x & xs] 的好点,在这种情况下它是复合的,因为有两个序列。
  • 我同意循环比较是最好的:当您处理两个不同长度的序列时,最没有创意和最乏味的解决方案胜过所有聪明的 HOF 方法。如果你小心点,你可以让它适用于任意序列以及向量。
【解决方案2】:

目前为止

(defn cmpv-int
  "Compare vectors of integers using 'string semantics'"
  [vx vy]
  (let [res (first (drop-while zero? (map compare vx vy)))
        diffenence (- (count vx) (count vy))]
     (if res res diffenence)
    )
  )

基于 Fabian 方法。

【讨论】:

  • 这是这里唯一的答案(除了我的,现在),它实际上得到了所有的情况。不过,关于风格的几点说明:Lisps 不喜欢在自己的行上使用近括号,所以只需将最后两个放在最后,就像difference))) 一样。另外,(if x x y) 是一种冗长的写法(or x y);一旦你这样做了,你可以内联所有的本地人,得到(or (first ...) (- ...))
  • 如果您合并@amalloy 提出的更改,我认为如果您接受自己的答案,没有人会抱怨。它解决了您的问题,并且非常干净优雅。
【解决方案3】:

为什么不使用subvec

(defn compare-like-strings
  [vec1 vec2]
  (let [len (min (count vec1) (count vec2))]
    (compare (subvec vec1 0 len)
             (subvec vec2 0 len))))

【讨论】:

  • 当一个向量是另一个向量的前缀时实际上不起作用:这只是写法比安答案的冗长方式,(-> (first (remove zero? (map compare xs ys))) (or 0))
【解决方案4】:

如果两个向量的长度相同,比较似乎有效,所以让我提供这个:

(defn compare-vectors
  [a b]
  (compare
    (reduce conj a (map #{} b))
    (reduce conj b (map #{} a))))

这基本上是在运行比较之前用尽可能多的nils 填充输入。我喜欢它的外观(它应该完全符合您的要求),但我不确定我是否会向任何人推荐它。 ;)

(compare-vectors [2 2] [2 2]) ;; => 0
(compare-vectors [4 2] [2 2]) ;; => 1
(compare-vectors [2 2] [4 2]) ;; => -1
(compare-vectors [4]   [2 2]) ;; => 1

编辑:我可能不会 - 这非常低效。

【讨论】:

  • 这个答案也不适用于包含 nils 的向量:考虑 [][nil]
  • @amalloy:哦,我知道。但是由于这里的每个人基本上都在编写相同的循环(并且问题定义不够明确,无法找到确切的解决方案),所以我只是想从不同的角度解决它。
【解决方案5】:

正如我在关于 Diego 的回答的 cmets 中所说的那样,我认为最没有创意的方法在这里是最好的:只需编写一个循环,列举所有案例,然后通过它。作为奖励,这种方法也适用于任意序列,可能是惰性的,因为我们不需要依赖任何特定于向量的技巧。

(defn lexicographic-compare
  ([xs ys]
     (lexicographic-compare compare xs ys))
  ([compare xs ys]
     (loop [xs (seq xs) ys (seq ys)]
       (if xs
         (if ys
           (let [c (compare (first xs) (first ys))]
             (if (not (zero? c))
               c
               (recur (next xs), (next ys))))
           1)
         (if ys
           -1
           0)))))

【讨论】:

    【解决方案6】:

    可能是这样的?

    (defn compare-like-strings [a b]
      (let [res (first (drop-while zero? (map compare a b)))]
        (if (nil? res)
          0
          res)))
    

    我们的想法是进行成对比较,返回 -1、0 或 1 的序列,然后删除所有前导 0。第一个非零元素是第一个不同的元素。

    【讨论】:

    • 不要忘记所有元素都相同的情况——它应该产生 0。
    • 另外,如果我们比较[1 1 2 2][1 1]会怎样?
    • 对,刚刚注意到并修复了两者相等的情况。我猜[1 1 2 2] vs [1 1] 的情况取决于您的要求?我猜是阿列克谢的电话。不错的代码高尔夫,但欢迎改进长度:)。
    • [1 1] 应小于 [1 1 2 2]
    猜你喜欢
    • 2011-06-20
    • 1970-01-01
    • 2018-10-13
    • 1970-01-01
    • 1970-01-01
    • 2014-12-16
    • 2015-08-30
    • 1970-01-01
    • 2017-09-06
    相关资源
    最近更新 更多