第5章 函數與函數式編程

第5章 函數與函數式編程

凡此變數中函彼變數者,則此為彼之函數。 ( 李善蘭《代數學》)

函數式編程語言最重要的基礎是λ演算(lambda calculus),而且λ演算的函數可以傳入函數參數,也可以返回一個函數。函數式編程 (簡稱FP) 是一種編程范式(programming paradigm)。

函數式編程與命令式編程最大的不同是:函數式編程的焦點在數據的映射,命令式編程(imperative programming)的焦點是解決問題的步驟。函數式編程不僅僅指的是Lisp、Haskell、 Scala等之類的語言,更重要的是一種編程思維,解決問題的思考方式,也稱面向函數編程。

函數式編程的本質是函數的組合。例如,我們想要過濾出一個List中的奇數,用Kotlin代碼可以這樣寫

package com.easy.kotlinfun main(args: Array<String>) {val list = listOf(1, 2, 3, 4, 5, 6, 7)println(list.filter { it % 2 == 1 }) // lambda表達式
}

這個映射的過程可以使用下面的圖來形象化地說明

filter 函數

而同樣的邏輯我們使用命令式的思維方式來寫的話,代碼如下

package com.easy.kotlin;import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;import static java.lang.System.out;public class FilterOddsDemo {public static void main(String[] args) {List<Integer> list = Arrays.asList(new Integer[] {1, 2, 3, 4, 5, 6, 7});out.println(filterOdds(list)); // 輸出:[1, 3, 5, 7]}public static List<Integer> filterOdds(List<Integer> list) {List<Integer> result = new ArrayList();for (Integer i : list) {if (isOdd(i)) {result.add(i);}}return result;}private static boolean isOdd(Integer i) {return i % 2 != 0;}
}

我們可以看出,函數式編程是簡單自然、直觀易懂且美麗優雅的編程風格。函數式編程語言中通常都會提供常用的map、reduce、filter等基本函數,這些函數是對List、Map集合等基本數據結構的常用操作的高層次封裝,就像一個更加智能好用的工具箱。

5.1 函數式編程簡介

函數式編程是關于不變性和函數組合的編程范式。函數式編程有如下特征

  • 一等函數支持(first-class function):函數也是一種數據類型,可以當做參數傳入另一個函數,同時一個函數也可以返回函數。
  • 純函數(pure function)和不變性(immutable):純函數指的是沒有副作用的函數(函數不去改變外部的數據狀態)。例如,一個編譯器就是一個廣義上的純函數。在函數式編程中,傾向于使用純函數編程。正因為純函數不會去修改數據,同時又使用不可變數據,所以程序不會去修改一個已經存在的數據結構,而是根據一定的映射邏輯創建一份新的數據。函數式編程是去轉換數據而非修改原始數據。
  • 函數的組合(compose function):在面向對象編程中,是通過對象之間發送消息來構建程序邏輯;而在函數式編程中,是通過不同函數的組合構建程序邏輯。

5.2 聲明函數

Kotlin中使用 fun 關鍵字來聲明函數,其語法實例如下圖所示

Kotlin 聲明函數

為了更加直觀的感受到函數也可以當做變量來使用,我們聲明一個函數類型的變量 sum 如下

>>> val sum = fun(x:Int, y:Int):Int { return x + y }
>>> sum
(kotlin.Int, kotlin.Int) -> kotlin.Int

我們可以看到這個函數變量 sum 的類型是

(kotlin.Int, kotlin.Int) -> kotlin.Int

這個帶箭頭( -> )的表達式就是一個函數類型,表示一個輸入兩個Int類型值,輸出一個Int類型值的函數。我們可以直接使用這個函數字面值 sum

>>> sum(1,1)
2

從上面的這個典型的例子我們可以看出,Kotlin也是一門面向表達式的語言。既然 sum 是一個代表函數類型的變量,稍后我們將看到一個函數可以當做參數傳入另一個函數中(高階函數)。

當然,我們仍然可以像C/C++/Java中一樣,直接帶上函數名來聲明一個函數

fun multiply(x: Int, y: Int): Int {return x * y
}multiply(2, 2) // 4

5.3 lambda表達式

我們在本章開頭部分講到了這段代碼

val list = listOf(1, 2, 3, 4, 5, 6, 7)
list.filter { it % 2 == 1 }

這里的filter函數的入參 { it % 2 == 1 } 就是一段 lambda表達式。實際上,因為filter函數只有一個參數,所有括號被省略了。所以,filter函數調用的完整寫法是

list.filter ({ it % 2 == 1 })

其中的filter函數聲明如下

public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T>

其實,filter函數的入參是一個函數 predicate: (T) -> Boolean 。 實際上,

{ it % 2 == 1 } 

是一種簡寫的語法,完整的lambda表達式是這樣寫的

{  it -> it % 2 == 1 } 

如果拆開來寫,就更加容易理解

>>> val isOdd = { it: Int -> it % 2 == 1 } // 直接使用lambda表達式聲明一個函數,這個函數判斷輸入的Int是不是奇數
>>> isOdd
(kotlin.Int) -> kotlin.Boolean // isOdd函數的類型
>>> val list = listOf(1, 2, 3, 4, 5, 6, 7)
>>> list.filter(isOdd) // 直接傳入isOdd函數
[1, 3, 5, 7]

5.4 高階函數

其實,在上面的代碼示例 list.filter(isOdd) 中,我們已經看到了高階函數了。現在我們再添加一層映射邏輯。我們有一個字符串列表

val strList = listOf("a", "ab", "abc", "abcd", "abcde", "abcdef", "abcdefg")

然后,我們想要過濾出字符串元素的長度是奇數的列表。我們把這個問題的解決邏輯拆成兩個函數來組合實現

val f = fun (x: Int) = x % 2 == 1 // 判斷輸入的Int是否奇數
val g = fun (s: String) = s.length // 返回輸入的字符串參數的長度

我們再使用函數 h 來封裝 “字符串元素的長度是奇數” 這個邏輯,實現代碼如下

val h = fun(g:  (String) -> Int, f: (Int) -> Boolean):  (String) -> Boolean {return { f(g(it)) }
}

但是,這個 h 函數的聲明未免有點太長了。尤其是3個函數類型聲明的箭頭表達式,顯得不夠簡潔。不過不用擔心。

Kotlin中有簡單好用的 Kotlin 類型別名, 我們使用 G,F,H 來聲明3個函數類型

typealias G = (String) -> Int
typealias F = (Int) -> Boolean
typealias H = (String) -> Boolean

那么,我們的 h 函數就可簡單優雅的寫成下面這樣了

val h = fun(g: G, f: F): H {return { f(g(it)) } // 需要注意的是,這里的 {} 是不能省略的
}

這個 h 函數的映射關系可用下圖說明

h 函數的映射關系

函數體中的這句代碼 return { f(g(it)) } , 這里的 {} 它代表這是一個lambda表達式,返回的是一個 (String) -> Boolean 函數類型。如果沒有 { } , 那么返回值就是一個布爾類型Boolean了。

通過上面的代碼例子,我們可以看到,在Kotlin中,我們可以簡單優雅的實現高階函數。OK,現在邏輯已經實現完了,下面我們在 main 函數中運行測試一下效果。

fun main(args: Array<String>) {val strList = listOf("a", "ab", "abc", "abcd", "abcde", "abcdef", "abcdefg")println(strList.filter(h(g, f))) // 輸出:[a, abc, abcde, abcdefg]
}

當你看到 h(g, f) 這樣的復合函數的代碼時,你一定很開心,感到很自然,這跟數學公式真是很貼近,簡單易懂。

本章小結

在Kotlin中,支持函數作為一等公民。它支持高階函數、lambda表達式等。我們不僅可以把函數當做普通變量一樣傳遞、返回,還可以把它分配給變量、放進數據結構或者進行一般性的操作。在Kotlin中進行函數式編程相當簡單自如。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/540498.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/540498.shtml
英文地址,請注明出處:http://en.pswp.cn/news/540498.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

mcq 隊列_人工智能能力問答中的人工智能概率推理(MCQ)

mcq 隊列1) Which of the following correctly defines the use of probabilistic reasoning in AI systems? In situations of uncertainty, probabilistic theory can help us give an estimate of how much an event is likely to occur or happen.It helps to find the pr…

