Max*_* B. 7 monads haskell either
我尝试通过以下要求找到自己锻炼的解决方案:
以下是可能的操作:F,L,R
然后一个序列由一个字符串表示,如下所示:
"FFLRLFF"
我想解析上面的序列(并处理错误),然后将每个动作绑定到一个函数,如下所示:
parseAction :: Char -> Either String (a -> a)
parseAction 'F' = Right moveForward
parseAction 'L' = Right turnLeft
parseAction 'R' = Right turnRight
parseAction s = Left ("Unkown action : " ++ [s])
-- Implementation omitted
moveForward :: a -> a
turnLeft :: a -> a
turnRight :: a -> a
Run Code Online (Sandbox Code Playgroud)
现在我想要的是具有以下签名的东西:
parseSequence :: String -> Either String [(a -> a)]
Run Code Online (Sandbox Code Playgroud)
我想通过多次使用parseAction函数来解析一个完整的序列,并在返回Left时失败.我坚持不知道如何实现这个功能.
你有什么想法 ?
ama*_*loy 11
这看起来像
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
Run Code Online (Sandbox Code Playgroud)
哪里
a ~ Char
b ~ (a -> a)
m ~ (Either String)
Run Code Online (Sandbox Code Playgroud)
所以实现很简单:
parseSequence :: String -> Either String [a -> a]
parseSequence = mapM parseAction
Run Code Online (Sandbox Code Playgroud)
顺便说一句,请注意您parseAction并不真正想要使用类型(a -> a),类型必须适用于调用该函数的人选择的任何类型a.您反而希望它使用类型(Location -> Location),其中Location是您用来表示您正在移动的对象的位置的任何类型.
与mapM等效,您可以(如duplode所示)使用遍历,这稍微更通用.最近版本的GHC遍历是Prelude; 在旧版本中,您可能必须从Data.Traversable导入它才能使用它.
注意:为了更加清晰,我将使用Thing -> Thing而不是a -> a.
你需要traverse:
traverse parseAction
:: Traversable t => t Char -> Either String (t (Thing -> Thing))
Run Code Online (Sandbox Code Playgroud)
在你的情况,t是[]等,t Char是String.traverse parseAction将走过String,为每个生成一个动作Char并收集结果.traverse使用处理s和s 的Applicative实例,在第一个停止.EitherLeftRightLeft
PS:mapM在合金的答案是,在这种情况下,相当于traverse.它们之间的唯一区别traverse是更通用,因为它只需要Applicative(而不是Monad)在遍历时使用的仿函数.