久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

Spark局部套用和部分應用方法是什么

158次閱讀
沒有評論

共計 7167 個字符,預計需要花費 18 分鐘才能閱讀完成。

本篇內容主要講解“Spark 局部套用和部分應用方法是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓丸趣 TV 小編來帶大家學習“Spark 局部套用和部分應用方法是什么”吧!

  局部套用   和部分應用   是來源于數學的語言技術(基于 20 世紀數學家 Haskell Curry 和其他人的工作成果)。這兩種技術存在于各種類型的語言中,可以單獨或同時存在于函數式語言中。局部套用和部分應用使您能夠處理函數或方法的參數數量,通常的方法是為一些參數提供一個或多個默認值(稱為修正   參數)。所有 Java 下一代語言都包括局部套用和部分應用,但以不同的方式實現它們。在本文中,我將介紹這兩種技術的不同之處,并展示它們在 Scala、Groovy 和 Clojure 中的實現細節,以及實際應用。

注意術語

  出于本部分的目的,方法(method)  和   函數(function)  是可互換的。支持局部套用和部分應用的面向對象語言使用方法。同樣,函數參數(function parameter)  和   函數參數(function argument)  也是可互換的。由于這些概念起源于數學,因此我自始至終使用的是   函數(function)  和   參數(argument),但這并不意味著這兩種技術對方法不起作用。

定義和區別

對于業余人士來說,局部套用和部分應用具有相同的效果。使用這兩種技術時,都可以創建一個一些參數具有預先提供值的函數版本:

局部套用是將多參數函數轉換為一系列單參數函數。它描述了轉換過程,而不是轉換函數的調用。調用方可以確定應用了多少參數,從而創建一個參數更少的導出函數。

部分應用將多參數函數轉換為一個參數更少的多參數函數,其值為提前提供的省略參數的值。本技術的名稱非常恰當:它將一些參數部分應用到函數,并返回一個具有簽名(由剩余參數組成)的函數。

使用局部套用和部分應用,可以提供參數值并返回一個可使用缺少參數調用的函數。但是,對函數應用局部套用會返回鏈中的下一個函數,而部分應用會將參數值綁到在運算期間提供的值上,生成一個具有更少   元數(參數的數量)的函數。當考慮具有兩個以上元數的函數時,這一區別會更加明顯。例如,process(x, y, z)  函數的完全套用版本是 process(x)(y)(z),其中  process(x)  和  process(x)(y)  都是接受一個參數的函數。如果只對第一個參數應用了局部套用,那么  process(x)  的返回值將是接受一個參數的函數,因此僅接受一個參數。與此相反,在使用部分應用時,會剩下一個具有更少元數的函數。對  process(x, y, z)  的一個參數使用部分應用會生成接受兩個參數的函數:process(y, z)。

這兩種技術的結果通常是相同的,但二者的區別也很重要,人們通常會對它們之間的區別產生誤解。更復雜的是,Groovy 可以實現部分應用和局部套用,但都將它們稱為  currying。而 Scala 具有偏應用函數(partially applied function)和  PartialFunction,盡管它們的名稱類似,但它們卻是兩個不同的概念。

在 Scala 中

Scala 支持局部套用和部分應用,還支持特征(trait),特征可以定義約束函數(constrained function)。

局部套用

在 Scala 中,函數可以將多個參數列表定義為括號組。調用參數數量比其定義數量少的函數時,會返回一個將缺少參數列表作為其參數的函數。請考慮 Scala 文檔的示例,如清單 1 所示。

清單 1. Scala 的參數局部套用

def filter(xs: List[Int], p: Int =  Boolean): List[Int] =
 if (xs.isEmpty) xs
 else if (p(xs.head)) xs.head :: filter(xs.tail, p)
 else filter(xs.tail, p)
def modN(n: Int)(x: Int) = ((x % n) == 0)
val nums = List(1, 2, 3, 4, 5, 6, 7, 8)
println(filter(nums, modN(2)))
println(filter(nums, modN(3)))

在清單 1 中,filter()  函數遞歸地應用傳遞的過濾條件。modN()  函數定義了兩個參數列表。在我使用  filter()  調用  modN  時,我傳遞了一個參數。filter()  函數被作為函數的第二個參數,具有一個  Int  參數和一個  Boolean  返回值,這與我傳遞的局部套用函數的簽名相匹配。

偏應用函數

在 Scala 中還可以部分應用函數,如清單 2 所示。

清單 2. Scala 中部分應用的函數

