这是一个有效的例子,在java中currying

8wtpewkr  于 2021-06-29  发布在  Java
关注(0)|答案(2)|浏览(425)

我试图理解java中的currying,并创建了以下简单的示例。这是一个有效的讨好的例子还是我错过了重点?

// Before
    House constructHouse1(Foundation f, Building b) {
        return new House(f, b);
    }

    // After
    House constructHouse2(Foundation f, Building b) {
        return constructHouseWithBuilding(b).apply(f);
    }

    Function<Foundation, House> constructHouseWithBuilding(Building b) {
        return constructHouseWithFoundation(b);
    }

    Function<Foundation, House> constructHouseWithFoundation(Building b) {
        return foundation -> new House(foundation, b);
    }

    // Beans
    class Foundation { }
    class Building { }
    class House {
        Foundation f;
        Building b;
        House(Foundation f, Building b) {
            this.f = f;
            this.b = b;
        }
    }
cetgtptt

cetgtptt1#

有点。
通过curried函数,我们理解了一个函数,它不接受多个参数,而是接受一个参数并返回另一个函数,直到返回所有参数。所以不是:

BiFunction<Foundation, Building, House> constructHouse = ...;

你会的

Function<Foundation, Function<Building, House>> curriedConstructHouse = ...;

我们在这里讨论的是概念,而不是特定的实现,所以我们可以认为这些是手动实现的。

House constructHouse(Foundation f, Building b) {
    return new House(f, b);
}

Function<Foundation, House> constructHouseWithBuilding(Building b) {
    return f -> constructHouse(f, b);
}

因为类型很重要 Foundation -> Building -> House 不等于 Building -> Foundation -> House . 如果我们说的是curry,它们是两个函数的curry变体,具有不同的签名: (Foundation, Building) -> House 以及 (Building, Foundation) -> House .
如果你想对一个函数应用几个参数,而这个函数有更多你想应用的参数(不管它们在哪里),我们通常会讨论部分应用。看到currying通常是如何被理解的,你可以把它看作是部分应用的一个特例,你总是部分地应用剩余参数列表中的第一个参数,所有剩余的参数我们将一次部分地应用一个。
因此,我认为这是部分应用:

House constructHouse(Foundation f, Building b) {
    return new House(f, b);
}

Function<Building, House> constructHouseWithFoundation(Foundation f) {
    return b -> constructHouse(f, b);
}

也许更多的例子可以澄清这些区别(假设VAV或某个函数有两个以上的参数,但这段代码是伪代码):

A a; B c; C c; // arguments to pass
               // a1, b2, c1 will be used as arguments in lambdas to avoid confusion

// uncurried
// (A, B, C) -> D
Function3<A, B, C , D> f;
f.apply(a, b, c); // returns D

// curried
// A -> B -> C -> D
Function1<A, Function1<B, Function1<C, D>>> curried = f.curried(); // curried() is available in Vavr's functions
curried.apply(a).apply(b).apply(c); // returns D

// partially applied B
// (A, C) -> D
Function2<A, C, D> partially1 = (a1, c1) -> f(a1, b, c1); // there is no syntactic sugar for this in Java that I am aware of

// partially applied A and C
// B -> D
Function1<B, D> partially2 = (b1) -> f(a, b1, c); 

// neither currying nor partial application
Function3<C, A, B, D> reordered = (c1, a1, b1) -> f(a1, b1, c1);

所以我可以考虑你的 constructHouseWithFoundation 将成为curried函数,以及 constructHouse2 未结婚的版本 constructHouseWithFoundation .

pbpqsu0x

pbpqsu0x2#

currying指的是将一个包含多个参数的函数转换为一个包含单个参数的函数,该函数返回另一个函数,该函数接受其余的参数。对于两个参数函数,可以用java实现,如下所示:

static <T, U, R> Function<T, Function<U, R>> curry(BiFunction<T, U, R> f) {
    return t -> u -> f.apply(t, u);
}

用这个

Objects.equals("one", "two");

相当于

curry(Object::equals)
    .apply("one")
    .apply("two");

另一种看待咖喱的方法是如下所示,咖喱对java来说更为惯用:
货币交换函数是采取 rate 和一个 amount 参数并返回交换:

static double change(double rate, double amount) { 
    return rate * amount; 
}

要使用相同的汇率转换多个金额,可以将汇率封装在 Exchange 班级:

class Exchange {
    final double rate;
    Exchange(double rate) {this.rate = rate;}

    double change(double amount) { 
        return rate * amount; 
    }
}

这是非常讨好:的构造器 Exchange 接受单个参数并返回 Exchange ,它本质上是一个单参数的函数 amount . 为了使这一点更加明显,请注意这两个函数是如何起作用的 exchange1 以及 exchange2 相互等价:

Function<Double, Double> exchange1 = new Exchange(1.3)::change;
Function<Double, Double> exchange2 = curry(Outer::change).apply(1.3);

相关问题