模式匹配能讓分支程式碼更具表達力,而解構則能優雅地將值綁定到變數。
透過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
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 員工,並包含留言的歷史紀錄。
以下四個任務可以改善你目前的工單系統。
空白留言會讓介面顯得雜亂,又無法提供任何資訊,所以你想把它們過濾掉。
定義一個輔助函式emptyComment,它接收一組使用者與留言的配對,並對空白留言回傳True。
在函式的引數中直接使用解構,取出使用者和留言。你不需要那位使用者的資訊,所以可以忽略它。
emptyComment ( User "Alice", "" )
-- => True
你的主管想要掌握使用者在工單流程中的參與情況統計。 她要你查看工單建立者新增了多少留言。
定義numberOfCreatorComments,它接收一張工單,並回傳工單建立者新增的留言數量。
在函式的引數中使用解構,從Ticket型別取出記錄,同時取出你需要的欄位。
接著,在let綁定中解構createdBy,這樣就能計算相關的留言。
使用List的函式來計數,並在匿名函式的引數中使用解構,以分開使用者與留言的配對。
numberOfCreatorComments
numberOfCreatorComments
(Ticket
{ status = Closed
, createdBy = ( User "Chiaki", 1 )
, assignedTo = Nothing
, comments = [ ( User "Chiaki", "Never mind, fixed it." ) ]
}
)
-- => 1
有些工單必須呈報給 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
有時候一批工單需要重新指派給其他 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!!" ) ]
-- }