使用 maybe for pattern-matching of list 呈现错误

Using maybe for pattern-matching of list renders error

分解模式时应该如何使用Just构造函数?
E.G:
如果我的模式是:(x1,x2,x3,....xn) 我将不得不用它的 Just 将模式的每个元素括起来?

我的问题:我正在尝试实现 Init 函数 "safely"。
我是否必须使用 Just尾巴和头部也在第二行?

safeInit::[a]->Maybe [a]
safeInit (x:xs)=x: safeInit (Just xs) #Just x : safeInit Just xs ?
safeInit [x,y]=Just [x]
safeInit _ =Nothing

这里有两个问题:

  • 您使用输入 a Maybe [a] 拨打电话;和
  • 您的第一个模式包含一个(严格的)超集,然后是第二个,因此第二个 永远不会 触发。

对于第一个问题,罪魁祸首在行中:

safeInit (x:xs) = x: safeInit (Just xs)

这里 safeInit,需要一个列表(类型 [a]),但是你将列表包装在 Just 构造函数中,因此你传递给它一个 Maybe [a] 值,而 safeInit 无法处理这个。所以我们可以改写为:

safeInit (x:xs) = x : safeInit xs

然而,这 不会 解决问题,因为现在我们在 a 上调用 "cons" (:),并且 Maybe [a],同样,函数无法处理这个。我们首先必须检查递归调用是否产生 safeInit,然后在 x 前面加上 Just,我们可以用 fmap 来做到这一点:

safeInit (x:xs) = fmap (x:) (safeInit xs)

对于第二个问题,我们可以重新排列从句:

safeInit :: [a] -> Maybe a
safeInit [x, _] = Just [x]
safeInit (x:xs) = fmap (x:) (safeInit xs)
safeInit [] = Nothing

然而,这种方法仍然存在问题:它效率很低,因为我们为所有元素(最后一个除外)解包和包装 Just 元素,前提是它没有优化。此外,如果我们处理一个无限列表,我们将陷入无限循环。我们可以通过使用内部函数来改进它,该函数计算 init,因为我们确定 init 是有效的。例如:

safeInit :: [a] -> Maybe a
safeInit (x:xs) = Just (go x xs)
    where go _ [] = []
          go x (x2:xs) = x : go x2 xs
safeInit [] = Nothing

好吧,这取决于你想要什么语义。在 init 的情况下,一旦找到任何元素,您就会知道结果实际上是 Just,即 Just init_xs。然后您想要将当前的 x 添加到包含的列表中,而不更改 Just 的任何内容。最简单的方法是使用 MaybeFunctor 实例:

safeInit (x:xs) = (x:) <$> safeInit xs

但是请注意,这仅在您放入额外的基本情况时才有效,并且该子句必须在之前通用缺点之一:

safeInit :: [a] -> Maybe [a]
safeInit [_] = Just []
safeInit (x:xs) = (x:) <$> safeInit xs
safeInit []  = Nothing

另一种可能更容易理解的替代方法是 pattern-match 递归结果:

safeInit (x:xs) = case safeInit xs of
      Just init_xs -> Just $ x : init_xs
      Nothing -> Just []
safeInit [] = Nothing

根据 Elmex80s 的建议进行扩展,

safeInit :: [a] -> Maybe [a]
safeInit [] = Nothing
safeInit xs = Just (init xs)