[轉載] Python中的xrange和range的區別

參考鏈接&#xff1a; Python中的range()和xrange() 在python2 中 range(start,end,step)返回一個列表&#xff0c;返回的結果是可迭代對象&#xff0c;但不是迭代器。iter()轉化為列表迭代器。xrange()返回的是一個序列&#xff0c;他也是可迭代對象&#xff0c;但不是迭代…

Kubernetes基礎組件概述

本文講的是Kubernetes基礎組件概述【編者的話】最近總有同學問Kubernetes中的各個組件的相關問題&#xff0c;其實這些概念內容在官方文檔中都有&#xff0c;奈何我們有些同學可能英文不好&#xff0c;又或者懶得去看&#xff0c;又或者沒有找到&#xff0c;今天有時間就專門寫…

c語言將鏈表寫入二進制文件_通過逐級遍歷將二進制樹轉換為單鏈表的C程序

c語言將鏈表寫入二進制文件Problem statement: Write a C program to convert a binary tree into a single linked list by traversing level-wise. 問題陳述&#xff1a;編寫一個C程序&#xff0c;通過逐級遍歷將二進制樹轉換為單個鏈表 。 Example: 例&#xff1a; The ab…

[轉載] C Primer Plus 第6章 C控制語句 6.16 編程練習及答案

