借助Java 8實(shí)現(xiàn)柯里化

在計(jì)算機(jī)科學(xué)中,柯里化(英語(yǔ):Currying),又譯為卡瑞化加里化,是把接受多個(gè)參數(shù)的函數(shù)變換成接受一個(gè)單一參數(shù)(最初函數(shù)的第一個(gè)參數(shù))的函數(shù),并且返回接受余下的參數(shù)而且返回結(jié)果的新函數(shù)的技術(shù)。這個(gè)技術(shù)由克里斯托弗·斯特雷奇以邏輯學(xué)家哈斯凱爾·加里命名的,盡管它是Moses Sch?nfinkel和戈特洛布·弗雷格發(fā)明的。

在函數(shù)式編程中,函數(shù)的概念跟數(shù)學(xué)中函數(shù)的概念是一樣的,類似于“映射”。高階函數(shù)和柯里化是函數(shù)式編程的特性。

對(duì)于柯里化而言,首先我們來(lái)舉個(gè)栗子,先定義這樣一個(gè)函數(shù)

f(x,y,z) = (x+y)*z

當(dāng)x是一個(gè)常量時(shí),比如x=4,可以用一個(gè)新的函數(shù)來(lái)代替

f(4,y,z)=g(y,z)=(4+y)*z

新的函數(shù)g(y,z)是由f(x,y,z)轉(zhuǎn)換而來(lái)的,它的參數(shù)是y、z是原先函數(shù)的后兩個(gè)參數(shù)。

我們?cè)僖淮螌?duì)y賦值,比如y=5,函數(shù)再次變成

f(4,5,z)=g(4,5)=(4+5)*z

我們可以理解為將原來(lái)的函數(shù)變量拆分開(kāi)來(lái)調(diào)用:

f(x,y,z) -> f(x)(y)(z)

借助Java 8實(shí)現(xiàn)柯里化

孔乙己中茴香豆的“茴”字有四種寫(xiě)法,我也給出多種方式來(lái)實(shí)現(xiàn)柯里化

  • 第一種方式,嵌套多層Function
Function<Integer, Function<Integer, Function<Integer, Integer>>> currying =  x -> y -> z -> (x+y)*z;

System.out.println(currying.apply(4).apply(5).apply(6)); //54
  • 第二種方式
IntFunction<IntFunction<IntUnaryOperator>> f = x -> y -> z -> (x + y) * z;
        
System.out.println(f.apply(4).apply(5).applyAsInt(6)); //54
  • 第三種方式,需要先定義一個(gè)TriFunction函數(shù)接口:
@FunctionalInterface
public interface TriFunction<U, T, S, R> {

    /**
     * Applies this function to the given arguments.
     * @param <U>
     * @param <T>
     * @param <S>
     * @return the function result
     */
     R apply(T t, U u, S s);
}

然后借助TriFunction來(lái)實(shí)現(xiàn)柯里化

TriFunction<Integer,Integer,Integer, Integer> triFunction = (x,y,z) -> (x+y)*z;

System.out.println(triFunction.apply(4,5,6)); //54

為啥要定義一個(gè)TriFunction呢?其實(shí)Java 8的function庫(kù)中包含了BiFunction的函數(shù)接口,但它只能傳兩個(gè)參數(shù)。然后Java的設(shè)計(jì)者們打住了,不再定義三個(gè)及以上參數(shù)的函數(shù)接口。我們可以想象要是真的定義n(n>=3)個(gè)參數(shù)的函數(shù)接口,那么這個(gè)函數(shù)需要傳遞n+1個(gè)參數(shù),其中包含一個(gè)返回的參數(shù)類型。在Rxjava1中確實(shí)存在可以定義到9個(gè)參數(shù)的Func9,真要這樣寫(xiě)起代碼來(lái)還是很痛苦的,得好好思考一下是不是設(shè)計(jì)的問(wèn)題了。

  • 第四種方式,借助匿名內(nèi)部類,每次調(diào)用都返回一個(gè)新的函數(shù)
        Function<Integer, Function<Integer, Function<Integer, Integer>>> currying = new Function<Integer, Function<Integer, Function<Integer, Integer>>>() {
            @Override
            public Function<Integer, Function<Integer, Integer>> apply(Integer x) {
                return new Function<Integer, Function<Integer, Integer>>() {
                    @Override
                    public Function<Integer, Integer> apply(Integer y) {
                        return new Function<Integer, Integer>() {
                            @Override
                            public Integer apply(Integer z) {
                                return (x + y) * z;
                            }
                        };
                    }
                };
            }
        };
        
        System.out.println(currying.apply(4).apply(5).apply(6));//54

柯里化的好處

隨著函數(shù)在Java 8中變成一等公民,自然而然會(huì)產(chǎn)生柯里化??吕锘逆?zhǔn)秸{(diào)用的確用起來(lái)很爽??吕锘部梢匝舆t加載一個(gè)函數(shù)。

除此以外,柯里化在很多時(shí)候簡(jiǎn)化了函數(shù)式編程的復(fù)雜性,使編程更加優(yōu)雅。當(dāng)然,在團(tuán)隊(duì)中使用的話,也需要充分考慮到團(tuán)隊(duì)中其他成員是否接受。

總結(jié)

Java 8雖然是OO+FP的結(jié)合,能夠支持lambda表達(dá)式、高階函數(shù)、閉包等,但是并沒(méi)有提供函數(shù)柯里化與偏函數(shù)(函數(shù)部分調(diào)用)的語(yǔ)法糖,當(dāng)然想要使用的話肯定是可以模擬出來(lái)。想要嘗試更好的FP可以玩玩Scala,Scala在這些方面確實(shí)比Java更好。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容