日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

高阶函数(二)

發布時間:2025/4/16 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 高阶函数(二) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

原文鏈接:https://github.com/enbandari/Kotlin-Tutorials

上周我已經給大家推送了一篇關于高階函數的文章,這一期,我們繼續探討一些相關的有意思的話題。

1. 復合函數

大家一定見過下面的數學題吧:

求 f(g(x)) 的值。
解:設 m(x) = f(g(x)) …
?
m 就是 f 和 g 的復合。

我們在 Kotlin 當中要如何對函數進行復合呢?

val add5 = { i: Int -> i + 5 } val multiplyBy2 = { i: Int -> i * 2 }

我們定義了這么兩個函數,接著這么調用它:

println(multiplyBy2(add5(2))) // (2 + 5) * 2

add5 相當于我們的 g(x),multiplyBy2 相當于 f(x),那么上面的式子就相當于 f(g(x))。下面我們提供一個簡單的方式來復合這兩個函數,得到 m(x) = f(g(x)):

// f andThen g -> g(f(x)) infix fun <P1, P2, R> Function1<P1, P2>.andThen(function: Function1<P2, R>): Function1<P1, R> {return fun(p1: P1): R{return function.invoke(this.invoke(p1))} }

這里面有幾個知識點,我請大家一起復習一下。

第一個就是 infix,中綴表達式,有了這個關鍵字,我們的 add5 在調用 andThen 方法時,就不需要用 .andThen() 的形式了,而是像使用操作符一樣。

第二個是擴展方法,andThen 其實就是 Function1

val add5AndMultiplyBy2 = add5 andThen multiplyBy2 println(add5AndMultiplyBy2(2))

這個例子的輸出結果其實與前面的相同, ( 2 + 5 ) * 2 = 14

通過 andThen,我們看到一個全新的函數 add5AndMultiplyBy2 被創造出來,它其實就是 add5 和 multiplyBy2 的復合。

當然,有時候我們其實還需要這樣的結果:

val multiplyBy2AndAdd5 = add5 compose multiplyBy2 println(multiplyBy2AndAdd5(2))

這個相當于 2 * 2 + 5 = 9。 我們簡單看下 compose 的實現:

// f compose g -> f(g(x)) infix fun <P1, P2, R> Function1<P2, R>.compose(function: Function1<P1, P2>): Function1<P1, R> {return fun(p1: P1): R{return this.invoke(function.invoke(p1))} }

compose 與 andThen 的結果是完全相反的,f andThen g -> g(f(x)),而 f compose g -> f(g(x))。

這就是函數復合,其實你在初中數學就學過這些東西了。

2. Currying

Curry 也有咖喱的意思,不過這一節可并不是充滿咖喱味的。在函數式編程當中,Currying 也經常被翻譯成“科里化”,我們從這個名字完全讀不出它究竟是要干啥。為什么?因為,Curry 是個人名——Haskell Curry。

回到我們的程序當中,我們首先必須要搞清楚什么是 Currying?Currying 其實就是由一個多參數的函數構造出一系列只有一個參數的函數的方法。這么說可能還是有些抽象,我們直接上例子:

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

我們有一個三元函數,這個沒什么復雜的,你在高中數學當中見到過比這個恐怖的多的式子。接著我們給它做個變式:

f(x,y,z) = k_{yz}(x)

其中,(k_{yz}(x)) 是關于 (x) 的一個函數,(yz) 可當做常量看待。而一旦傳入 (x) 的值以后,例如 (k_{yz}(x_0)) ,那么此時又有變換:

f(x_0,y,z) = k_{yz}(x_0) = m_{z,x=x_0}(y)

類似的,我們還能最終變換成:

f(x_0,y_0,z) = m_{z,x=x_0}(y_0) = n_{x=x_0, y=y_0}(z)

這么一個數學概念,其實就是 Currying。那么它到底想說明怎樣一件事情呢?大家看,參數是一個一個傳進來的,這就好比我們完成一件事情,也是對其進行肢解,然后一步一步完成的,通過 Currying,我們可以對一個函數的調用細節進行仔細的考量,甚至像流水線一樣處理,以實現我們的目標。

用程序的語言描述,假設我們有一個函數:

fun hello(x: String, y: Int, z: Double): Boolean{... }

它 Currying 的結果便是:

fun curriedHello(x: String): (y: Int) ->(z: Double)-> Boolean{... }

下面我們給出一個 Kotlin 的例子:

fun log(tag: String, target: OutputStream, message: Any?){... }

這是一個日志打印的函數,第一個參數 tag 是一個日志的標識,第二個參數是日志的內容,第三個參數是日志打印的目標,這個可以是控制臺,也可以是文件,由調用者指定。

顯然,我們通常調試時,輸出日志都是直接到控制臺的,于是我們定義一個新函數:

fun consoleLog(tag: String, message: Any?) = log(tag, System.out, message)

由于我們可能針對某一個問題不斷地調試,這些日志的 tag 也是相同的,那么我們又會定義一個新函數:

val TAG = ... ... fun consoleLogWithTag(message: Any?) = log(TAG, System.out, message)

這樣看上去似乎沒什么問題,不過你有可能會想,我不過是臨時打幾行日志,真的有必要定義這么多函數?調試一段代碼還好,調試的內容多了呢,而且他們的 tag 都還不一樣,難道我要定義 consoleLogWithTag2 、consoleLogWithTag3 … 么?

顯然,如果你運用 Currying,問題就簡單的多了,只不過是定義一個局部變量嘛:

val consoleLogWithTag = (::log.curried())(TAG)(System.out)... //打印日志 consoleLogWithTag("This may be an error to call here.")

其中 log.curried() 這個方法的簽名如下:

fun <P1, P2, P3, R> Function3<P1, P2, P3, R>.curried(): (p1: P1)->(p2: P2)->(p3: P3)-> R{... }

注意,由于 log 是函數名,因此我們在獲取其對應的函數引用時需要加 ::。

好,說到這里,你可能直接去試前面的代碼,然后垂頭喪氣的告訴我,說我這代碼是騙人的,根本不能跑。為啥呢?因為根本沒有 curried() 這個方法啊。

對啊,非常遺憾,截止到 1.1RC 版,我們也沒有看到這樣的 API 出現在標準庫當中,所以我們只好自己搞咯:

fun <P1, P2, P3, R> Function3<P1, P2, P3, R>.curried()= fun(p1: P1) = fun(p2: P2) = fun(p3: P3) = this(p1, p2, p3)

當然,這只是 Function3

3. 偏函數

我們再來看一下上一節這個打日志的例子,對于有三個參數的 log 函數,我們在極大多數的使用場景下都對前兩個參數傳入了相同的值:

fun log(tag: String, target: OutputStream, message: Any?){... }...val consoleLogWithTag = (::log.curried())(TAG)(System.out)

其實,對一個多參數的函數,通過指定其中的一部分參數后得到的仍然是一個函數,那么這個函數就是原函數的一個偏函數了。從這個意義上來講,consoleLogWithTag 也可以認為是 log 的一個偏函數。

顯然,偏函數與 Currying 有一些內在的聯系,如果我們需要構造的偏函數的參數恰好處于原函數參數的最前面,那么我們是可以使用 Currying 的方法獲得這一偏函數的;當然,如果我們希望得到任意位置的參數被指定后的偏函數,那么我們就有足夠的理由使用一些更好的方法。

例如:

val makeString = fun(byteArray: ByteArray, charset: Charset): String{return String(byteArray, charset) }...val makeStringFromGbkBytes = makeString.partial2(charset("GBK")) //實際當中這個字節流可以是文件流,也可以是網絡數據等等 val gbkByteArray = ... println(makeStringFromGbkBytes(gbkByteArray))

對于第二個參數 Charset,我們在國內有不少公司仍在用 GBK 編碼,那么在開發的過程中,我們就沒有必要每次都指定 GBK 這個編碼選項了,下面這一句代碼返回了一個 makeString 的偏函數,這個函數第二個參數確定為 charset(“GBK”)。

val makeStringFromGbkBytes = makeString.partial2(charset("GBK"))

接下來,同樣我們需要給出 partial2 的實現:

fun <P1, P2, R> Function2<P1, P2, R>.partial1(p1: P1) = fun(p2: P2) = this(p1, p2) fun <P1, P2, R> Function2<P1, P2, R>.partial2(p2: P2) = fun(p1: P1) = this(p1, p2)