參考鏈接&#xff1a; 用Python打印金字塔圖案的程序 2019獨角獸企業重金招聘Python工程師標準>>> 1、編寫一個程序&#xff0c;創建一個具有26個元素的數組&#xff0c;并在其中存儲26個小寫字母。并讓該程序顯示該數組的內容。 #include int main (void) { …

C# String和string的區別

C#中同時存在String與string MSDN中對string的說明&#xff1a; string is an alias for String in the .NET Framework。string是String的別名而已&#xff0c;string是c#中的類&#xff0c;String是Framework的類&#xff0c;C# string 映射為 Framework的 String。如果用str…

要求用戶在Python中輸入整數| 限制用戶僅輸入整數值

input() function can be used for the input, but it reads the value as a string, then we can use the int() function to convert string value to an integer. input()函數可用于輸入&#xff0c;但它將值讀取為字符串&#xff0c;然后可以使用int()函數將字符串值轉換為…

[轉載] python——if語句、邏輯運算符號

參考鏈接&#xff1a; 用Python鏈接比較運算符 1.if條件判斷語句&#xff1a; if 要判斷的條件(True): 條件成立的時候&#xff0c;要做的事情 elif 要判斷的條件(True): .... elif 要判斷的條件(True): .... else: 條件不成立的時候要做的事情 示例&#xff1a; 判斷學生…

洛谷 P2689 東南西北【模擬/搜索】

題目描述 給出起點和終點的坐標及接下來T個時刻的風向(東南西北)&#xff0c;每次可以選擇順風偏移1個單位或者停在原地。求到達終點的最少時間。 如果無法偏移至終點&#xff0c;輸出“-1”。 輸入輸出格式 輸入格式&#xff1a; 第一行兩個正整數x1,y1&#xff0c;表示小明所…

單鏈表遍歷_單鏈表及其遍歷實現的基本操作

單鏈表遍歷單鏈表 (Single linked list) Single linked list contains a number of nodes where each node has a data field and a pointer to next node. The link of the last node is to NULL, indicates end of list. 單個鏈表包含許多節點&#xff0c;其中每個節點都有一…

