gpt4 book ai didi

clojure - clojure 是否具有与 C# 等价的 Yield?

转载 作者:行者123 更新时间:2023-12-02 10:40:50 24 4
gpt4 key购买 nike

我正在阅读电子书 Functional Programming Patterns in Scala & Clojure并找到了导致这个问题的代码示例。

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

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

(def person1 {:fname "John" :mname "Q" :lname "Doe"})
(def person2 {:fname "Jane" :mname "P" :lname "Doe"})

(defn fname-compare [p1 p2]
(do
(println "Comparing fname")
(compare (:fname p1) (:fname p2))))

(defn lname-compare [p1 p2]
(do
(println "Comparing lname")
(compare (:lname p1) (:lname p2))))

(defn mname-compare [p1 p2]
(do
(println "Comparing mname")
(compare (:mname p1) (:mname p2))))

(defn make-composed-comparison [& comparisons]
(fn [p1 p2]
(let [results (for [comparison comparisons] (comparison p1 p2))
first-non-zero-result
(some (fn [result] (if (not (= 0 result)) result nil)) results)]
(if (nil? first-non-zero-result)
0
first-non-zero-result))))

(def people-comparision-1
(make-composed-comparison fname-compare lname-compare mname-compare))

(people-comparision-1 person1 person2)

;Output
;Comparing fname
;Comparing lname
;Comparing mname
;14

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

public class Person {
public string FName {get; set;}
public string LName {get; set;}
public string MName {get; set;}
}

var comparators =
new List<Func<Person, Person, int>> {
(p1, p1) => {
Console.WriteLine("Comparing FName");
return string.Compare(p1.FName, p2.FName);
},
(p1, p1) => {
Console.WriteLine("Comparing LName");
return string.Compare(p1.LName, p2.LName);
},
(p1, p1) => {
Console.WriteLine("Comparing MName");
return string.Compare(p1.MName, p2.MName);
}
};

var p1 = new Person {FName = "John", MName = "Q", LName = "Doe"};
var p2 = new Person {FName = "Jane", MName = "P", LName = "Doe"};

var result =
comparators
.Select(x => x(p1, p2))
.Where(x => x != 0)
.FirstOrDefault();

Console.WriteLine(result);

// Output
// Comparing FName
// 1

将上述代码简单地翻译成 clojure 给了我

(defn compose-comparators [& comparators]
(fn [x y]
(let [result
(->> comparators
(map #(% x y))
(filter #(not (zero? %)))
first)]
(if (nil? result)
0
result))))

(def people-comparision-2
(compose-comparators fname-compare lname-compare mname-compare))

(people-comparision-2 person1 person2)

;Output
;Comparing fname
;Comparing lname
;Comparing mname
;14

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

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

(defn compose-comparators-2 [& comparators]
(fn [x y]
(loop [comparators comparators
result 0]
(if (not (zero? result))
result
(let [comparator (first comparators)]
(if (nil? comparator)
0
(recur (rest comparators) (comparator x y))))))))

(def people-comparision-3
(compose-comparators-2 fname-compare lname-compare mname-compare))

(people-comparision-3 person1 person2)

;Output
;Comparing fname
;14

编辑:

基于此问题以及 answer to a related question 的答案,我想如果我需要提前退出,我应该明确表示。一种方法是将集合转换为惰性集合。另一种选择是使用reduced 提前退出reduce 循环。

以我目前掌握的知识,我更倾向于选择显式惰性收集路线。使用以下函数执行此操作是否存在问题 -

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

这样我就可以像平常一样使用mapremove

最佳答案

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

如果我们查看您的 compose-comparators 函数(稍微简化)

(defn compose-comparators [& comparators]
(fn [x y]
(let [result (->> comparators
(map #(% x y))
(remove zero?)
first)]
(if (nil? result) 0 result))))

...所有三个比较都在 people-comparison-2 中运行的原因是 map 处理 block 中的分块序列,如您所见 here

一个简单的解决方案是用删除了 block 的 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])))

关于clojure - clojure 是否具有与 C# 等价的 Yield?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26039461/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com