我正在独自学习haskell。并且正在努力使用基本列表和大小写实现自定义List数据类型。
所以数据结构与此类似
data List = List [String] | EmptyList deriving Show
现在,如果我要为基本用例进行大小写表达式,则必须进行两次匹配。一个简单的例子就是size函数
size :: List -> Int
size lst = case lst of
(List []) -> 0
EmptyList -> 0
(List (x:xs)) -> 1 + size (List xs)
我是否可以通过某种方式合并列表为空的两个基本案例(List [])
和EmptyList
来减少冗余?
size :: List -> Int
size lst = case lst of
(List []) | EmptyList -> 0
(List (x:xs)) -> 1 + size (List xs)
我试图在网上搜索所有内容,但是不幸的是,在一种情况下,无法找到匹配多个模式的具体内容。
答案 0 :(得分:4)
首先,您应该首先考虑为什么要为List
和EmptyList
分别使用构造函数。无论如何,空列表显然已经是列表的一种特殊情况,因此这是一个笨拙的冗余。如果有的话,你应该做到
import Data.List.NonEmpty
data List' a = NEList (NonEmpty a) | EmptyList
适用于此特定示例的另一个选项是将空案例变成“全包模式”:
size :: List -> Int
size lst = case lst of
(List (x:xs)) -> 1 + size (List xs)
_ -> 0
顺便说一句,这里没有理由使用case
,您也可以只编写两个函数子句:
size :: List -> Int
size (List (x:xs)) = 1 + size (List xs)
size _ = 0
无论如何-通常不建议这样做,因为如果将来扩展数据类型,那么包罗万象的子句很容易发现难以爬入的错误。
也可以,但更糟糕的样式是使用布尔值后卫匹配 –这可以轻松地在选项列表中使用查找,例如
size lst | lst`elem`[EmptyList, List []] = 0
size (List (x:xs)) = 1 + size (List xs)
如果可能,应避免进行平等检查;他们引入了Eq
约束,这毫无必要,将要求 elements 是可比的。通常,相等性检查也比模式匹配在计算上更昂贵。
如果您无法更改数据结构本身,但是愿意想要使用它,就像List []
和EmptyList
一样,这是另一种选择,那就是编写自定义模式同义词。这是Haskell的相对较新的功能; 假装的数据结构实际上与实际布局不同,例如List'
。
答案 1 :(得分:1)
在评论中,您说
没有此类函数[应该为
EmptyList
和List []
返回不同的结果]
因此,我建议在类型本身中合并这两个构造函数:
data List = List [String] deriving Show
现在,在使用EmptyList
的函数中,您不再需要在List []
和List
之间进行区分。
...事实上,我将走得更远,完全取消定义,只需在各处使用[String]
即可。对此有一个例外:如果您需要为一个行为不同于[String]
的现有实例的类定义一个实例。在这种特殊情况下,定义一种新类型是明智的;但出于通常的效率和语义原因,我将使用newtype
而不是data
。