clojure有C#当量的产量吗? [英] Does clojure have the C# equivalent of yield?

查看:97
本文介绍了clojure有C#当量的产量吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在阅读电子书 Scala中的函数编程模式& Clojure ,并找到了导致此问题的代码示例。

I was reading the ebook Functional Programming Patterns in Scala & Clojure and found a code sample that led to this question.

这段代码是用来比较两个Person对象。比较算法是 - 首先比较他们的FNames,如果相等,然后比较他们的LName,如果相等,然后比较他们的MNames。

This piece of code is meant to compare two Person objects. The comparision algo is - First compare their FNames, if equal then compare their LName, if equal then compare their MNames.

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

Clojure code as given in the book (more or less)

(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#代码

Thing is, as per this sample it will do all three comparisons even if the first one returned non zero. In this case its not an issue. However if I had written idiomatic C# code, then that code would have done only one comparison and exited. Sample C# code

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的简单的翻译给了我

A naive translation of the above code into clojure gives me

(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个元素的性能原因或某事。什么是习惯的Clojure方式来获得类似于C#代码的输出/行为?

And that is not what I expected. I read somewhere that clojure processes 32 elements of a sequence at a time for performance reasons or something. What is the idiomatic Clojure way to get the output/behaviour similar to the C# code?

以下是我的尝试。但它不觉得clojurey。

The following is my attempt. However it doesn't feel "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

编辑

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

Based on answers to this question as well as the answer to a related question, I think if I need early exit, I should be explicit about it. One way would be to convert the collection to a lazy one. The other option is to use reduced to exit early from the reduce loop.

根据我目前的知识,我更倾向于选择明确的延迟收集途径。使用以下函数是否存在问题 -

With the knowledge I currently have, I am more inclined to opt for the explicit lazy collection route. Is there an issue with using the following function to do so -

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

这样我可以使用 map remove 我通常会有的方式。

This way I can use map, remove the way I normally would have.

推荐答案

正如你怀疑自己和其他答案所指出的,问题是分块序列不是那么懒惰是。

As you suspected yourself and as the other answers have noted, the problem is that chunked sequences are not as lazy as they might be.

如果我们看看您的 compose-comparatorators 函数(略微简化)

If we look at your compose-comparators function (slightly simplified)

(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 处理大块中的分块序列,您可以看到此处

... the reason that all three comparisons are run in people-comparison-2 is that map deals with a chunked sequence in chunks, as you can see here.

一个简单的解决方案是用 map 替换chunkiness:

A simple solution is to substitute a map with the chunkiness removed:

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






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


By the way, you can abstract the construction of the comparator functions. If we define

(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有C#当量的产量吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