我們看到,我們為 Function2 實現了兩個擴展方法 partial1 和 partial2,這兩個方法分別用來生成兩個參數分別被指定后的偏函數。

目前 Kotlin 標準庫尚且沒有對此提供支持,如果需要得到 FunctionN (N > 1) 的偏函數,那么我們需要把他們對應的 partialN 依次實現。

需要注意的是,makeString 是一個函數引用,可以直接用于調用函數的方法,這與上一節當中的 ::log 本質上是一樣的,只是二者的定義方式不同,希望大家不要感到困惑。

// log 是函數名 fun log(tag: String, target: OutputStream, message: Any?){... } ... val consoleLogWithTag = (::log.curried())(TAG)(System.out)

4. 小結

本文主要給大家介紹了如何基于 Kotlin 的現有標準庫來實現一些函數式編程的特性,其實這些特性已經在 Github 的 funKTionale 當中給出,本文的內容也更多的是在向它致敬。

《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的高阶函数(二)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 日本人与黑人做爰视频 | 国产男男gay体育生白袜 | 久久毛片基地 | 精品久久二区 | 一区二区三区四区国产 | 日韩精品一区二区视频 | 日本黄色免费在线观看 | 日韩av电影手机在线观看 | 噜噜噜精品欧美成人 | 打屁股无遮挡网站 | 天天看av| 久久久久久国产精品无码 | 亚洲av成人精品一区二区三区 | 毛片网站网址 | 国产在线网 | www视频在线观看网站 | 无码av天堂一区二区三区 | 亚洲综合久久婷婷 | 亚洲成av人片一区二区 | 牛牛影视一区二区三区 | 久久中文免费视频 | 精品欧美乱码久久久久久1区2区 | 求个黄色网址 | 欧美高清在线观看 | 69久人妻无码精品一区 | 成人激情av | 一区二区三区国产精品 | 五月天综合激情网 | 精品人妻aV中文字幕乱码色欲 | 国产黄a三级三级三级看三级男男 | 极品久久久久久 | 黄视频免费观看 | 尤物综合网 | 国产91在线 | 亚洲 | www.操| 日本一本视频 | 国产喷水吹潮视频www | 樱花草涩涩www在线播放 | 亚洲av无码乱码在线观看性色 | 色偷偷资源网 | 男生插女生视频在线观看 | jizz日本在线观看 | 99国产精品久久久 | 国产a∨精品一区二区三区仙踪林 | 天堂中文视频在线 | 欧美性猛交69 | 中文在线资源天堂 | 国内视频一区二区 | 大黑人交xxx极品hd | 最新视频 - x88av| 伊人久久国产精品 | 最新日韩视频 | 免费a视频 | 久久刺激| 亚洲自拍第三页 | 亚洲av无码国产精品永久一区 | 女性裸体无遮挡胸 | 日韩欧美中出 | 国产成人免费在线视频 | 日韩无码精品一区二区 | 国产精品久久久一区 | 超碰网址 | 国产免费无码XXXXX视频 | 久久91亚洲人成电影网站 | av免播放器在线观看 | 日韩福利片 | 美女诱惑一区二区 | 亚洲精品a| 天堂视频在线观看免费 | 日本在线观看免费 | 少妇人妻一级a毛片 | 欧洲视频在线观看 | 五月激情婷婷综合 | 日韩精品电影一区 | 午夜精品一区二区三区在线播放 | 日本在线视频www | 丝袜一级片| 热久久精 | 韩日午夜在线资源一区二区 | 欧美性猛交乱大交3 | 国产绳艺sm调教室论坛 | 狠狠操一区| 午夜少妇av | 成人黄色激情网 | 色免费看 | 免费在线一区二区三区 | 综合国产精品 | 久久这里都是精品 | 麻豆国产精品 | 亚洲欧美日韩在线播放 | 亚洲国产成人精品视频 | 欧美自拍一区 | 久久婷婷色综合 | 久久精品观看 | 岛国片免费在线观看 | www.成人网.com | 极品少妇一区二区三区 | 国产伦理在线 | 免费一级黄色片 |