學習軌道
/
Elm
Elm
/
練習
/
請出示車票!
請出示車票!

請出示車票!

學習練習

簡介

模式匹配

模式匹配能讓分支程式碼更具表達力,而解構則能優雅地將值綁定到變數。

簡單模式匹配

透過case敘述,模式匹配可以將值綁定到變數,也可以用萬用字元_將它們捨棄。

type Entity = Alien | Stranger (Maybe String) | Friend { name: String }

hello : Entity -> String
hello entity =
    case entity of
        -- Custom type variant
        Alien -> "Hello, you are not from around here are you?"
        -- Binding to a litteral value nested in Stranger
        Stranger (Just nametag) -> "Hello, erm... " ++ nametag ++ "."
        -- Discarding Friend's data
        Friend _ -> "Hi!"
        -- All other cases
        _ -> "Hello stranger!"

解構

當資料只有一種可能的形狀時,解構可以在let綁定、函式引數,當然還有 case 運算式中綁定值。

pairSum : ( Int, Int ) -> Int
pairSum pair =
    -- Destructuring of a pair in a 'let' binding
    let ( x, y ) = pair
    in x + y

-- Destructuring in the function argument
pairSum : ( Int, Int ) -> Int
pairSum ( x, y ) = x + y

-- Custom type containing a single variant
type Container = Box String

-- Destructuring in the function argument
unbox : Container -> String
unbox (Box str) = str

-- Destructuring combined with pattern matching
unboxMaybe : Maybe Container -> Maybe String
unboxMaybe maybeContainer =
    case maybeContainer of
        Nothing -> Nothing
        Just (Box "42") -> Just "The answer to the universe!"
        Just (Box str) -> Just str

解構也可以用在記錄上,或是搭配as關鍵字。

type alias Circle =
    { radius : Float
    , center : ( Float, Float )
    }

perimeter : Circle -> Float
perimeter { radius } =
    2 * pi * radius

left : Circle -> Float
left { radius, center } =
    -- the pair center cannot be pattern matched directly in the function argument
    -- so we do it in a 'let' statement
    let ( x, _ ) = center
    in x - radius

-- using the 'as' keyword to bind both the fields and the whole record
smaller : Float -> Circle -> Circle
smaller reduction ({ radius } as circle) =
    if reduction < radius then
        { circle | radius = radius - reduction }
    else
        circle

說明

你是 IT 服務台的一員,你想要改善工單系統,讓它更有效率,並為你的主管提供統計資料。

一張工單有狀態(new、in progress……)、建立者(以使用者名稱和員工 ID 辨識),可以被指派給 IT 員工,並包含留言的歷史紀錄。

以下四個任務可以改善你目前的工單系統。

1. 偵測空白留言

空白留言會讓介面顯得雜亂,又無法提供任何資訊,所以你想把它們過濾掉。

定義一個輔助函式emptyComment,它接收一組使用者與留言的配對,並對空白留言回傳True。

在函式的引數中直接使用解構,取出使用者和留言。你不需要那位使用者的資訊,所以可以忽略它。

emptyComment ( User "Alice", "" )
-- => True

2. 計算建立者的留言數量

你的主管想要掌握使用者在工單流程中的參與情況統計。 她要你查看工單建立者新增了多少留言。

定義numberOfCreatorComments,它接收一張工單,並回傳工單建立者新增的留言數量。

在函式的引數中使用解構,從Ticket型別取出記錄,同時取出你需要的欄位。 接著,在let綁定中解構createdBy,這樣就能計算相關的留言。 使用List的函式來計數,並在匿名函式的引數中使用解構,以分開使用者與留言的配對。

numberOfCreatorComments
numberOfCreatorComments
    (Ticket
        { status = Closed
        , createdBy = ( User "Chiaki", 1 )
        , assignedTo = Nothing
        , comments = [ ( User "Chiaki", "Never mind, fixed it." ) ]
        }
    )
-- => 1

3. 偵測指派給 Dev Team 的工單

有些工單必須呈報給 Dev Team 的 Alice、Bob 或 Charlie。 密切留意這些工單、確保它們持續有進展,是很重要的。

定義一個輔助函式assignedToDevTeam,用來檢查工單是否指派給 Dev Team 的其中一位成員。

在函式的引數中使用解構,從Ticket型別取出記錄,以及你需要的欄位。 接著,用單一的case敘述檢查工單是否指派給 Alice、Bob 或 Charlie:使用遞迴模式比對取出他們的使用者名稱,並使用字面值模式比對來比對它們。

assignedToDevTeam
    (Ticket
        { status = InProgress
        , createdBy = ( User "Bill", 2 )
        , assignedTo = Just (User "Alice")
        , comments = [ ( User "Bill", "What's an 'undefined'?" ) ]
        }
    )
-- => True

4. 將工單指派給 IT 員工

有時候一批工單需要重新指派給其他 IT 員工。

定義一個函式assignTicketTo來幫你完成這件事。這個函式接收兩個引數:要指派工單的目標使用者,以及一張工單。 如果工單的狀態是New,就把它指派給新的使用者,並將狀態改為InProgress。 至於其他狀態,只要把工單重新指派給新的使用者即可,但狀態為Archived的工單完全不能修改。

在函式的引數中使用解構,從Ticket型別取出記錄以及你需要的欄位,並使用as關鍵字將整個記錄原樣捕捉下來。 用單一的case敘述對狀態進行模式比對,並回傳適當的工單,該工單是由你以as關鍵字捕捉的原始記錄修改而來。

assignTicketTo (User "Danny")
    (Ticket
        { status = New
        , createdBy = ( User "Jesse", 3 )
        , assignedTo = Just (User "Alice")
        , comments = [ ( User "Jesse", "I've been waiting for 6 months!!" ) ]
        }
    )
-- => Ticket
--        { status = InProgress
--        , createdBy = ( User "Jesse", 3 )
--        , assignedTo = Just (User "Danny")
--        , comments = [ ( User "Jesse", "I've been waiting for 6 months!!" ) ]
--        }
透過 GitHub 編輯 連結會在新視窗或分頁中開啟
Elm Exercism

準備好開始 請出示車票! 了嗎?

註冊 Exercism,透過 28 個概念110 個練習 和真人引導來學習並精通 Elm,全部免費。