python3 第二十章 - 函數式編程之Higher-order function(高階函數)

来源:https://www.cnblogs.com/mrbug/archive/2018/01/28/8326167.html
-Advertisement-
Play Games

什麼是高階函數?把函數作為參數傳入,這樣的函數稱為高階函數,函數式編程就是指這種高度抽象的編程範式。 在前面的章節中,我們知道可以用abs()這個函數來得到一個數的絕對值,如: 以上代碼,輸出: 如果,我們把代碼修改下,把abs賦值給一個變數: 以上代碼,輸出: 可見,abs(-100)是函數調用, ...


什麼是高階函數?把函數作為參數傳入,這樣的函數稱為高階函數,函數式編程就是指這種高度抽象的編程範式。

在前面的章節中,我們知道可以用abs()這個函數來得到一個數的絕對值,如:

print('abs(-100):', abs(-100))

以上代碼,輸出:

abs(-100): 100

 

如果,我們把代碼修改下,把abs賦值給一個變數:

f = abs
print(f(-100))

以上代碼,輸出:

100

可見,abs(-100)是函數調用,而abs是函數本身,要獲得函數調用結果,我們可以把結果賦值給變數。函數本身也可以賦值給變數,即:變數可以指向函數,這時變數就獲得了函數的功能,如上例中的 f ,直接調用f()和直接調用abs()完全相同。

 

那麼函數名是什麼呢?函數名其實就是指向函數的變數!對於abs()這個函數,完全可以把函數名abs看成變數,它指向一個可以計算絕對值的函數!

如果把abs指向其他對象,會有什麼情況發生?

abs = 10
print(abs(-10))

把abs指向10後,就無法通過abs(-10)調用該函數了!因為abs這個變數已經不指向求絕對值函數而是指向一個整數10!當然實際代碼絕對不能這麼寫,這裡是為了說明函數名也是變數。

註:由於abs函數實際上是定義在import builtins模塊中的,所以要讓修改abs變數的指向在其它模塊也生效,要用import builtins; builtins.abs = 10。關於什麼是模塊,後面會講到,這裡不要去糾結。

 

既然變數可以指向函數,函數的參數能接收變數,那麼一個函數就可以接收另一個函數作為參數,這種函數就稱之為高階函數。

一個最簡單的高階函數:

def add(x, y, f):
    return f(x) + f(y)

當我們調用add(-5, 6, abs)時,參數x,y和f分別接收-5,6和abs,根據函數定義,我們可以推導計算過程為:

x = -5
y = 6
f = abs
f(x) + f(y) ==> abs(-5) + abs(6) ==> 11
return 11

用代碼驗證下:

def add(x, y, f):
    return f(x) + f(y)

print(add(-5, 6, abs))

 

1、map函數

這是Python內置的一個函數,map()函數接收兩個參數,一個是函數,一個是Iterable(迭代器對象),map將傳入的函數依次作用到序列的每個元素,並把結果作為新的Iterator(迭代器)返回:

def f(x):
    """
    返回一個數的3次方
    :param x: 數
    :return: 3次方後的結果
    """
    return x**3

r = map(f, [1, 2, 3, 4, 5])

print(list(r))

 以上代碼,輸出:

[1, 8, 27, 64, 125]

map()傳入的第一個參數是f,即函數對象本身。由於結果r是一個Iterator,Iterator是惰性序列,因此通過list()函數讓它把整個序列都計算出來並返回一個list。

 

當然,不需要map()函數,寫一個迴圈,也可以計算出結果:

def f(x):
    """
    返回一個數的3次方
    :param x: 數
    :return: 3次方後的結果
    """
    return x**3

L = []
for n in [1, 2, 3, 4, 5]:
    L.append(f(n))

print(L)

執行後,結果是一樣的,但這樣的寫法,我們並不能直觀的知道f()是作用在列表的所有元素上,並返回一個列表,我們必須讀了源代碼後才知道。

所以,map()作為高階函數,事實上它把運算規則抽象了,因此,我們不但可以計算簡單的f(x)=x3,還可以計算任意複雜的函數,比如,把這個list所有數字轉為字元串:

print(list(map(str, [1, 2, 3, 4, 5])))

以上代碼,輸出:

['1', '2', '3', '4', '5']

 

當然,更多的時候我們也會配合lambda來使用,如:

r = map(lambda x: x**3, [1, 2, 3, 4, 5])

print(list(r))

 以上代碼,輸出:

[1, 8, 27, 64, 125]

 

 

2、reduce函數

reduce把一個函數作用在一個序列[x1, x2, x3, ...]上,這個函數必須接收兩個參數,reduce把結果繼續和序列的下一個元素做累積計算,其效果就是:

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

 對一個序列求和,就可以用reduce實現:

from functools import reduce

def add(x, y):
    return x + y

print('reduce(add, [1, 3, 5, 7, 9]) = ', reduce(add, [1, 3, 5, 7, 9]))

 以上代碼,輸出:

reduce(add, [1, 3, 5, 7, 9]) =  25

當然求和運算可以直接用Python內建函數sum(),思考:如何把序列 [1, 2, 3, 4, 5] 轉化為整型的 12345 ,下麵給出代碼但請儘可能自己先寫出

from functools import reduce

def f(x, y):
    return x*10 + y

res = reduce(f, [1, 2, 3, 4, 5])

print(res)
View Code

 

