<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

    淺析函數(shù)式編程

    共 5962字,需瀏覽 12分鐘

     ·

    2023-05-18 05:33

    函數(shù)式編程

    1. 函數(shù)

    高中一年級,應(yīng)該是最早接觸函數(shù)這個(gè)概念的時(shí)間,印象很深刻,畢竟是高考壓軸大題,但它卻是必修一第二章的內(nèi)容。

    我們來看一個(gè)必修一中最簡單的一個(gè)函數(shù):

    上面的函數(shù)由三個(gè)部分組成:

    • x 自變量,由它決定初始值
    • f 它代表一個(gè)規(guī)則,用來對 自變量 進(jìn)行計(jì)算。它也是用來描述函數(shù)關(guān)系的。
    • y 因變量,它用來標(biāo)識通過函數(shù)計(jì)算以后的結(jié)果

    一個(gè)函數(shù)的定義是:「一個(gè)自變量 x ,經(jīng)過一個(gè)規(guī)則 f(x) 的運(yùn)算,得到一個(gè)因變量。其中的規(guī)則 f(x) 稱為函數(shù)」

    上面的函數(shù)在數(shù)學(xué)上有一個(gè)術(shù)語叫「一元函數(shù)」

    如上,它被稱為「二元函數(shù)」,自變量的多少決定了這個(gè)函數(shù)的名稱。

    函數(shù)的概念,也被引入了計(jì)算機(jī)的領(lǐng)域中。很多的語言內(nèi)置了函數(shù)的語法,幫助我們?nèi)?shí)現(xiàn)類似于數(shù)學(xué)中函數(shù)的功能。

    我們來看一個(gè)函數(shù)的定義

          
          int?main(int?x)?{
    ?return?x?+?1;?
    }

    這是 C 語言中定義一個(gè)函數(shù)的語法規(guī)則,可以看出它和數(shù)學(xué)中的函數(shù)完全一樣,它傳入了一個(gè)自變量 x 在計(jì)算機(jī)中,它叫「參數(shù)」,同時(shí)在函數(shù)內(nèi)部,它對這個(gè)參數(shù)做了加法運(yùn)算,并將其返回。此時(shí)這個(gè)加法運(yùn)算就是數(shù)學(xué)中的規(guī)則 f(x) ,返回值就是因變量 y

    類似,上面的函數(shù)叫一元函數(shù),因?yàn)樗挥幸粋€(gè)參數(shù)。

    同理,兩個(gè)參數(shù)的,就稱為二元函數(shù),以此類推。

    各種編程語言,提供了多種多樣的函數(shù)的定義方式,但其本質(zhì)和上面的函數(shù)完全一樣,只是定義方式發(fā)生了變化而已。

    • Java
          
          public?static?int?method(int?x)?{
    ??return?x?+?1;
    }
    • scala
          
          def?method(x:?Int):?Int?=?{
    ??x?+?1
    }
    • javascript
          
          function?method(x)?{
    ??return?x?+?1
    }

    2. 面向?qū)ο缶幊毯秃瘮?shù)式編程

    寫 OOP 的人都有一個(gè)體會,以類作為最小的調(diào)度單元,實(shí)現(xiàn)一個(gè)功能,需要去「定義一些數(shù)據(jù)結(jié)構(gòu)和操作這些數(shù)據(jù)結(jié)構(gòu)的方法」

    也基于此,衍生出了設(shè)計(jì)模式這個(gè)代碼復(fù)用的規(guī)則。設(shè)計(jì)模式的出現(xiàn)就是為了解決 OOP 帶來的一些弊端,一定程度上實(shí)現(xiàn)對方法級別的重用。

    函數(shù)式編程(后文以 FP 代替)講究不變性,如同一個(gè)數(shù)學(xué)函數(shù)一樣,只要你的入?yún)⑾嗤敲茨愕姆祷刂当厝幌嗤?,這樣做的好處在于,「這個(gè)函數(shù)對你的代碼沒有任何副作用,它不會更改所有的變量,只會返回一個(gè)新的變量,這也意味著它沒有線程安全的問題?!?/strong>

    了解過一些支持 FP 的同學(xué),一定在相關(guān)的書籍上看到過一句話:「函數(shù)是一等公民」,支持 FP 的語言,將函數(shù)作為一種數(shù)據(jù)類型而存在。

    而當(dāng)函數(shù)成為一種數(shù)據(jù)類型的時(shí)候,很多我們經(jīng)常使用到的設(shè)計(jì)模式也就有了其他的一些玩法。

    3. 函數(shù)式編程下的設(shè)計(jì)模式

    策略

    策略設(shè)計(jì)模式,用來解決參數(shù)相同場景下的 if|else 的問題,直接看類圖3af114404afeb98cadb235f49f953d7d.webp

    • Strategy(策略),定義所有支持的算法的公共接口。
    • ConcreteStrategy(具體策略,如 SimpleCompository , TeXCompositor)
    • Context (上下文,用來對具體的策略進(jìn)行切換)

    上面是典型的 OOP 的思路,我們來看一下 FP 下的代碼實(shí)現(xiàn)

          
          function?test01(func)?{
    ??func("HelloWorld")
    }

    function?test02(str)?{
    ??log.info(str)
    }

    test01(test02())

    模版方法

    模版方法,預(yù)留一些擴(kuò)展(方法)留給子類自己實(shí)現(xiàn),如生命周期函數(shù)。來看類圖

    e38c82f628a823c53c70ee8fadeb9784.webp

    FP函數(shù)式代碼實(shí)現(xiàn):

          
          function?test01(doCreateDocument,?aboutToOpenDocument)?{
    ??log.info("start")
    ??doCreateDoCument(str)
    ??aboutToOpenDocument(str)
    }

    test01(data?=>?{
    ??
    },?error?=>?{
    ??
    })

    可以發(fā)現(xiàn)無論是策略還是模版設(shè)計(jì)模式,都在使用函數(shù)作為數(shù)據(jù)類型,進(jìn)而代替了 OOP 中的繼承的作用。但對于一個(gè)函數(shù)而言,參數(shù)的個(gè)數(shù)問題成為了一個(gè)問題,實(shí)現(xiàn)一個(gè)功能我們可以依賴于外部的多個(gè)參數(shù),此時(shí)一味的進(jìn)行傳參,對于后續(xù)代碼維護(hù)、擴(kuò)展都有很大的影響,于此函數(shù)式編程的一個(gè)特性也隨之誕生。

    4. 柯里化

    閉包

    閉包的概念來自于前端,通俗的話來講,「閉包就是引用了一個(gè)函數(shù)內(nèi)部所有變量(包括參數(shù))的一個(gè)組合?!?/strong>

    閉包在函數(shù)創(chuàng)建的時(shí)候就會被默認(rèn)創(chuàng)建,如同類的構(gòu)造函數(shù)。

    ?

    一個(gè)問題:沒有返回值的函數(shù)存在閉包嗎?

    答:存在,沒有使用而已

    ?

    上面提到了一個(gè)多參數(shù)的問題,我們來看一段代碼

          
          function?func1(str1)?{
    ??return?func2(str2)?{
    ????return?str1?===?str2
    ??}
    }

    const?func3?=?func1("1")
    const?ans?=?func3("2")

    可以看到,func1 返回了一個(gè)函數(shù) func2 ,func2 函數(shù)訪問了 func1 函數(shù)的參數(shù)。這個(gè)的實(shí)現(xiàn)就依賴于 「閉包」。

    func3 獲取到了 func2 的一個(gè)引用。此時(shí)繼續(xù)調(diào)用 func3 ,得到 func1 最終的處理結(jié)果。

    上面將函數(shù)調(diào)用分為了兩步,如果連在一起寫就是 func1("1")("2")

    ?

    Func1(1)(2) 這樣寫的好處在哪里?

    ?
    • 將一個(gè)多元函數(shù)拆分為多個(gè)低元函數(shù),參數(shù)之間可以進(jìn)行預(yù)處理,然后進(jìn)行整合;

    • 一元函數(shù)方便復(fù)用

    這種變形調(diào)用方式,在函數(shù)式編程中存在一個(gè)術(shù)語「柯里化」。

    5. Java 中的函數(shù)式

    從 1.8 開始 Jdk 從語言層面提供了一些能力用以在 Java 領(lǐng)域書寫一些函數(shù)式編程。

    函數(shù)作為參數(shù)

    Java 1.8 以后提供了一個(gè)注解 @FunctionalInterface 。它的定義是:內(nèi)部僅僅存在一個(gè)抽象方法的接口,即可聲明為函數(shù)式接口。同時(shí)也提供了一些通用的類,來實(shí)現(xiàn)函數(shù)式編程。

    c3708578cb1b82a43dc40980ca0435c9.webp

    圖中最上面是四個(gè)基本的函數(shù)式接口,Consumer, Predicate, Supplier 三個(gè)類都是對 Function 類的一次封裝,Consumer 類沒有返回值,Predicate 返回值為 「布爾值」Supplier 類沒有參數(shù)。而 Function 類是一個(gè)標(biāo)準(zhǔn)的函數(shù)式接口,看一下它的定義。

          
          @FunctionalInterface
    public?interface?Function<T,?R>?{

    ????/**
    ?????*?Applies?this?function?to?the?given?argument.
    ?????*
    ?????*?@param?t?the?function?argument
    ?????*?@return?the?function?result
    ?????*/

    ????R?apply(T?t);
    ??
    ???default?<V>?Function<V,?R>?compose(Function<??super?V,???extends?T>?before)?{
    ????????Objects.requireNonNull(before);
    ????????return?(V?v)?->?apply(before.apply(v));
    ????}
    ???default?<V>?Function<T,?V>?andThen(Function<??super?R,???extends?V>?after)?{
    ????????Objects.requireNonNull(after);
    ????????return?(T?t)?->?after.apply(apply(t));
    ????}
    ??
    }

    可以看到,他內(nèi)部提供了一個(gè)抽象函數(shù), R apply(T t) 接受一個(gè)泛型,返回一個(gè)泛型。「這是最標(biāo)準(zhǔn)的一元函數(shù)(只有一個(gè)參數(shù))?!?/strong>

    那么問題來了,如果我需要兩個(gè)參數(shù)怎么辦,JDK,提供了一個(gè) BiFunction 可以接受兩個(gè)參數(shù),當(dāng)然只能返回一個(gè)值,如果想返回多個(gè)值,請封裝一個(gè) 集合類型。

    那么問題又來了,如果我需要三個(gè),或者更多的參數(shù)怎么辦,不可能 JDK 將所有的參數(shù)個(gè)數(shù)的函數(shù)都封裝一遍吧。

    我們來思考一下這個(gè)問題的解決方法:對于多元(多參)的函數(shù),我們能否將它們拆成一個(gè)個(gè)的一元函數(shù),然后讓這個(gè)一元函數(shù)返回一個(gè)一元函數(shù),來實(shí)現(xiàn)多參數(shù)的傳遞。這就是我們前面提到的柯里化的思想。

    來看一下具體的實(shí)現(xiàn)

          
          public?void?test()?{
    ????????System.out.println(test01(param01?->?param02?->?param03?->?param01?+?param02?+?param03));
    ????}

    ????public?String?test01(Function<String,?Function<String,?Function<String,?String>>>?function)?{
    ????????//?condition

    ????????String?method01?=?"111";
    ????????String?method02?=?"222";
    ????????String?method03?=?"333";

    ????????return?function.apply(method01).apply(method02).apply(method03);
    ????}

    通過以上的方式可以簡單的去寫一些服用代碼,比如返回值相同但是調(diào)用方方不同的第三方調(diào)用。

    以上就是對函數(shù)式編程的一個(gè)簡單的介紹,具體的其他行為需要在真正的編碼中進(jìn)行實(shí)踐。

    創(chuàng)

    l s p 內(nèi)

    態(tài) 設(shè) 計(jì)

    設(shè) 計(jì)

    設(shè) 計(jì)

    T h r e a d L o c a l ?




    瀏覽 31
    點(diǎn)贊
    評論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報(bào)
    評論
    圖片
    表情
    推薦
    點(diǎn)贊
    評論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報(bào)

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

        • <dd id="5sdj3"></dd>
          <dfn id="5sdj3"></dfn>
        • <th id="5sdj3"></th>
          <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

          • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
          • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
            亚州最大操B网站 | 影音先锋成人无码在线观看 | 亚洲sese在线 | 国产之级淫秽 | 成人影视导航 |