创建 Haskell 'init' 函数的安全版本

Pat*_* B. 4 haskell

我正在通过我的方式“真实世界哈斯克尔”,而任务是使安全版本head, tail, last,init. 我已经成功的前三,但Maybe类型类是杀死我的init

这是我的代码:

-- safeInit
safeInit    ::  [a] ->  Maybe [a]
safeInit []     =   Nothing
safeInit (x:xs) =   if null xs
                    then Just [x]
                    else x : (safeInit xs)
Run Code Online (Sandbox Code Playgroud)

这是加载到 GHCI 时产生的错误(该函数从原始文件的第 23 行开始:

[1 of 1] Compiling Main             ( ch04.exercises.hs, interpreted )

    > ch04.exercises.hs:27:26: error:
    >     • Couldn't match expected type ‘Maybe [a]’ with actual type ‘[a]’
    >     • In the expression: x : (safeInit xs)
    >       In the expression: if null xs then Just [x] else x : (safeInit xs)
    >       In an equation for ‘safeInit’:
    >           safeInit (x : xs) = if null xs then Just [x] else x : (safeInit xs)
    >     • Relevant bindings include
    >         xs :: [a] (bound at ch04.exercises.hs:25:13)
    >         x :: a (bound at ch04.exercises.hs:25:11)
    >         safeInit :: [a] -> Maybe [a] (bound at ch04.exercises.hs:24:1)    | 27 |                     else x : (safeInit xs)    |                
    > ^^^^^^^^^^^^^^^^^
    > 
    > ch04.exercises.hs:27:31: error:
    >     • Couldn't match expected type ‘[a]’ with actual type ‘Maybe [a]’
    >     • In the second argument of ‘(:)’, namely ‘(safeInit xs)’
    >       In the expression: x : (safeInit xs)
    >       In the expression: if null xs then Just [x] else x : (safeInit xs)
    >     • Relevant bindings include
    >         xs :: [a] (bound at ch04.exercises.hs:25:13)
    >         x :: a (bound at ch04.exercises.hs:25:11)
    >         safeInit :: [a] -> Maybe [a] (bound at ch04.exercises.hs:24:1)    | 27 |                     else x : (safeInit xs)    |                
    > ^^^^^^^^^^^ Failed, no modules loaded.
Run Code Online (Sandbox Code Playgroud)

无论如何我用 标记或不标记最后两行的x或,我都会得到不同但非常相关的打字错误。我错过了在列表中使用 Maybe 类型的哪些微妙之处?xsJust

Wil*_*sem 7

这不起作用的主要原因是您的表达式x : safeInit xs不会进行类型检查。确实,safeInit xsis a Maybe [a],但(:)有 type (:) :: a -> [a] -> [a],所以类型不匹配。

还有一个语义错误。如果null xsTrue,那么你应该返回Just []而不是Just [x],因为 thenx是列表中的最后一个元素。

您可以使用fmap :: Functor f => (a -> b) -> f a -> f b(so for f ~ Maybe, fmapis fmap :: (a -> b) -> Maybe a -> Maybe b) 来更改包含在 a 中的值Just

safeInit :: [a] -> Maybe [a]
safeInit [] = Nothing
safeInit [_] = Just []
safeInit (x:xs) = fmap (x:) (safeInit xs)
Run Code Online (Sandbox Code Playgroud)

但这将导致在Just. 这也意味着对于无限列表,它将陷入无限循环。我们可以简单地检查列表是否至少包含一个元素,然后执行 init 逻辑作为我们包装在 a 中的函数的结果Just

safeInit :: [a] -> Maybe [a]
safeInit [] = Nothing
safeInit (x:xs) = Just (go xs x)
    where go [] _ = []
          go (x2:xs) x = x : go xs x2
Run Code Online (Sandbox Code Playgroud)

  • 另一个选项是`safeInit [] = Nothing ; safeInit xs = Just (init xs)`。即使它不那么干净,它也很短。 (2认同)