<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>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                16-協議 ======== [協議和結構體](#161-%E5%8D%8F%E8%AE%AE%E5%92%8C%E7%BB%93%E6%9E%84%E4%BD%93) [回歸一般化](#) [內建協議](#163-%E5%86%85%E5%BB%BA%E5%8D%8F%E8%AE%AE) 協議是實現Elixir多態性的重要機制。任何數據類型只要實現了某協議,那么該協議的分發就是可用的。 讓我們看個例子。 >這里的“協議”二字對于熟悉ruby等具有duck-typing特性的語言的人來說會比較容易理解。 在Elixir中,只有false和nil被認為是false的。其它的值都被認為是true。 根據程序需要,有時需要一個```blank?```協議(注意,我們此處稱之為“協議”), 返回一個布爾值,以說明該參數是否為空。 舉例來說,一個空列表或者空二進制可以被認為是空的。 我們可以如下定義協議: ```elixir defprotocol Blank do @doc "Returns true if data is considered blank/empty" def blank?(data) end ``` 從上面代碼的語法上看,這個協議```Blank```聲明了一個函數```blank?```,接受一個參數。 看起來這個“協議”像是一份聲明,需要后續的實現。 下面我們為不同的數據類型實現這個協議: ```elixir # 整型永遠不為空 defimpl Blank, for: Integer do def blank?(_), do: false end # 只有空列表是“空”的 defimpl Blank, for: List do def blank?([]), do: true def blank?(_), do: false end # 只有空map是“空” defimpl Blank, for: Map do # 一定要記住,我們不能匹配 %{} ,因為它能match所有的map。 # 但是我們能檢查它的size是不是0 # 檢查size是很快速的操作 def blank?(map), do: map_size(map) == 0 end # 只有false和nil這兩個原子被認為是空得 defimpl Blank, for: Atom do def blank?(false), do: true def blank?(nil), do: true def blank?(_), do: false end ``` 我們可以為所有內建數據類型實現協議: - 原子 - BitString - 浮點型 - 函數 - 整型 - 列表 - 圖 - PID - Port - 引用 - 元祖 現在手邊有了一個定義并被實現的協議,如此使用之: ```elixir iex> Blank.blank?(0) false iex> Blank.blank?([]) true iex> Blank.blank?([1, 2, 3]) false ``` 給它傳遞一個并沒有實現該協議的數據類型,會導致報錯: ```elixir iex> Blank.blank?("hello") ** (Protocol.UndefinedError) protocol Blank not implemented for "hello" ``` ## 16.1-協議和結構體 協議和結構體一起使用能夠加強Elixir的可擴展性。 在前面幾章中我們知道,盡管結構體本質上就是圖(map),但是它們和圖并不共享各自協議的實現。 像前幾章一樣,我們先定義一個名為```User```的結構體: ```elixir iex> defmodule User do ...> defstruct name: "john", age: 27 ...> end {:module, User, <<70, 79, 82, ...>>, {:__struct__, 0}} ``` 然后看看能不能用剛才定義的協議: ```elixir iex> Blank.blank?(%{}) true iex> Blank.blank?(%User{}) ** (Protocol.UndefinedError) protocol Blank not implemented for %User{age: 27, name: "john"} ``` 果然,結構體沒有使用協議針對圖的實現。 因此,結構體需要使用它自己的協議實現: ```elixir defimpl Blank, for: User do def blank?(_), do: false end ``` 如果愿意,你可以定義你自己的語法來檢查一個user是否為空。 不光如此,你還可以使用結構體創建更強健的數據類型(比如隊列),然后實現所有相關的協議 (就像枚舉```Enumerable```那樣),檢查是否為空等等。 有些時候,程序員們希望給結構體提供某些默認的協議實現,因為顯式給所有結構體都實現某些協議實在是太枯燥了。 這引出了下一節“回歸一般化”(falling back to any)的說法。 ## 16.2-回歸一般化 能夠給所有類型提供默認的協議實現肯定是很方便的。 在定義協議時,把```@fallback_to_any```設置為```true```即可: ```elixir defprotocol Blank do @fallback_to_any true def blank?(data) end ``` 現在這個協議可以被這么實現: ```elixir defimpl Blank, for: Any do def blank?(_), do: false end ``` 現在,那些我們還沒有實現```Blank```協議的數據類型(包括結構體)也可以來判斷是否為空了 (雖然默認會被認為是false,哈哈)。 ## 16.3-內建協議 Elixir自帶了一些內建的協議。在前面幾章中我們討論過枚舉模塊,它提供了許多方法。 只要任何一種數據結構它實現了Enumerable協議,就能使用這些方法: ```elixir iex> Enum.map [1, 2, 3], fn(x) -> x * 2 end [2,4,6] iex> Enum.reduce 1..3, 0, fn(x, acc) -> x + acc end 6 ``` 另一個例子是```String.Chars```協議,它規定了如何將包含字符的數據結構轉換為字符串類型。 它暴露為函數```to_string```: ```elixir iex> to_string :hello "hello" ``` 注意,在Elixir中,字符串插值操作里面調用了```to_string```函數: ```elixir iex> "age: #{25}" "age: 25" ``` 上面代碼能工作,是因為25是數字類型,而數字類型實現了```String.Chars```協議。 如果傳進去的是元組就會報錯: ```elixir iex> tuple = {1, 2, 3} {1, 2, 3} iex> "tuple: #{tuple}" ** (Protocol.UndefinedError) protocol String.Chars not implemented for {1, 2, 3} ``` 當想要打印一個比較復雜的數據結構時,可以使用```inspect```函數。該函數基于協議```Inspect```: ```elixir iex> "tuple: #{inspect tuple}" "tuple: {1, 2, 3}" ``` _Inspect_ 協議用來將任意數據類型轉換為可讀的文字表述。IEx用來打印表達式結果用的就是它: ```elixir iex> {1, 2, 3} {1,2,3} iex> %User{} %User{name: "john", age: 27} ``` >```inspect```是ruby中非常常用的方法。 這也能看出Elixir的作者們真是絞盡腦汁把Elixir的語法盡量往ruby上靠。 記住,頭頂著#號被插的值,會被```to_string```表現成純字符串。 在轉換為可讀的字符串時丟失了信息,因此別指望還能從該字符串取回原來的那個對象: ```elixir iex> inspect &(&1+2) "#Function<6.71889879/1 in :erl_eval.expr/5>" ``` Elixir中還有些其它協議,但本章就講這幾個比較常用的。下一章將講講Elixir中的錯誤捕捉以及異常。
                  <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>

                              哎呀哎呀视频在线观看