Clojure 序列的最大值,其值由键函数确定,其中键返回向量

Clojure 序列的最大值,其值由键函数确定,其中键返回向量,clojure,Clojure,继续使用Udacity基于python的CS212并翻译成Clojure的过程,我正在尝试编写一个函数: 返回序列中的所有项 等于序列的最大值 使用返回向量的“键”函数 Clojure's几乎做到了这一点,除了它需要我的键返回一个整数,在本例中,我不返回一个向量。经过一些修改,我可以使用compare函数来比较键控函数返回的向量: (defn all-max-key "Returns a vector of x for which (k x), arbitrated by compare,

继续使用Udacity基于python的CS212并翻译成Clojure的过程,我正在尝试编写一个函数:

  • 返回序列中的所有项
  • 等于序列的最大值
  • 使用返回向量的“键”函数
  • Clojure's几乎做到了这一点,除了它需要我的键返回一个整数,在本例中,我不返回一个向量。经过一些修改,我可以使用compare函数来比较键控函数返回的向量:

    (defn all-max-key
      "Returns a vector of x for which (k x), arbitrated by compare, are greatest."
      ([k x] x)
      ([k x y] (if (= 1 (compare (k x) (k y))) x y))
      ([k x y & more]
         (reduce #(all-max-key k %1 %2) (all-max-key k x y) more)))
    
    现在的问题是,这并没有考虑到关系(其中比较返回0)。换句话说,我的列表中的所有元素都是唯一的,但有些元素的每个键控函数的值可能相等。在命令式世界中,我可能会在列表中循环,跟踪我的最大值,将每个元素与之进行比较,然后在执行时附加/替换一个可变列表


    但我觉得必须有一种惯用的、优雅的、功能性的方法来实现这一点,而不必求助于循环。我使用reduce的尝试都导致了将序列与成员元素进行无意义的比较。有人能照亮它吗?

    这将返回最大项的向量:

    (defn maximal-key [k x & xs]
      (reduce (fn [ys x]
                (let [c (compare (k x) (k (peek ys)))]
                  (cond
                    (pos? c) [x]
                    (neg? c) ys
                    :else    (conj ys x))))
              [x]
              xs))
    
    在REPL进行测试:

    ;; convoluted key function to demonstrate that vector keys are fine
    user> (maximal-key #(vector (first %))
                        (list 1 2 3) (list 4 5 6) (list 3 6 8)
                        (list 4 7 9) (list 1 5 9))
    [(4 5 6) (4 7 9)]
    

    请注意,
    compare
    可能返回一个任意的正整数,以指示其第一个参数大于第二个参数——因此,
    pos?
    取代了
    ==1

    这正是我所希望的答案。我不确定我是否仍然完全理解它——特别是[YSX]是如何被分解的,但我认为这将需要我做一些面条。peek对我来说也是新事物。感谢您减少了处理集合的代码。我看到的很多例子都是琐碎的(reduce+[1,2,3]),很高兴听到这些!这里实际上不涉及解构,
    [ysx]
    是归约函数的参数向量。在缩减的每一步中,
    ys
    参数将保存累加器的当前值,累加器是迄今为止处理的输入片段中最大的所有项的向量,
    x
    将是当前正在处理的项。如果当前元素大于先前最大元素的最后一个(因此是所有),则使用它初始化新累加器;如果比较相等,则将其添加到累加器中;否则它就会被丢弃,这就更有意义了。谢谢你的解释!