6 2 1 使用函式處理元組

2021-05-25 06:55:06 字數 2967 閱讀 7431

6.2.1 使用函式處理元組

在第 3 章,我們用元組來表示城市和人口。當我們想要增加人口時,不得不寫點東西,像這樣:

let (name, population) = oldprague

let newprague = (name, population + 13195)

這很清晰,但有點羅唆。第一行分解元組,第二行對第二個元素執行計算,然後,生成新的元組。理想情況下,我們可能想說,我們要對第二個元素執行分解計算,然後重建這個元組。首先,讓我們看一下我們希望能夠寫的**, f# 和 c#,然後,將實現方法,使其工作。這就是我們的目標:

let newprague = oldprague |> mapsecond ((+) 13195)       //f#

var newprague = oldprague.mapsecond(n => n + 13195);  //c#

此版本刪除了所有額外的**,以重建該元組,並指定核心理念,即,我們要把元組中第二個元素中加上某個數。要在第二個元素上執行計算的想法,是通過使用 f# 中的 mapsecond 函式表示的。清單 6.4 顯示了這個函式和類似的 mapfirst 的實現。

listing 6.4 higher-order functions for working with tuples (f# interactive)

> let mapfirst f (a, b) = (f(a), b)

let mapsecond f (a, b) = (a, f(b))

;; val mapfirst : ('a -> 'b) -> 'a * 'c -> 'b * 'c

val mapsecond : ('a -> 'b) -> 'c * 'a -> 'c * 'b

清單 6.4 實現兩個函式:乙個是在元組的第乙個元素上執行操作,另乙個是作用於第二個元素。這兩個函式的實現很簡單:我們在引數列表中使用模式匹配,以分解給定的元組,然後,以元素之一呼叫該函式。最後,我們返回乙個新的元組,由函式呼叫的結果與其他元素的原始值組成。雖然,函式體看起來不難,推導出的型別簽名看起來卻相當複雜,當你第一次看到它們時。很快我們會再回來。

對映操作

我使用術語對映(map),我們剛才討論的函式的名稱。對映(也稱為投影)是一項常見的操作,並正如你將要看到的,它可以與許多資料型別一起使用。一般情況下,它取乙個函式作為引數值,並應用此函式乙個,或有時多個值,儲存在這個資料型別中。結果被包在具有相同結構的資料型別中,並返回作為這個對映操作的結果。結構並未改變,因為,我們所指定的操作不會告訴我們用這個組合值來做什麼。它只指定用這個值的元件做什麼,不知道其他,投影必須保留原始結構。現在,這個描述可能不完全清楚,因為,這很大程度上取決於你獲得的直覺,在本章後面更類似的操作之後。

這些函式的簽名,對於了解它們在做什麼是有用的。圖 6.1 分解了 mapfirst 的簽名,並顯示每個部分的意思。

圖 6.1 mapfirst 函式取乙個函式作為第乙個引數值,並將其應用到元組的第乙個元素,元組作為第二個引數值傳遞。

讓我們看看這個簽名能告訴我們有關這個函式的什麼內容。首先,它是乙個泛型函式,有三個型別引數,由 f# 編譯器自動命名。它取乙個函式作為第乙個引數,元組包含型別為 'a 和 ' c 的值,作為第二引數值。這個簽名告訴我們,返回的元組由型別為 'b 和 'c 的值組成。

因為這個函式不具有任何安全的方式,來處理 ' c 型別的值,它可能是只複製第二個元素。接下來的問題,是我們怎樣才能在結果中,獲得 ' b 型別的值。我們有乙個 ' a 型別的值(元組的第乙個元素),和乙個函式,它可以將乙個 'a 型別的值轉換成 ' b 型別的值,所以,最明顯的解釋是,mapfirst 適用這個函式到元組的第乙個元素。

現在,我們已經實現了 mapfirst 和 mapsecond 函式,讓我們開始使用它們。清單 6.5 顯示了f# interactive 會話演示如何能用來處理元組。

listing 6.5 working with tuples (f# interactive)

> let oldprague = ("prague", 1188000);;

val prague : string * int

> mapsecond (fun n -> n + 13195) oldprague;;

val it : string * int = ("prague", 1201195)

> oldprague |> mapsecond ((+) 13195);;

val it : string * int = ("prague", 1201195)

該示例演示了兩種方來法寫相同的操作,使用 mapsecond 函式。第一種情況,我們直接呼叫這個函式,給它 lambda 函式作為第乙個引數值,原始的元組作為第二個引數。如果你看看由 f# interactive 列印的結果元組,可以看到這個函式應用到元組的第二個元素,正是我們想要的。

在第二個版本中,我們使用兩個強大的技術。我們使用了偏函式應用(在前一章中介紹的)來建立乙個函式,為第二個元素加上  13195。不用顯式寫 lambda 函式,我們寫了 (+) 13195。如果在括號中使用運算子,它的行為像普通函式,這意味著,我們可以通過寫 (+) 10 5 來加兩個數。如果我們使用偏函式應用,並只給它有乙個引數值,我們就獲得了乙個 int –> int 型別的函式,加這個數到任意給定的引數值,型別與 mapsecond 函式預期的相容。型別是 'a -> 'b,在這種情況下, int 替換為 'a 和' b。

由於有了流,我們可以寫原始的元組,然後是應用的這個函式。這使**更具可讀性,首先,描述我們要去操作什麼;然後,我們打算用它做什麼——就像在 c# 中,操作是通常的目標形式,methodtocall()。使用流也是原因之一,mapsecond 為什麼需要取乙個函式作為第乙個引數值,並且,元組作為第二個引數值,而不是相反。

我這一節開始討論了 f#,因為,顯示推導出高階函式的型別簽名,使用流可以非常自然地在 f# 中演示。當然,我們可以在 c# 中,使用相同的概念,我們將在下一節這樣做。

python 元組使用 使用元組

usr bin python filename using tuple.py zoo wolf elephant penguin print number of animals in the zoo is len zoo new zoo monkey dolphin zoo print number...

元組 函式一

作業深拷貝 deepcopy copy 模組的 deepcopy 方法,完全拷貝了父物件及其子物件。列印名片程式 輸入姓名,號碼,性別,最後列印出來名片 控制姓名長度為6 20 號碼長度11 性別只能允許輸入男或女 每一樣資訊不允許為空import re name input phone input...

使用函式處理資料

使用函式處理資料 substr substr substring substring 擷取字元 select substring prod name,2,6 from products select prod name from products select convert varchar,pro...