<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                前幾章介紹了 Scala 容器類型的可組合性特征。接下來,你會發現,Scala 中的一等公民——函數也具有這一性質。 組合性產生可重用性,雖然后者是經由面向對象編程而為人熟知,但它也絕對是純函數的固有性質。(純函數是指那些沒有副作用且是引用透明的函數) 一個明顯的例子是調用已知函數實現一個新的函數,當然,還有其他的方式來重用已知函數。這一章會討論函數式編程的一些基本原理。你將會學到如何使用高階函數,以及重用已有代碼時,遵守 [DRY](http://en.wikipedia.org/wiki/Don%27t_repeat_yourself) 原則。 ### 高階函數 和一階函數相比,高階函數可以有三種形式: 1. 一個或多個參數是函數,并返回一個值。 1. 返回一個函數,但沒有參數是函數。 1. 上述兩者疊加:一個或多個參數是函數,并返回一個函數。 看到這里的讀者應該已經見到過第一種使用:我們調用一個方法,像 `map` 、 `filter` 、 `flatMap` ,并傳遞另一個函數給它。傳遞給方法的函數通常是匿名函數,有時候,還涉及一些代碼冗余。 這一章只關注另外兩種功能:一個可以根據輸入值構建新的函數,另一個可以根據現有的函數組合出新的函數。這兩種情況都能夠消除代碼冗余。 ### 函數生成 你可能認為依據輸入值創建新函數的能力并不是那么有用。函數組合非常重要,但在這之前,還是先來看看如何使用可以產生新函數的函數。 假設要實現一個免費的郵件服務,用戶可以設置對郵件的屏蔽。我們用一個簡單的樣例類來代表郵件: ~~~ case class Email( subject: String, text: String, sender: String, recipient: String ) ~~~ 想讓用戶可以自定義過濾條件,需有一個過濾函數——類型為 `Email => Boolean` 的謂詞函數,這個謂詞函數決定某個郵件是否該被屏蔽:如果謂詞成真,那這個郵件被接受,否則就被屏蔽掉。 ~~~ type EmailFilter = Email => Boolean def newMailsForUser(mails: Seq[Email], f: EmailFilter) = mails.filter(f) ~~~ 注意,類型別名使得代碼看起來更有意義。 現在,為了使用戶能夠配置郵件過濾器,實現了一些可以產生 `EmailFilter` 的工廠方法: ~~~ val sentByOneOf: Set[String] => EmailFilter = senders => email => senders.contains(email.sender) val notSentByAnyOf: Set[String] => EmailFilter = senders => email => !senders.contains(email.sender) val minimumSize: Int => EmailFilter = n => email => email.text.size >= n val maximumSize: Int => EmailFilter = n => email => email.text.size <= n ~~~ 這四個 _vals_ 都是可以返回 `EmailFilter` 的函數,前兩個接受代表發送者的 `Set[String]` 作為輸入,后兩個接受代表郵件內容長度的 `Int` 作為輸入。 可以使用這些函數來創建 `EmialFilter` : ~~~ val emailFilter: EmailFilter = notSentByAnyOf(Set("johndoe@example.com")) val mails = Email( subject = "It's me again, your stalker friend!", text = "Hello my friend! How are you?", sender = "johndoe@example.com", recipient = "me@example.com") :: Nil newMailsForUser(mails, emailFilter) // returns an empty list ~~~ 這個過濾器過濾掉列表里唯一的一個元素,因為用戶屏蔽了來自 `johndoe@example.com` 的郵件。可以用工廠方法創建任意的 `EmailFilter` 函數,這取決于用戶的需求了。 ### 重用已有函數 當前的解決方案有兩個問題。第一個是工廠方法中有重復代碼。上文提到過,函數的組合特征可以很輕易的保持 DRY 原則,既然如此,那就試著使用它吧! 對于 `minimumSize` 和 `maximumSize` ,我們引入一個叫做 `sizeConstraint` 的函數。這個函數接受一個謂詞函數,該謂詞函數檢查函數內容長度是否OK,郵件長度會通過參數傳遞給它: ~~~ type SizeChecker = Int => Boolean val sizeConstraint: SizeChecker => EmailFilter = f => email => f(email.text.size) ~~~ 這樣,我們就可以用 `sizeConstraint` 來表示 `minimumSize` 和 `maximumSize` 了: ~~~ val minimumSize: Int => EmailFilter = n => sizeConstraint(_ >= n) val maximumSize: Int => EmailFilter = n => sizeConstraint(_ <= n) ~~~ ### 函數組合 為另外兩個謂詞(`sentByOneOf`、 `notSentByAnyOf`)介紹一個通用的高階函數,通過它,可以用一個函數去表達另外一個函數。 這個高階函數就是 `complement` ,給定一個類型為 `A => Boolean` 的謂詞,它返回一個新函數,這個新函數總是得出和謂詞相對立的結果: ~~~ def complement[A](predicate: A => Boolean) = (a: A) => !predicate(a) ~~~ 現在,對于一個已有的謂詞 `p` ,調用 `complement(p)` 可以得到它的補。然而, `sentByAnyOf` 并不是一個謂詞函數,它返回類型為 `EmailFilter` 的謂詞。 Scala 函數的可組合能力現在就用的上了:給定兩個函數 `f` 、 `g` , `f.compose(g)` 返回一個新函數,調用這個新函數時,會首先調用 `g` ,然后應用 `f` 到 `g` 的返回結果上。類似的, `f.andThen(g)` 返回的新函數會應用 `g` 到 `f` 的返回結果上。 知道了這些,我們就可以重寫 `notSentByAnyOf` 了: ~~~ val notSentByAnyOf = sentByOneOf andThen (g => complement(g)) ~~~ 上面的代碼創建了一個新的函數,這個函數首先應用 `sentByOneOf` 到參數 `Set[String]` 上,產生一個 `EmailFilter` 謂詞,然后,應用 `complement` 到這個謂詞上。使用 Scala 的下劃線語法,這短代碼還能更精簡: ~~~ val notSentByAnyOf = sentByOneOf andThen (complement(_)) ~~~ 讀者可能已經注意到,給定 `complement` 函數,也可以通過 `minimumSize` 來實現 `maximumSize` 。不過,先前的實現方式更加靈活,它允許檢查郵件內容的任意長度。謂 #### 謂詞組合 郵件過濾器的第二個問題是,當前只能傳遞一個 `EmailFilter` 給 `newMailsForUser` 函數,而用戶必然想設置多個標準。所以需要可以一種可以創建組合謂詞的方法,這個組合謂詞可以在任意一個標準滿足的情況下返回 `true` ,或者在都不滿足時返回 `false` 。 下面的代碼是一種實現方式: ~~~ def any[A](predicates: (A => Boolean)*): A => Boolean = a => predicates.exists(pred => pred(a)) def none[A](predicates: (A => Boolean)*) = complement(any(predicates: _*)) def every[A](predicates: (A => Boolean)*) = none(predicates.view.map(complement(_)): _*) ~~~ `any` 函數返回的新函數會檢查是否有一個謂詞對于輸入 `a` 成真。`none` 返回的是 `any` 返回函數的補,只要存在一個成真的謂詞, `none` 的條件就無法滿足。最后, `every` 利用 `none` 和 `any` 來判定是否每個謂詞的補對于輸入 `a` 都不成真。 可以使用它們來創建代表用戶設置的組合 `EmialFilter` : ~~~ val filter: EmailFilter = every( notSentByAnyOf(Set("johndoe@example.com")), minimumSize(100), maximumSize(10000) ) ~~~ #### 流水線組合 再舉一個函數組合的例子。回顧下上面的場景,郵件提供者不僅想讓用戶可以配置郵件過濾器,還想對用戶發送的郵件做一些處理。這是一些簡單的 `Emial => Email` 函數,一些可能的處理函數是: ~~~ val addMissingSubject = (email: Email) => if (email.subject.isEmpty) email.copy(subject = "No subject") else email val checkSpelling = (email: Email) => email.copy(text = email.text.replaceAll("your", "you're")) val removeInappropriateLanguage = (email: Email) => email.copy(text = email.text.replaceAll("dynamic typing", "**CENSORED**")) val addAdvertismentToFooter = (email: Email) => email.copy(text = email.text + "\nThis mail sent via Super Awesome Free Mail") ~~~ 現在,根據老板的心情,可以按需配置郵件處理的流水線。通過 `andThen` 調用實現,或者使用 Function 伴生對象上的 `chain` 方法: ~~~ val pipeline = Function.chain(Seq( addMissingSubject, checkSpelling, removeInappropriateLanguage, addAdvertismentToFooter)) ~~~ ### 高階函數與偏函數 這部分不會關注細節,不過,在知道了這么多通過高階函數來組合和重用函數的方法之后,你可能想再重新看看偏函數。 #### 鏈接偏函數 匿名函數那一章提到過,偏函數可以被用來創建責任鏈:`PartialFunction` 上的 `orElse` 方法允許鏈接任意個偏函數,從而組合出一個新的偏函數。不過,只有在一個偏函數沒有為給定輸入定義的時候,才會把責任傳遞給下一個偏函數。從而可以做下面這樣的事情: ~~~ val handler = fooHandler orElse barHandler orElse bazHandler ~~~ #### 再看偏函數 有時候,偏函數并不合適。仔細想想,一個函數沒有為所有的輸入值定義操作,這樣的事實還可以用一個返回 `Option[A]` 的標準函數代替:如果函數為一個輸入定義了操作,那就返回 `Some[A]` ,否則返回 `None` 。 要這么做的話,可以在給定的偏函數 `pf` 上調用 `lift` 方法得到一個普通的函數,這個函數返回 `Option` 。反過來,如果有一個返回 `Option` 的普通函數 `f` ,也可以調用 `Function.unlift(f)` 來得到一個偏函數。總 ### 總結 這一章給出了高階函數的使用,利用它可以在一個新的環境里重用已有函數,并用靈活的方式去組合它們。在所舉的例子中,就代碼行數而言,可能看不出太多價值,這些例子都很簡單,只是為了說明而已,在架構層面,組合和重用函數是有很大幫助的。 下一章,我們繼續探索函數組合的方式:_函數部分應用和柯里化(Partial Function Application and Currying)_。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看