2016-03-31 14 views
4

を使用して、同じプログラムが、私はプログラムを書いて、例えば:Haskellは、あなたが見ることができるようにモナド

テスト "12 124 212" =右[12、124、212]

テスト「43 243 fs3d 2" =左 "fs3d学習モナドを始めています番号"

test :: String -> Either String [Int] 
test w = iter [] $ words w 
    where 
     iter acc []  = Right (reverse acc) 
     iter acc (x:xs) = if (all isDigit x) then 
           iter ((read x):acc) xs 
          else 
           Left (x++ "is not a number") 

Aではありません。モナドを使って実装する方法を教えてください。

答えて

4

あなたはtraverse/mapMを探していると思います(リストは同じです)。また、あなたは簡略化のためreadEitherを使用することができます。

import Data.Traversable (traverse) 
import Data.Bifunctor (first) 
import Text.Read (readEither) 

test :: String -> Either String [Int] 
test = traverse parseItem . words 

parseItem :: String -> Either String Int 
parseItem x = first (const $ x++" is not a number") $ readEither x 

のでmapMは何をするのでしょうか?基本的には、手動で行ったリストの再帰を実装します。しかし、標準map機能とは異なり、それは単項関数(Either Stringがモナドである我々の場合、中parseItem)をとり、他の後にリストの一歩を適用します。

iter [] = Right [] 
iter (x:xs) = do 
    r <- parseItem x 
    rs <- iter xs 
    return (r:rs) 
+0

は、なぜあなたはreadEitherのための2つの引数をaplicate? @HaskellFun:1つのみ(String)引数を取る。 –

+1

:ありがとう。私は何とかエラーメッセージと文字列を解析することを期待していました。 [驚異的なBifunctors](http://hackage.haskell.org/package/base-4.8.2.0/docs/Data-Bifunctor.html#v:first)を使用して今すぐ修正: – Bergi

+0

それは私のようには機能しませんsoltuion。それは間違った文字列なしで解析エラーを返します –

2

Bergiの答えはちょうどいいです、多分私がお勧めしたい、他のものは末尾再帰アキュムレータはあなたの例ではiter様ループ書かないで

test :: String -> Either String [Int] 
test str = traverse parseNumber (words str) 

parseNumber :: String -> Either String Int 
parseNumber str 
    | all isDigit str = Right (read str) 
    | otherwise  = Left (str ++ " is not a number") 

:あなたはそれが簡単に提示し、この方法を理解するために見つけることができます。代わりに、ライブラリのドキュメントを見て、あなたが望むことをするリスト関数を見つけようとしてください。この場合、Bergiが正しく指摘したように、traverseはあなたが望むものです。しかし、この機能に完全に慣れていくためには、いくらかの検討が必要です。しかしEitherMonadインスタンスとリストの仕事のTraversableインスタンス、この例ではtraverseは次のようにどのように機能するかを与えられた:

-- This is the same as `traverse` for lists and `Either` 
traverseListWithEither :: (a -> Either err b) -> [a] -> Either err [b] 
traverseListWithEither f [] = Right [] 
traverseListWithEither f (a:as) = 
    case f a of 
     Left err -> Left err 
     Right b -> mapEither (b:) (traverseListWithEither f as) 

-- This is the same as the `fmap` function for `Either`  
mapEither :: (a -> b) -> Either e a -> Either e b 
mapEither f (Left e) = Left e 
mapEither f (Right a) = Right (f a) 
関連する問題