有没有办法比较lambdas? [英] Is there a way to compare lambdas?

查看:88
本文介绍了有没有办法比较lambdas?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个使用lambda表达式(闭包)定义的对象列表。有没有办法检查它们以便对它们进行比较?

Say I have a List of object which were defined using lambda expressions (closures). Is there a way to inspect them so they can be compared?

我最感兴趣的代码是

    List<Strategy> strategies = getStrategies();
    Strategy a = (Strategy) this::a;
    if (strategies.contains(a)) { // ...

完整代码是

import java.util.Arrays;
import java.util.List;

public class ClosureEqualsMain {
    interface Strategy {
        void invoke(/*args*/);
        default boolean equals(Object o) { // doesn't compile
            return Closures.equals(this, o);
        }
    }

    public void a() { }
    public void b() { }
    public void c() { }

    public List<Strategy> getStrategies() {
        return Arrays.asList(this::a, this::b, this::c);
    }

    private void testStrategies() {
        List<Strategy> strategies = getStrategies();
        System.out.println(strategies);
        Strategy a = (Strategy) this::a;
        // prints false
        System.out.println("strategies.contains(this::a) is " + strategies.contains(a));
    }

    public static void main(String... ignored) {
        new ClosureEqualsMain().testStrategies();
    }

    enum Closures {;
        public static <Closure> boolean equals(Closure c1, Closure c2) {
            // This doesn't compare the contents 
            // like others immutables e.g. String
            return c1.equals(c2);
        }

        public static <Closure> int hashCode(Closure c) {
            return // a hashCode which can detect duplicates for a Set<Strategy>
        }

        public static <Closure> String asString(Closure c) {
            return // something better than Object.toString();
        }
    }    

    public String toString() {
        return "my-ClosureEqualsMain";
    }
}

看来唯一的解决办法是定义每个lambda作为一个字段,只使用那些字段。如果要打印出调用的方法,最好使用方法。使用lambda表达式有更好的方法吗?

It would appear the only solution is to define each lambda as a field and only use those fields. If you want to print out the method called, you are better off using Method. Is there a better way with lambda expressions?

此外,是否可以打印lambda并获得人类可读的内容?如果你打印这个:: a 而不是

Also, is it possible to print a lambda and get something human readable? If you print this::a instead of

ClosureEqualsMain$$Lambda$1/821270929@3f99bd52

得到类似的东西

ClosureEqualsMain.a()

甚至使用 this.toString 和方法。

my-ClosureEqualsMain.a();


推荐答案

这个问题可以解释为相关规范或实现。显然,实现可能会改变,但你可能愿意在发生这种情况时重写你的代码,所以我会在两者都回答。

This question could be interpreted relative to the specification or the implementation. Obviously, implementations could change, but you might be willing to rewrite your code when that happens, so I'll answer at both.

这也取决于你想要什么做。您是在寻求优化,还是在寻找两个实例(或不是)相同功能的铁定保证? (如果是后者,你会发现自己与计算物理学不一致,因为即使是问两个函数是否计算相同的东西这样简单的问题也是不可判定的。)

It also depends on what you want to do. Are you looking to optimize, or are you looking for ironclad guarantees that two instances are (or are not) the same function? (If the latter, you're going to find yourself at odds with computational physics, in that even problems as simple as asking whether two functions compute the same thing are undecidable.)

从规范的角度来看,语言规范只承诺评估(不调用)lambda表达式的结果是实现目标功能接口的类的实例。它没有对结果的身份或别名程度做出任何承诺。这是设计,为实现提供最大的灵活性以提供更好的性能(这就是lambdas如何比内部类更快;我们不依赖于内部类的必须创建唯一实例约束。)

From a specification perspective, the language spec promises only that the result of evaluating (not invoking) a lambda expression is an instance of a class implementing the target functional interface. It makes no promises about the identity, or degree of aliasing, of the result. This is by design, to give implementations maximal flexibility to offer better performance (this is how lambdas can be faster than inner classes; we're not tied to the "must create unique instance" constraint that inner classes are.)

所以基本上,规范并没有给你太多,除了显然两个引用相等的lambda(==)将计算相同的函数。

So basically, the spec doesn't give you much, except obviously that two lambdas that are reference-equal (==) are going to compute the same function.

从实施的角度来看,你可以得出更多结论。实现lambdas的合成类与程序中的捕获站点之间存在(当前可能会发生变化)1:1的关系。因此,捕获x - > x + 1的两个独立代码位可以很好地映射到不同的类。但是,如果您在同一个捕获站点评估相同的lambda,并且该lambda是非捕获的,那么您将获得相同的实例,可以将其与引用相等性进行比较。

From an implementation perspective, you can conclude a little more. There is (currently, may change) a 1:1 relationship between the synthetic classes that implement lambdas, and the capture sites in the program. So two separate bits of code that capture "x -> x + 1" may well be mapped to different classes. But if you evaluate the same lambda at the same capture site, and that lambda is non-capturing, you get the same instance, which can be compared with reference equality.

如果你的lambdas是可序列化的,他们会更容易放弃他们的状态,以换取牺牲一些性能和安全性(没有免费的午餐。)

If your lambdas are serializable, they'll give up their state more easily, in exchange for sacrificing some performance and security (no free lunch.)

调整相等定义可能实用的一个方面是使用方法引用,因为这样可以将它们用作侦听器并正确地取消注册。这是在考虑中。

One area where it might be practical to tweak the definition of equality is with method references, because this would enable them to be used as listeners and be properly unregistered. This is under consideration.

我认为你要做的是:如果两个lambda被转换为相同的功能接口,则由相同的行为函数表示,并且具有相同的捕获args,它们是相同的

I think what you're trying to get to is: if two lambdas are converted to the same functional interface, are represented by the same behavior function, and have identical captured args, they're the same

不幸的是,这很难做到(对于不可序列化的lambda,你不能得到它的所有组件)而不是够了(因为两个单独编译的文件可以将相同的lambda转换为相同的功能接口类型,你无法分辨。)

Unfortunately this is both hard to do (for non-serializable lambdas, you can't get at all the components of that) and not enough (because two separately compiled files could convert the same lambda to the same functional interface type, and you wouldn't be able to tell.)

EG讨论是否要暴露足够的信息以便能够做出这些判断,以及讨论lambdas是否应该实现更具选择性的equals / hashCode或更具描述性的toString。结论是,我们不愿意支付任何性能成本,以便向呼叫者提供这些信息(糟糕的权衡,惩罚99.99%的用户获得的利益为.01%)。

The EG discussed whether to expose enough information to be able to make these judgments, as well as discussing whether lambdas should implement more selective equals/hashCode or more descriptive toString. The conclusion was that we were not willing to pay anything in performance cost to make this information available to the caller (bad tradeoff, punishing 99.99% of users for something that benefits .01%).

未达到对toString的明确结论,但未来将重新审视。但是,双方在这个问题上都提出了一些很好的论点;这不是一个扣篮。

A definitive conclusion on toString was not reached, but left open to be revisited in the future. However, there were some good arguments made on both sides on this issue; this is not a slam-dunk.

这篇关于有没有办法比较lambdas?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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