def price(product : String) : Double =
 product match {
 case  apples  =  140
 case  oranges  =  223
def withTax(cost: Double, state: String) : Double =
 state match {
 case  NY  =  cost * 2
 case  FL  =  cost * 3
}val locallyTaxed = withTax(_: Double,  NY)val costOfApples = locallyTaxed(price( apples))
assert(Math.round(costOfApples) == 280)

在清單 2 中,我首先創建了一個  price  函數,它返回了產品和價格之間的映射。然后我創建了一個  withTax()  函數,其參數為  cost  和 state。但是,在特殊的源文件中,我知道要專門處理一個國家的稅收。我沒有對每次調用的額外參數應用局部套用,而是部分應用了  state 參數,并返回一個 state 值固定的函數。locallyTaxed  函數接受一個參數,即  cost。

偏(約束)函數

Scala PartialFunction  特征可以與模式無縫地配合使用(請閱讀函數式思維   系列的 Either 樹和模式匹配 部分中的模式匹配)。盡管名稱類似,但此特征不會創建偏應用函數。相反,可以使用它定義僅適用于值和類型定義子集的函數。

Case 塊是應用偏函數(partial function)的一種方式。清單 3 使用了 Scala 的  case,沒有傳統對應的  match  操作符。

清單 3. 使用不帶 match 的  case

val cities = Map( Atlanta  -   GA ,  New York  -   New York ,
  Chicago  -   IL ,  San Francsico   -   CA ,  Dallas  -   TX )
cities map { case (k, v) =  println(k +   -    + v) }

在清單 3 中,我創建了一個城市和該城市所對應的州的映射。然后,我對該集合調用了  map  函數,map  會拆開鍵值對以輸出它們。在 Scala 中,包含  case  聲明的代碼塊是定義匿名函數的一種方式。不使用  case  可以更簡潔地定義匿名函數,但是,case  語法提供了如清單 4 所示的額外好處。

清單 4. map  和  collect  之間的區別

List(1, 3, 5,  seven) map { case i: Int ? i + 1 } // won t work
// scala.MatchError: seven (of class java.lang.String)
List(1, 3, 5,  seven) collect { case i: Int ? i + 1 }
// verify
assert(List(2, 4, 6) == (List(1, 3, 5,  seven) collect { case i: Int ? i + 1 }))

在清單 4 中,我不能在具有  case  的異構集合上使用  map:我收到了  MatchError,因為函數試圖增加  seven  字符串。但是  collect  工作正常。為什么會出現這種不同?什么地方出錯了?

Case 塊定義的是偏函數,而不是偏應用函數。偏函數   具有有限的允許值。例如,數學函數  1/x  是無效的,如果  x = 0。偏函數提供了一種定義允許值約束的方式。在   清單 4  的  collect  示例中,定義了  Int  而不是  String  的約束,因此沒有收集  seven  字符串。

要定義偏函數,還可以使用  PartialFunction  特征,如清單 5 所示。

清單 5. 在 Scala 中定義偏函數

val answerUnits = new PartialFunction[Int, Int] { def apply(d: Int) = 42 / d
 def isDefinedAt(d: Int) = d != 0
assert(answerUnits.isDefinedAt(42))
assert(! answerUnits.isDefinedAt(0))
assert(answerUnits(42) == 1)
//answerUnits(0)
//java.lang.ArithmeticException: / by zero

在清單 5 中,我從  PartialFunction  特征導出了  answerUnits,并提供了兩個函數:apply()  和  isDefinedAt()。apply()  函數計算值。我使用了  isDefinedAt()(PartialFunction  的必要方法)來定義確定參數適用性的約束。

還可以使用  case  塊實現偏函數,清單 5  的 answerUnits  可以采用更簡潔的方式編寫,如清單 6 所示。

清單 6. answerUnits  的另一種定義

def pAnswerUnits: PartialFunction[Int, Int] =
 { case d: Int if d != 0 =  42 / d }
assert(pAnswerUnits(42) == 1)
//pAnswerUnits(0)
//scala.MatchError: 0 (of class java.lang.Integer)

在清單 6 中,我結合使用了  case  和保衛條件來約束值并同時提供值。與   清單 5  的一個明顯區別是  MatchError(而不是 ArithmeticException),因為清單 6 使用了模式匹配。

偏函數并不僅局限于數值類型。它可以使用所有類型的數值,包括  Any。可以考慮增量器(incrementer)的實現,如清單 7 所示。

清單 7. 在 Scala 中定義增量器

def inc: PartialFunction[Any, Int] =
 { case i: Int =  i + 1 }
assert(inc(41) == 42)
//inc(Forty-one)
//scala.MatchError: Forty-one (of class java.lang.String)
assert(inc.isDefinedAt(41))
assert(! inc.isDefinedAt( Forty-one))
assert(List(42) == (List(41,  cat) collect inc))

在清單 7 中,我定義了一個偏函數來接受任意類型的輸入 (Any),但選擇對類型子集做出反應。請注意,我還可以調用偏函數的 isDefinedAt()  函數。使用  case  的  PartialFunction  特征的實現者可以調用  isDefinedAt(),它是隱式定義的。在   清單 4  中,我說明了  map  和  collect  的表現不同。偏函數的行為解釋了它們的區別:collect  旨在接受偏函數,并調用元素的  isDefinedAt()  函數,會忽略那些不匹配的函數。

在 Scala 中,偏函數和偏應用函數的名稱類似,但是它們提供了不同的正交特性集。例如,沒有什么可以阻止您部分地應用偏函數。

在 Groovy 中

在我的函數式思維   系列的 運用函數式思維,第 3 部分 中詳細介紹了 Groovy 中的局部套用和部分應用。Groovy 通過  curry()  函數實現了局部套用,該函數來自  Closure  類。盡管名稱如此,但  curry()  實際上通過處理其下面的閉包塊來實現部分應用。但是,您可以模擬局部套用,方法是使用部分應用將函數減少為一系列部分應用的單參數函數,如清單 8 所示。

清單 8. Groovy 的部分應用和局部套用

def volume = { h, w, l -  return h * w * l }
def area = volume.curry(1)
def lengthPA = volume.curry(1, 1) //partial application
def lengthC = volume.curry(1).curry(1) // currying
println  The volume of the 2x3x4 rectangular solid is ${volume(2, 3, 4)} 
println  The area of the 3x4 rectangle is ${area(3, 4)} 
println  The length of the 6 line is ${lengthPA(6)} 
println  The length of the 6 line via curried function is ${lengthC(6)}

在清單 8 中,在兩種  length  情況下,我使用  curry()  函數部分應用了參數。但是,在使用  lengthC  時,通過部分地應用參數,直到出現一連串的單參數函數為止,我制造了一種使用局部套用的幻覺。

在 Clojure 中

Clojure 包含  (partial f a1 a2 …)  函數,它具有函數  f  以及比所需數量更少的參數,而且返回一個在提供剩余參數時調用的部分應用函數。清單 9 顯示了兩個示例。

清單 9. Clojure 的部分應用

(def subtract-from-hundred (partial - 100))
(subtract-from-hundred 10) ; same as (- 100 10)
; 90
(subtract-from-hundred 10 20) ; same as (- 100 10 20)
; 70

在清單 9 中,我將  subtract-from-hundred  函數定義為部分應用的  –  運算符(Clojure 中的運算符與函數無法區分),并提供 100 作為部分應用的參數。Clojure 中的部分應用適用于單參數函數和多參數函數,如清單 9 中的兩個示例所示。

由于 Clojure 是動態類型的,并且支持可變參數列表,因此局部套用并不能作為一種語言功能來實現。部分應用將會處理必要的情況。但是,Clojure 被添加到 reducers 庫(參見   參考資料)的命名空間私有  (defcurried …)  函數,支持在該庫中更輕松地定義一些函數。鑒于 Clojure 的 Lisp 傳承的靈活特點,可以輕松擴大  (defcurried …)  的使用范圍。

常見用法

盡管局部套用和部分應用具有復雜的定義和大量實現細節,但是它們在實際編程中都占有一席之地。

函數工廠

局部套用(和部分應用)適合在傳統的面向對象語言中實現工廠函數的位置使用。作為一個示例,清單 10 在 Groovy 中實現了一個簡單的 adder  函數。

清單 10. Groovy 中的加法器和增量器

def adder = { x, y -  x + y}
def incrementer = adder.curry(1)
println  increment 7: ${incrementer(7)}  // 8

在清單 10 中,我使用  adder()  函數來導出  incrementer  函數。同樣,在   清單 2  中,我使用部分應用創建了一個更簡潔的本地函數版本。

Template Method 設計模式

Gang of Four 設計模式之一是 Template Method 模式。它的用途是幫助定義算法 shell,使用內部抽象方法來實現稍后的實現靈活性。部分應用和局部套用可以解決相同的問題。使用部分應用提供已知行為,并讓其他參數免費用于實現細節,這模擬了此面向對象設計模式的實現。

隱含值

與   清單 2  類似,一種常見的情況是您有一系列使用相似參數值調用的函數。例如,當與持久性框架交互時,必須將數據源作為第一個參數進行傳遞。通過使用部分應用,可以隱式地提供值,如清單 11 所示。

清單 11. 使用部分應用提供隱含值

(defn db-connect [data-source query params]
 ...)
(def dbc (partial db-connect  db/some-data-source))
(dbc  select * from %1   cust)

在清單 11 中,我使用了便利的  dbc  函數來訪問數據函數,無需提供數據源,就可以自動提供數據源。面向對象編程的精髓(隱含  this  上下文似乎出現在所有函數中)可以通過使用局部套用為所有函數提供  this  來實現,這使得它對用戶不可見。

到此,相信大家對“Spark 局部套用和部分應用方法是什么”有了更深的了解,不妨來實際操作一番吧!這里是丸趣 TV 網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

正文完
 
丸趣
版權聲明:本站原創文章,由 丸趣 2023-08-16發表,共計7167字。
轉載說明:除特殊說明外本站除技術相關以外文章皆由網絡搜集發布,轉載請注明出處。
評論(沒有評論)
主站蜘蛛池模板: 公安县| 同心县| 义马市| 大余县| 鹿泉市| 辽宁省| 梅州市| 林西县| 镇安县| 清镇市| 越西县| 宁阳县| 当阳市| 衡山县| 富锦市| 西畴县| 尼玛县| 昭平县| 离岛区| 辽阳市| 临夏市| 交城县| 平度市| 五大连池市| 仁化县| 嘉兴市| 文安县| 沈丘县| 嘉荫县| 梨树县| 塔城市| 克东县| 永新县| 宁河县| 万年县| 宝山区| 视频| 建阳市| 建平县| 陈巴尔虎旗| 虹口区|