clojure是否具有C#当量的收益率?

clojure是否具有C#当量的收益率?,clojure,Clojure,我在阅读电子书时发现了一个导致这个问题的代码示例 这段代码旨在比较两个Person对象。比较算法是-首先比较它们的FName,如果相等,则比较它们的LName,如果相等,则比较它们的MNAME 书中给出的Clojure代码(或多或少) 问题是,根据这个示例,它将执行所有三个比较,即使第一个返回非零。在这种情况下,这不是一个问题。然而,如果我编写了惯用的C代码,那么该代码将只进行一次比较并退出。示例C#代码 这不是我所期望的。我在某处读到clojure出于性能原因或其他原因一次处理32个序列元素。

我在阅读电子书时发现了一个导致这个问题的代码示例

这段代码旨在比较两个Person对象。比较算法是-首先比较它们的FName,如果相等,则比较它们的LName,如果相等,则比较它们的MNAME

书中给出的Clojure代码(或多或少)

问题是,根据这个示例,它将执行所有三个比较,即使第一个返回非零。在这种情况下,这不是一个问题。然而,如果我编写了惯用的C代码,那么该代码将只进行一次比较并退出。示例C#代码

这不是我所期望的。我在某处读到clojure出于性能原因或其他原因一次处理32个序列元素。获得类似于C代码的输出/行为的惯用Clojure方法是什么

以下是我的尝试。然而,它感觉不到“clojurey”

编辑

根据对这个问题的回答以及我的回答,我认为如果我需要提前退出,我应该明确说明这一点。一种方法是将集合转换为惰性集合。另一个选项是使用
reduced
提前退出reduce循环

根据我目前掌握的知识,我更倾向于选择显式惰性收集路径。使用以下功能进行此操作是否存在问题-

(defn lazy-coll [coll]
  (lazy-seq 
    (when-let [s (seq coll)]
      (cons (first s) (lazy-coll (rest s))))))

通过这种方式,我可以使用
映射
删除
我通常使用的方式

我对您的代码进行了一些测试,结果是:

((compose-comparators fname-compare lname-compare mname-compare) person1 person2)
按预期工作,仅比较
fname

根据研究,Clojure的懒惰可能不像我们想象的那样严格:

