读取和表示指定要使用的数据类型的输入 [英] Reading and representing input which specifies the data type to use

查看:106
本文介绍了读取和表示指定要使用的数据类型的输入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



例如,我们假设可能有像这样的用户输入:

p>

 整数对1 2 
整数三元组1 2 3
实数对1 2
实数三元组1 2 3

并且有一个数据类型来表示它:

  data(MValue a)=> T a = TP(Pair a)| TT(Triple a)
导出(Show,Eq)

数据Pair a = Pair aa导出(Show,Eq)
数据Triple a = Triple aaa导出(Show,Eq )

允许的值类型必须属于 MValue class:

  class(Num a,Read a)=> MValue a where 
typename :: a - >字符串
readval :: [String] - >也许是

实例MValue Int其中
typename _ =integer
readval [s] = maybeRead s
readval _ = Nothing

实例MValue Double其中
typename _ =real
readval [s] = maybeRead s
readval _ = Nothing

maybeRead s =
case读取
[(x,_)] - >只需x
_ - > Nothing

我可以轻松地为 Pair s和 Triple s:

  readPair(w1:w2:[] )=对< $> maybeRead w1 * * maybeRead w2 
readTriple(w1:w2:w3:[])= Triple< $> maybeRead w1 * *可能读取w2 * maybeRead w3

问题是如何为整个<$ c编写一个多态读取器
$ b $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $'阅读a)=>字符串 - >也许(T a)

我想要:


  1. 类型 a 由调用者选择。
  2. readT 如果用户的输入与 a 不兼容,
  3. $ b应产生 Nothing 如果输入有效,$ b
  4. readT 应该产生 Just(T a)。 b $ b
  5. 数字应根据输入读取为整数或双精度。

幼稚的执行方式

  readT ::(MValue a,Read a)=>字符串 - >可能(T a)
readT s =

(tp:frm:rest) - >
if tp / = typename(undefined :: a)
then Nothing
else case frm
pair - > TP< $> readPair rest
triple - > TT< $> readTriple休息
_ - > Nothing
_ - > Nothing

会在行中发生错误if tp / = typename(undefined: :a)

  rd.hs:45:17:
歧义类型变量约束中的'a':
`在'rd.hs:45:17-41
'中使用'typename'引起的'MValue a'可能的修复:添加一个修复这些类型变量的类型签名s)
失败,模块加载:无。

如果删除此检查,错误消失,但我如何验证用户输入是否兼容与调用者选择的数据类型?一个解决方案可能是单独的 readTInt readTDouble ,但我想要相同的 readT 以多种方式工作,方式与 read 相同。

解决方案

问题是 undefined :: a 中的 a 不一样 a 作为 readT 的签名。 GHC中有一种语言扩展可以实现,称为ScopedTypeVariables。一个更便携的解决方法是引入一些额外的代码来明确地将类型绑定在一起,例如:

  readT ::( MValue a,阅读a)=>字符串 - >可能(T a)
readT s =结果
其中
结果=

(tp:frm:rest) - >
if tp / = typename((const :: a - > Maybe(T a) - > a)undefined result)
then Nothing
else $ fr
对 - > TP< $> readPair rest
triple - > TT< $> readTriple休息
_ - > Nothing
_ - >没有什么

这是对代码的一个非常快速和肮脏的修改,而我的更改可能是做得更优雅,但应该有效。


I'd like to read some data which itself specifies the data type to use.

For example, let's assume there may be user inputs like these:

integer pair 1 2
integer triple 1 2 3
real pair 1 2
real triple 1 2 3

and there is a data type to represent it:

data (MValue a) => T a = TP (Pair a) | TT (Triple a)
  deriving (Show, Eq)

data Pair a = Pair a a deriving (Show, Eq)
data Triple a = Triple a a a deriving (Show, Eq)

where the allowed value types have to belong to MValue class:

class (Num a, Read a) => MValue a where
  typename :: a -> String
  readval  :: [String] -> Maybe a

instance MValue Int where
  typename _ = "integer"
  readval [s] = maybeRead s
  readval _   = Nothing

instance MValue Double where
  typename _ = "real"
  readval [s] = maybeRead s
  readval _   = Nothing

maybeRead s =
  case reads s of
    [(x,_)] -> Just x
    _       -> Nothing

I can easily write readers for Pairs and Triples:

readPair (w1:w2:[]) = Pair <$> maybeRead w1 <*> maybeRead w2
readTriple (w1:w2:w3:[]) = Triple <$> maybeRead w1 <*> maybeRead w2 <*> maybeRead w3

The problem is how do I write a polymorphic reader for the entire T a type?:

readT :: (MValue a, Read a) => String -> Maybe (T a)

I want:

  1. The type a is chosen by the caller.
  2. readT should produce Nothing if the user's input is incompatible with a.
  3. readT should produce Just (T a) if the input is valid.
  4. Numbers should be read as integers or as doubles depending on the input.

A naive implementation

readT :: (MValue a, Read a) => String -> Maybe (T a)
readT s =
  case words s of
    (tp:frm:rest) ->
        if tp /= typename (undefined :: a)
           then Nothing
           else case frm of
             "pair" -> TP <$> readPair rest
             "triple" -> TT <$> readTriple rest
             _ -> Nothing
    _ -> Nothing

gives an error in the line if tp /= typename (undefined :: a):

rd.hs:45:17:
    Ambiguous type variable `a' in the constraint:
      `MValue a' arising from a use of `typename' at rd.hs:45:17-41
    Probable fix: add a type signature that fixes these type variable(s)
Failed, modules loaded: none.

The error goes away if I remove this check, but how can I verify if the user input is compatible with the data type chosen by the caller? A solution might be to have separate readTInt and readTDouble, but I'd like the same readT to work polymorphically the same way as read does.

解决方案

The problem is that the a in undefined :: a is not the same a as the ones in readT's signature. There is a language extension available in GHC that enables that, called "ScopedTypeVariables". A more portable fix would be to introduce a little extra code to explicitly tie the types together, for example:

readT :: (MValue a, Read a) => String -> Maybe (T a)
readT s = result
  where
    result = 
      case words s of
        (tp:frm:rest) ->
            if tp /= typename ((const :: a -> Maybe (T a) -> a) undefined result)
               then Nothing
               else case frm of
                 "pair" -> TP <$> readPair rest
                 "triple" -> TT <$> readTriple rest
                 _ -> Nothing
        _ -> Nothing

This is a very quick and dirty modification of your code, and I'm the changes could be made more elegantly, but that should work.

这篇关于读取和表示指定要使用的数据类型的输入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