type Parser a = String -> Maybe (a, String)
parseChar :: Char -> Parser Char
parseChar _ "" = Nothing
parseChar ch (x:xs) | x == ch = Just (ch, xs)
| otherwise = Nothing
parseAnyChar :: String -> Parser Char
parseAnyChar "" _ = Nothing
parseAnyChar _ "" = Nothing
parseAnyChar (x:xs) str | isJust res = res
| otherwise = parseAnyChar xs str
where res = parseChar x str
我是haskell的初学者,我想知道如何以比“递归循环”更多的“ haskell方式”在parseAnyChar中使用parseChar。例如使用地图或其他找不到的东西。
答案 0 :(得分:2)
是的。一方面,您可以只使用标准函数而不是手动递归来尝试在所有指定的替代方案上使用parseChar
:
import Data.List (find)
parseAnyChar cs str = case find isJust ress of
Just res -> res
Nothing -> Nothing
where ress = [parseChar c str | c<-cs]
...或更短
import Data.Maybe (listToMaybe)
import Control.Monad (join)
parseAnyChar cs str = join . listToMaybe $ (`parseChar`str)<$>cs
更有效的解决方案是首先不要尝试所有选项,而要使用Set
选项:
import qualified Data.Set as Set
parseOneCharOf :: Set.Set Char -> Parser Char
parseOneCharOf _ "" = Nothing
parseOneCharOf cs (x:xs)
| x`Set.member`cs = Just x
| otherwise = Nothing
...或更短
import Control.Monad (guard)
parseOneCharOf cs (x:xs) = guard (x`Set.member`cs) >> Just x
答案 1 :(得分:1)
您可以使用Maybe
的{{3}}实例:
import Control.Applicative((<|>))
parseAnyChar :: Foldable f => f Char -> Parser Char
parseAnyChar xs str = foldr ((<|>) . (`parseChar` str)) Nothing xs
之所以可行,是因为(<|>)
的{{1}}是Alternative
:
Maybe
因此,如果左操作数为instance Alternative Maybe where
empty = Nothing
Nothing <|> r = r
l <|> _ = l
,则选择右操作数;如果左操作数为Nothing
,则选择第一个操作数。
因此,我们可以将Just …
重写为:
parseAnyChar
但是,第二个参数parseAnyChar :: String -> Parser Char
parseAnyChar "" _ = Nothing
parseAnyChar _ "" = Nothing
parseAnyChar (x:xs) str = parseChar x str <|> parseAnyChar xs str
不需要特殊情况,因为""
中的逻辑已经覆盖了该参数。因此,我们可以删除第二个子句:
parseChar
现在我们可以将其重写为parseAnyChar :: String -> Parser Char
parseAnyChar [] _ = Nothing
parseAnyChar (x:xs) str = parseChar x str <|> parseAnyChar xs str
模式。
这也使得可以使用foldr
作为字符源。
如果项目数量很大,则可能需要使用更有效的数据结构,例如implemented as [src]。