[轉載] python中for語句用法_詳解Python中for循環的使用_python

參考鏈接&#xff1a; 在Python中將else條件語句與for循環一起使用 這篇文章主要介紹了Python中for循環的使用,來自于IBM官方網站技術文檔,需要的朋友可以參考下 for 循環 本系列前面 “探索 Python&#xff0c;第 5 部分&#xff1a;用 Python 編程” 一文討論了 if 語句和…

windows 軟鏈接的建立及刪除

在windows服務器上有時有這樣的需求&#xff0c;你的文件在f:\test中&#xff0c;但由于其它原因用戶訪問的是e:\test&#xff0c;如果又希望e:\test 中的文件與f:\test的保持同步&#xff0c;除了用同步軟件來做外&#xff0c;可以用windows 的文件夾映射來做 cmd: mklink /J …

8086簡單的指令流水線_在8086微處理器中執行流水線的指令和概念的步驟

8086簡單的指令流水線Any computer or machine works according to some instructions. These instructions are responsible for all the work that the machine does. But how does a machine work to understand and execute that instruction? 任何計算機或機器都按照某些…

[轉載] 使用Python編寫打字訓練小程序

參考鏈接&#xff1a; 在Python中切換大小寫(替換) 你眼中的程序猿 別人眼中的程序猿&#xff0c;是什么樣子&#xff1f;打字如飛&#xff0c;各種炫酷的頁面切換&#xff0c;一個個好似黑客般的網站破解。可現實呢&#xff1f; 二指禪的敲鍵盤&#xff0c;寫一行代碼&#…

shell兩個數字相乘_使用8086微處理器將兩個16位數字相乘而不帶進位

shell兩個數字相乘Problem statement: 問題陳述&#xff1a; To perform multiplication operation between 2 16bit numbers with carry using 8086 Microprocessor. 使用8086微處理器在2個16位數字之間進行帶進位的乘法運算。 Algorithm: 算法&#xff1a; Load the first…

Dwr 框架簡單實例

Dwr 是一個 Java 開源庫&#xff0c;幫助你實現Ajax網站。 它可以讓你在瀏覽器中的Javascript代碼調用Web服務器上的Java&#xff0c;就像在Java代碼就在瀏覽器中一樣。 Dwr 主要包括兩部分&#xff1a; 在服務器上運行的 Servlet 來處理請求并把結果返回瀏覽器。 運行在瀏覽器…

[轉載] Python進階:設計模式之迭代器模式

參考鏈接&#xff1a; Python中的迭代器 在軟件開發領域中&#xff0c;人們經常會用到這一個概念——“設計模式”&#xff08;design pattern&#xff09;&#xff0c;它是一種針對軟件設計的共性問題而提出的解決方案。在一本圣經級的書籍《設計模式&#xff1a;可復用面向對…

JavaScript | 如何為變量分配十進制,八進制和十六進制值?

Just like C programming language, we can assign integer value in the different format to the variable. 就像C編程語言一樣 &#xff0c;我們可以將不同格式的整數值分配給變量。 Assigning decimal value: It can be assigned simply without using any prefix. 分配十…

路由器DHCP和DHCP中繼的配置

路由器 DHCP和DHCP中繼的配置 路由器作為DHCP服務器&#xff1a; 1.配置router的地址&#xff1a;Route(config)# hostname gateway (更改主機名字) Gateway(config)# interface gigabitethernet 0/0 …

[轉載] 大數據分析Python For循環教程

參考鏈接&#xff1a; Python中的迭代器函數1 大數據分析Python除了循環遍歷列表之外&#xff0c;for循環還有很多其他功能&#xff0c;在現實世界的數據科學工作中&#xff0c;您可能需要將numpy數組和pandas DataFrames用于其他數據結構的循環。 大數據分析Python For循環教…