1

我正在尝试解析日期,例如09/10/2015 17:20:52

{-# LANGUAGE FlexibleContexts #-}

import Text.Parsec
import Text.Parsec.String
import Text.Read
import Control.Applicative hiding (many, (<|>))

data Day = Day
  { mo  :: Int
  , dy  :: Int
  , yr  :: Int
  } deriving (Show)

data Time = Time
  { hr  :: Int
  , min :: Int
  , sec :: Int
  } deriving (Show)

day  = listUncurry Day  <$> (sepCount 3 (char '/') $ read <$> many digit)
time = listUncurry Time <$> (sepCount 3 (char ':') $ dign 2             )

dign :: (Stream s m Char, Read b) => Int -> ParsecT s u m b
dign = (read <$>) . flip count digit

-- how generalize to n?
listUncurry h [x1,x2,x3] = h x1 x2 x3

sepCount n sep p = (:) <$> p <*> (count (n-1) $ sep *> p)

我有一种预感,某种zipWithN会概括listUncurry。也许某种foldl ($)

作为一个附带问题(出于好​​奇),parsec解析器可以生成使用吗?

4

2 回答 2

7

实际上,您只需要Functor

listUncurry :: Functor f => (a -> a -> a -> r) -> f [a] -> f r
listUncurry h p =
  (\[x, y, z] -> h x y z) <$> p

Functor对我来说,只有当您有如下代码模式时才需要提示:

do x <- m
   return (f ...)

这相当于

m >>= (\x -> return (f ...))

这与

fmap (\x -> f ...) m

这是因为单子定律暗示了这种身份

fmap f xs  =  xs >>= return . f

多元变量listUncurry

在大多数情况下,我并不真正推荐这样做,因为它会将编译时错误转换为运行时错误,但这是实现多变量的方式listUncurry

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances     #-}

class ListUncurry a x r where
  listUncurry :: a -> [x] -> r

instance ListUncurry k a r => ListUncurry (a -> k) a r where
  listUncurry f (x:xs) = listUncurry (f x) xs
  listUncurry _ _      = error "listUncurry: Too few arguments given"

instance ListUncurry r a r where
  listUncurry r [] = r
  listUncurry _ _  = error "listUncurry: Too many arguments given"

如果您也使用它,您将需要大量显式类型注释。可能有一种方法可以使用类型族或函数依赖来帮助解决这个问题,但我现在想不出它。由于这可能是可以解决的(至少在一定程度上),在我看来,更大的问题是类型错误从编译时错误变为运行时错误。

示例用法:

ghci> listUncurry ord ['a'] :: Int
97
ghci> listUncurry ((==) :: Int -> Int -> Bool) [1,5::Int] :: Bool
False
ghci> listUncurry ((==) :: Char -> Char -> Bool) ['a'] :: Bool
*** Exception: listUncurry: Too few arguments given
ghci> listUncurry ((==) :: Char -> Char -> Bool) ['a','b','c'] :: Bool
*** Exception: listUncurry: Too many arguments given

更安全的listUncurry

如果您将课程更改为

class ListUncurry a x r where
  listUncurry :: a -> [x] -> Maybe r

并适当地更改实例中的错误情况,您至少会获得一个更好的界面来处理错误。如果您想保留该信息,您还可以将 替换为Maybe区分“太多”和“太少”参数错误的类型。

我觉得这会是一种更好的方法,尽管您需要添加更多的错误处理(Maybe尽管如此FunctorApplicativeMonad接口会变得相当好)。

比较两种方法

这最终取决于这将代表什么样的错误。如果程序执行在遇到此类错误时无法以任何有意义的方式继续执行,那么第一种方法(或类似的方法)可能比第二种方法更合适。如果有任何方法可以从错误中恢复,则第二种方法会比第一种方法更好。

是否应该首先使用多变量技术是一个不同的问题。重组程序以避免多变量内容的额外复杂性可能会更好。

于 2015-09-12T04:19:02.083 回答
2

我也确定我不应该snoc列出清单 - 这样做的正确方法是什么?

以下实现sepCount更有效:

-- | @sepCount n sep p@ applies @n@ (>=1) occurrences of @p@,
-- separated by @sep@. Returns a list of the values returned by @p@. 
sepCount n sep p = p <:> count (n - 1) (sep *> p)
  where (<:>) = liftA2 (:)
于 2015-09-12T11:25:25.687 回答