我正在尝试开发一个match
函数,将两个Tuplas组合成一个Record,它具有以下组合[(Label,Value)]。第一个元组,必须只有String类型的元素,第二个元组可以是任何其他类型的元素。
代码是:
type RItem = (Label, Term)
type TItem = (Term)
type Label = String
data Term = Record [RItem]
| Tuple [TItem]
| B Bool
| N Int
| S String
deriving(Eq, Show)
match :: Term -> Term -> Term
match (Tuple _) (Tuple []) = Record []
match (Tuple []) (Tuple _) = Record []
match (Tuple (x:xs)) (Tuple (z:zs)) = [(x,z)] ++ match (Tuple xs) (Tuple zs)
但是,在编译上面的代码时,会显示以下错误:
应该在此程序中编写和使用的代码示例如下:
> t1 = Tuple [(S "a"), (S "b")]
> t2 = Tuple [(N 10), (N 20)]
> match t1 t2
有效结果应为Record :: Record [("a", 10), ("b", 20)]
。
答案 0 :(得分:1)
x
是Term
但是为了返回您想要的类型,您需要它是Label
。所以你需要在标签上进行模式匹配。
match (Tuple (S x : xs)) (Tuple (z : zs)) = [(x, z)] ++ match (Tuple xs) (Tuple zs)
但这提出了一个问题:如果我通过match (Tuple [B True]) (Tuple [N 10])
该怎么办?根据您的类型,我们不能为键定义带有布尔值的Record
。该程序应该崩溃吗?或者我们应该得到一些“默认”行为?您可以考虑更改匹配,以便将[Label]
作为其第一个参数,而不是通用Term
。这样就无需进行所有这种模式匹配,因此它更加清晰,并且阻止人们将无意义的数据传递给它,因此它更加正确。
如果出于某种原因,您需要人员能够将普通Term
传递给match
,您可以考虑返回Maybe
,以便您可以优雅地处理失败案例。
答案 1 :(得分:1)
问题在于
行match (Tuple (x:xs)) (Tuple (z:zs)) = [(x,z)] ++ match (Tuple xs) (Tuple zs)
递归调用的结果将是Term
,因此您无法将列表并置运算符++
应用于它。此外,这种连接的结果将是一个列表,而不是Term
,因此您最终会得到错误的结果类型。
这里的基础操作实际上只是标准
zip :: [a] -> [b] -> [(a,b)]
所以让我们建立在那个:
match (Tuple labels) (Tuple terms) = Record (zip labels terms)
正如其他人所说,这有点奇怪,因为它会因非Tuple
值而失败。所以你可能想要更像
match2 :: [Label] -> [Term] -> Term
match2 labels values = Record (zip labels values)
但这仍然令人惊讶,因为从不同长度的标签和值列表构建记录似乎没什么意义。也许你可以使用
match3 :: [Label] -> [Term] -> Maybe Term
match3 ls ts = Record <$> zipSame ls ts
zipSame :: [a] -> [b] -> Maybe [(a,b)]
zipSame [] [] = Just []
zipSame (l:ls) (t:ts) = ((l,t):) <$> zipSame ls ts
zipSame _ _ = Nothing