7

次の要件を使用して、自分の演習で解決策を見つけようとします。

  • 特定のシーケンスに対してオブジェクトを移動する必要があります。
  • シーケンスはアクションで構成されます。

可能なアクションは次のとおりです: F、L、R

  • F : 前方に移動
  • L : 左に 90°回転
  • R : 右に 90°回転

シーケンスは、次のように文字列で表されます。

"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

今私が欲しいのは、次の署名を持つものです:

parseSequence :: String -> Either String [(a -> a)]

関数を何度も使用して完全なシーケンスを解析したいのですparseActionが、それが Left を返すと失敗します。この機能をどのように実装できるかについて、私は立ち往生しています。

あなたはなにか考えはありますか ?

4

3 に答える 3

11

これは次のように見えます

mapM :: Monad m => (a -> m b) -> [a] -> m [b]

どこ

a ~ Char
b ~ (a -> a)
m ~ (Either String)

したがって、実装は単純です。

parseSequence :: String -> Either String [a -> a]
parseSequence = mapM parseAction

余談ですが、parseAction実際には type を使用したくないことに注意してください。これは、関数を呼び出す人が選択した任意のtypeで機能(a -> a)する必要があります。代わりに、 type を使用する必要があります。ここで、 Location は、移動しているオブジェクトの場所を表すために使用しているタイプです。a(Location -> Location)

mapM と同様に、(duplode で提案されているように) 代わりに traverse を使用できます。これは、もう少し一般的です。GHC トラバースの最近のバージョンでは Prelude にあります。古いバージョンでは、Data.Traversable からインポートして使用する必要がある場合があります。

于 2015-09-11T19:57:40.583 に答える
7

注: わかりやすくするために、Thing -> Thingではなくを使用しますa -> a

必要なものtraverse:

traverse parseAction
  :: Traversable t => t Char -> Either String (t (Thing -> Thing))

あなたの場合、tはです。は を歩き回り、それぞれに対して 1 つのアクションを生成し、結果を収集します。のインスタンスを使用してとを処理し、最初の で停止します。[]t CharStringtraverse parseActionStringChartraverseApplicativeEitherLeftRightLeft

PS: mapMamalloy の回答は、この場合、 と同等traverseです。それらの唯一の違いは、トラバース中に使用するファンクターからのみ( ではなく) をtraverse必要とするため、より一般的であることです。ApplicativeMonad

于 2015-09-11T20:05:53.463 に答える