1

你好哈斯凯勒人,

我从一个月开始学习 Haskell,我正在努力为个人数据类型创建自定义读取实例。

我遵循了这个Learn Yourself a Haskell 中的相关章节,这是我的代码片段。

data Position      =  Position (Absc,Ordn) deriving (Show)
instance Read (Position) where
readsPrec _ input =
    let absc = List.filter (/='(') $ takeWhile (/=',')
        ordn = List.filter (/=')') $ tail (dropWhile (/=',') )
    in (\str -> Position ( (read (absc str) :: Int)
                       , (read (ordn str) :: Int) ) ) input
type Absc = Int
type Ordn = Int

我的目标是解析输入"(1,3)"以输出类似Position (1,3)

但是,我收到以下错误消息:

• Couldn't match expected type ‘[Char]’
              with actual type ‘[Char] -> [Char]’
• Probable cause: ‘takeWhile’ is applied to too few arguments
  In the second argument of ‘($)’, namely ‘takeWhile (/= ',')’
  In the expression: filter (/= '(') $ takeWhile (/= ',')
  In an equation for ‘absc’:
      absc = filter (/= '(') $ takeWhile (/= ',')

ordn 函数也一样。

• Couldn't match expected type ‘[(Position, String)]’
              with actual type ‘Position’
• In the expression:
    (\ str
       -> Position ((read (absc str) :: Int), (read (ordn str) :: Int)))
      input
  In the expression:
    let
      absc = filter (/= '(') $ takeWhile (/= ',')
      ordn = filter (/= ')') $ tail (dropWhile (/= ','))
    in
      (\ str
         -> Position ((read (absc str) :: Int), (read (ordn str) :: Int)))
        input
  In an equation for ‘readsPrec’:
      readsPrec _ input
        = let
            absc = filter (/= '(') $ takeWhile (/= ',')
            ordn = filter (/= ')') $ tail (dropWhile (/= ','))
          in
            (\ str
               -> Position ((read (absc str) :: Int), (read (ordn str) :: Int)))
              input

似乎我的let声明没有将abscand识别ordn为函数(或者至少尝试直接应用它们,而我只想将它们定义为部分应用的函数,以便稍后在参数处应用它们str)。我也可能弄乱了我的Position值构造函数。

我对 Haskell 编码风格不熟悉,可能使用了一些我不完全理解的关键字和工具。你能提示我如何编写它以使其工作吗?

先感谢您。

4

2 回答 2

6

您的代码中有很多错误-我认为您应该阅读一下两者之间的区别,$并且.在处理函数时了解这一点很重要,您的大多数错误是由于尝试将函数应用于其参数($)而不是连接函数(.)

这就是我将您的代码修改为至少类型检查的内容-尽管我认为readsPrec不应该保留原始输入,而是在解析完成后保留剩余的字符串。

修复代码

instance Read Position where
  readsPrec _ input =
      let absc = filter (/='(') . takeWhile (/=',')
          ordn = filter (/=')') . tail . dropWhile (/=',')
       in [(Position ( read (absc input) :: Int
                     , read (ordn input) :: Int), input)]

但是看到这让我不高兴——haskell 不是说是抽象的、圆滑的和真正富有表现力的吗?让我们再试一次

整理

instance Read Position where
  readsPrec _ str = let ('(':absc,',':ordn') = break (==',') str
                        (ordn,')':str') = break (==')') ordn'
                     in [(Position (read absc, read ordn), str')]

更好的是,我们捕捉到应该在开头和结尾都有括号,但还是有点麻烦。

使用现有功能

知道Tuples 已经是Read

instance Read Position where
  readsPrec s str = [(Position x, rest) | (x,rest) <- readsPrec s str]

很好,但我们仍然可以做得更好

假设我们最近对元组进行了一些研究,发现了一个非常方便的模块Data.Bifunctor,它具有函数firstsecond转换 2 元组的第一个和第二个组件(实际上是任何双函子)。

我们可以将上述简化为

instance Read Position where
  readsPrec s = map (first Position) . readsPrec s

干净而简短。

于 2017-04-23T13:16:00.457 回答
0

使用show "(3,4)"::Position退货Position (3,4)。非常感谢您的详细回答。我倾向于混淆$.表示,但是在我浏览了文档之后,它变得清晰了。

于 2017-04-23T15:05:46.153 回答