Haskell - 也许和递归

R. *_*old 2 haskell maybe

我想拿一个字符串然后变成一个列表Direction.例如,"UDDUD"应该返回[U,D,D,U,D],而任何不包含UD返回的字符串Nothing(例如"UDYD"返回Nothing).

data Direction = U | D
    deriving (Show, Eq)
-- where U is Up and D is Down

findDirection :: [Char] -> Maybe [Direction]
findDirection [] = Nothing
findDirection ['U'] = Just [U]
findDirection ['D'] = Just [D]
findDirection (x:xs)
    | x == 'U' = Just (U : findDirection xs)
    | x == 'D' = Just (D : findDirection xs)
    | otherwise = Nothing
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

Couldn't match expected type ‘[Direction]’
                with actual type ‘Maybe [Direction]’
    In the second argument of ‘(:)’, namely ‘findDirection xs’
    In the first argument of ‘Just’, namely
      ‘(U : findDirection xs)’

Test_findDirection.hs:8:32:
    Couldn't match expected type ‘[Direction]’
                with actual type ‘Maybe [Direction]’
    In the second argument of ‘(:)’, namely ‘findDirection xs’
    In the first argument of ‘Just’, namely
      ‘(D : findDirection xs)’
Run Code Online (Sandbox Code Playgroud)

据我了解,Just (D : findDirection xs)Just (U : findDirection xs)有型的[Direction]?为什么会这样?我做错了什么,在这里?

Zet*_*eta 5

Just (D : findDirection xs)并且Just (U : findDirection xs)是[方向]类型?为什么会这样?我做错了什么,在这里?

不,Just (D : findDirection xs)实际上是不合格的.让我们剖析这个错误信息:

Couldn't match expected type ‘[Direction]’
                with actual type ‘Maybe [Direction]’
Run Code Online (Sandbox Code Playgroud)

我们正在使用Maybe [Direction]我们应该使用的地方[Direction].

In the second argument of ‘(:)’, namely ‘findDirection xs’
Run Code Online (Sandbox Code Playgroud)

啊哈.我们用(:) :: a -> [a] -> [a]错了.毕竟,findDirection将返回一个Maybe [Direction],而不是一个[Direction].我们需要这样的东西:

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

现在你的功能可以写成

findDirection (x:xs)
    | x == 'U' = consOnMaybe U (findDirection xs)
    | x == 'D' = consOnMaybe D (findDirection xs)
    | otherwise = Nothing
Run Code Online (Sandbox Code Playgroud)

或者,我们可以使用consOnMaybe x = fmap (x:).作为额外的奖励,这里是一个使用预定义函数而没有显式递归的变体(练习:了解它是如何工作的)

findDirection :: [Char] -> Maybe [Direction]
findDirection [] = Nothing
findDirection xs = traverse toDirection xs

toDirection :: Char -> Maybe Direction
toDirection 'U' = Just U
toDirection 'D' = Just D
toDirection  _  = Nothing
Run Code Online (Sandbox Code Playgroud)