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

コンパイル時エラーが実行時エラーに変わるため、ほとんどの状況ではこれをお勧めしませんが、これは polyvariadic を実装する方法です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

2 つのアプローチの比較

最終的には、これが表すエラーの種類によって異なります。このようなエラーが発生した場合、プログラムの実行が意味のある方法で続行できなくなる場合は、最初のアプローチ (またはそれに似たもの) が 2 番目のアプローチよりも適切である可能性があります。エラーから回復する方法がある場合は、最初の方法よりも 2 番目の方法の方が適しています

そもそも多変量法を使用する必要があるかどうかは別の問題です。多変量の追加の複雑さを避けるために、プログラムを再構築する方が良いかもしれません。

于 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 に答える