Clojure作为一种语言在默认情况下并不是完全懒惰的(不像 因此,懒惰可能与严格的评估相混淆 导致意外和未优化的后果


我想你遇到了分块的序列。我不是这方面的专家,但我的理解是,根据您拥有的序列类型,clojure可能会在32个元素的块中对其进行评估,而不是完全懒惰

e、 g.您的第一个代码(未按预期工作)是:

;; I renamed your compare fns to c1, c2, c3

(->> [c1 c2 c3] ; vector; will be chunked
     (map #(% person1 person2)) 
     (filter #(not (zero? %))) 
     first)

comparing fname
comparing lname
comparing mname
14

(->> (list c1 c2 c3) ; list i.e. (cons c1 (cons c2 (cons c3 nil))) 
     (map #(% person1 person2)) 
     (filter #(not (zero? %))) 
     first)

;comparing fname
;14
鉴于这种不幸的行为,你可能想尝试另一种方法。这个怎么样:

(修正版基于Amith George在下面的评论)


事实上,我们确实有一些接近于
收益率的东西。它被称为
reduced

lazy seq
避免计算结果是偶然的,没有严格保证不评估未使用的结果。这有助于提高性能(一次计算
惰性seq
的结果块通常比一次计算一个更快)

这里我们想要的不是结果的惰性,而是在发现特定结果时短路,这就是
reduced
的设计目的

(defn compare-by-key
  [k]
  (fn [p1 p2]
    (println "Comparing" (name k))
    (compare (k p1) (k p2))))

(def fname-compare (compare-by-key :fname))

(def lname-compare (compare-by-key :lname))

(def mname-compare (compare-by-key :mname))

(defn make-composed-comparison [& comparisons] 
  (fn [p1 p2]
    (or (reduce (fn [_ comparison]
                  (let [compared (comparison p1 p2)]
                    (when-not (zero? compared)
                      (reduced compared))))
                false
                comparisons)
        0)))

(def people-comparison-1 
  (make-composed-comparison fname-compare lname-compare mname-compare))
我还尝试在这里使一些东西更地道,希望您的原始代码仍然可以识别

user> (people-comparison-1 person1 person2)
Comparing fname
14

正如您自己所怀疑的,正如其他答案所指出的,问题在于分块序列并不像可能的那样懒惰

如果我们看一下您的
组合比较器
函数(稍微简化)

。。。这三个比较都在
people-comparison-2
中运行的原因是
map
以块的形式处理块序列,如您所见

一个简单的解决方案是用删除的大块度替换
贴图

(defn lazy-map [f coll]
  (lazy-seq
    (when-let [s (seq coll)]
      (cons (f (first s)) (lazy-map f (rest s))))))

顺便说一下,您可以抽象比较器函数的构造。如果我们定义

(defn comparer [f]
  (fn [x y]
    (println "Comparing with " f)
    (compare (f x) (f y))))
。。。我们可以用它来定义

(def people-comparision-2 
 (apply compose-comparators (map comparer [:fname :lname :mname])))

第二种是正确的,因为它是急切的。问题的作者只是觉得它不是很“clojury”。@coredump,很有趣。你发布的内容和使用中间值有什么区别?像这样-
(def people-comparision-2(compose comparators fname compare lname compare mname compare))(people-comparision-2 person1 person2)
。。。为什么后者执行所有三个功能,而前者只执行第一个功能?@AmithGeorge我不知道。我快速查看了clojure的实现,但没有发现任何相关的内容(我应该花更多的时间)。不知何故,
fn
表单的求值行为不同,无论是立即使用还是与
def
@coredump一起存储。哇,这对我来说是一个新的。当您将结果分配给var(即使用
def
)时,似乎为变量arity arg
比较器选择的类型不同于仅评估变量arity arg(即,如果不使用变量arity,则更容易预测)我没有深入研究编译器,而是问了这个问题:)我不得不将
some
示例重写为-
(some#(让[result(%person1 person2)](if(=result 0)false result))[c1 c2 c3])
。如果名字相同,且不比较姓氏/中间名,则前者将打印0。不管怎样,为什么这样做有效?我从你的帖子中得到的印象是,
向量
是分块的,
列表
不是。那么为什么使用
向量的
some
示例有效呢?如果你查看源代码中的some(),你可以看到它一次调用一个函数。给定函数序列中的值是一次实现一项还是一次实现32项并不重要,因为在调用其中一项之前不会发生任何事情。我想我在这里证明了我缺乏全面的理解:)似乎我需要重新阅读Clojure的基础知识。这与C#不同。根据基础集合实现或迭代集合的函数实现,输出可能会有所不同。还有一个问题是为什么@coredump的答案与我的代码行为不同。你的帖子回答了我的问题。虽然我想把这个问题留几个月
(defn compare-by-key
  [k]
  (fn [p1 p2]
    (println "Comparing" (name k))
    (compare (k p1) (k p2))))

(def fname-compare (compare-by-key :fname))

(def lname-compare (compare-by-key :lname))

(def mname-compare (compare-by-key :mname))

(defn make-composed-comparison [& comparisons] 
  (fn [p1 p2]
    (or (reduce (fn [_ comparison]
                  (let [compared (comparison p1 p2)]
                    (when-not (zero? compared)
                      (reduced compared))))
                false
                comparisons)
        0)))

(def people-comparison-1 
  (make-composed-comparison fname-compare lname-compare mname-compare))
user> (people-comparison-1 person1 person2)
Comparing fname
14
(defn compose-comparators [& comparators]
  (fn [x y]
    (let [result (->> comparators
                      (map #(% x y))
                      (remove zero?)
                      first)]
      (if (nil? result) 0 result))))
(defn lazy-map [f coll]
  (lazy-seq
    (when-let [s (seq coll)]
      (cons (f (first s)) (lazy-map f (rest s))))))
(defn comparer [f]
  (fn [x y]
    (println "Comparing with " f)
    (compare (f x) (f y))))
(def people-comparision-2 
 (apply compose-comparators (map comparer [:fname :lname :mname])))