思考:假如python沒有提供 int() 函數,如何使用 mapreduce自己寫一個函數,實現將 '123456' 轉化為 123456 ,下麵給出代碼但請儘可能自己先寫出:

from functools import reduce


def str2int(s):
    """
    將字元串轉為數字
    :param s: 要轉化的字元串
    :return: 數字
    """

    DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
    def char2num(c):
        """
        字元轉數字
        :param c:字元
        :return: 數字
        """
        return DIGITS[c]

    def f(x, y):
        return x*10 + y

    resList = map(char2num, s) # 將字元串轉為列表

    return reduce(f, resList) # 將列表元素依次排列組成數字,並返回



print(str2int('1234567'))
View Code

 

 

3、filter函數

Python內建的filter()函數用於過濾序列。filter()接收一個函數和一個序列,把傳入的函數依次作用於每個元素,然後根據返回值是True還是False決定保留還是丟棄該元素。

例如,在一個list中,刪掉偶數,只保留奇數,可以這麼寫:

def is_odd(n):
    return n % 2 == 1

res = list(filter(is_odd, [1, 2, 3, 4, 5, 6, 7, 8]))
print(res)

 以上代碼,輸出:

[1, 3, 5, 7]

 

註:filter()函數返回的是一個Iterator,也就是一個惰性序列,filter()的作用是從一個序列中篩出符合條件的元素。由於filter()使用了惰性計算,所以只有在取filter()結果的時候,才會真正篩選並每次返回下一個篩出的元素。

 

4、sorted函數

排序也是在程式中經常用到的演算法。無論使用冒泡排序還是快速排序,排序的核心是比較兩個元素的大小。如果是數字,我們可以直接比較,但如果是字元串或者兩個dict呢?直接比較數學上的大小是沒有意義的,因此,比較的過程必須通過函數抽象出來。

Python內置的sorted()函數就可以對list進行排序:

print(sorted([36, 5, -12, 9, -21]))

 以上代碼,輸出:

[-21, -12, 5, 9, 36]

 

它還可以接收一個key函數來實現自定義的排序,例如按絕對值大小排序:

print(sorted([36, 5, -12, 9, -21], key=abs))

 以上代碼,輸出:

[5, 9, -12, -21, 36]

key指定的函數將作用於list的每一個元素上,並根據key函數返回的結果進行排序。

 

這個是數字,那字元串排序是怎麼樣的呢?我們來看實例:

print(sorted(['hello', 'world', 'roy', 'python', 'c++']))

 以上代碼,輸出:

['c++', 'hello', 'python', 'roy', 'world']

 預設情況下,對字元串排序,是按照ASCII的大小比較的,每個字元依次比較(兩個字元串比較,先比較第一個字元,當第一個字元分出大小時則結束比較且比較的結果即為第一個字元相比較的結果,當第一個字元相同時則比較第二個字元,第二個字元相同時則比較第三個,依次下去直到得出結果),由於'Z' < 'a',結果,大寫字母Z會排在小寫字母a的前面。

 

可以用 reverse=True 進行反向排序:

print(sorted([36, 5, -12, 9, -21], reverse=True))
print(sorted(['hello', 'world', 'roy', 'python', 'c++'], reverse=True))

以上代碼,輸出:

[36, 9, 5, -12, -21]
['world', 'roy', 'python', 'hello', 'c++']

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 每次我都不想接觸鏈表和樹的操作 這次要求逆轉鏈表結構(javascript) 核心思想是通過先存儲鏈表當前節點的next數據 let tt = tem.next; 使當前的節點的next指向我們設置的新鏈表(開始為null) tem.next = newhead; 更新新鏈表 newhead = t ...
  • 一、ORM 工具 1.1、Dapper.NET 簡介 Dapper.NET是個開源的輕型ORM。它擴展了IDbConnection 介面的功能,所以只要某類實現IDbConnection 介面,那麼該類對象就能調用到 Dapper.NET中的方法。提供的 Dapper.dll,支持.NET Fram ...
  • 轉載至笑松小站http://blog.seoui.com/2018/01/27/redis-lua/ redis從2.6版本開始內置支持Lua解釋器,解釋器提供了3個函數來處理redis的命令redis.call() redis.pcall()和 redis.log,同時redis 也保證腳本會以原 ...
  • 最近博客園出現了一篇文章《微服務時代之2017年五軍之戰:Net PHP誰先死》,掀起了一波撕逼,作者只是從一個使用者的角度來指點江山,這個姿勢是不對的。.NET Core就是專門針對模塊化的微服務架構而設計,在微服務架構這方面Java的Spring Cloud具有非常高的人氣,這個正是這篇文章作者 ...
  • Spring Cloud微服務實戰閱讀筆記(一) 基礎知識 ...
  • 一. 準備工作 1. 本文參考 J2EE企業級應用架構 二. 架構發展 1. 原始版 2. 動靜分離版 3. 緩存版 4. 分散式服務 5. 彈性計算 ...
  • 用戶手冊是作者經過實踐活動形成的經驗。包括實踐過程中所有的記錄,正確的,或者錯誤的。用戶手冊應該分為兩部分,經驗與理論。 經驗不是指實踐的過程,應該是實踐的結果。用戶不需要經歷作者的實踐活動,他們需要的是能夠直接指導其開始FreeNAS伺服器設置的工作。用戶不需要知道一步一步的做什麼,需要的是我有一 ...
  • 新人報道,初來乍到,對博客還不太熟悉,先寫點隨筆記錄一下每天的學習情況自娛自樂一下。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...