1

我正在尝试将嵌套列表转换为Mydata使用列表理解调用的自定义类型,如下所示:

main = do
    let a = [["12.345", "1", "4.222111"],
             ["31.2", "12", "9.1234"],
             ["43.111111", "3", "8.13"],
             ["156.112121", "19", "99.99999"]]
    let b = foo a
    print b

foo xss = [(xs,xs) | xs <- xss, xs <- xss]
    where
         xs = Mydata (read xs!!0 :: Float) (read xs!!1 :: Int) (read xs!!2 :: Float)

data Mydata = Mydata {valA :: Float, valB :: Int, valC :: Float}

当我运行我的程序时,我收到以下错误:

1.hs:11:28:
    Couldn't match expected type `String' with actual type `Mydata'
    In the first argument of `read', namely `xs'
    In the first argument of `(!!)', namely `read xs'
    In the first argument of `Mydata', namely `(read xs !! 0 :: Float)'

谁能帮我找出问题所在?谢谢。

4

3 回答 3

3

xs列表理解中的定义是(可能是无意的)递归的,没有任何意义。可能的实现如下:

data Mydata = Mydata {valA :: Float, valB :: Int, valC :: Float} deriving Show

-- rely on type inference instead of specifying explicit type for each `read'
dataFromList [a, b, c] = Mydata (read a) (read b) (read c)
dataFromList _ = error "dataFromList: not enough arguments in list"

main = do
    let a = [["12.345", "1", "4.222111"],
             ["31.2", "12", "9.1234"],
             ["43.111111", "3", "8.13"],
             ["156.112121", "19", "99.99999"]]
    let b = map dataFromList a
    -- alternatively
    -- let b = [dataFromList triple | triple <- a]
    print b
于 2012-08-29T15:26:18.150 回答
3

{- 我将借此机会告诉您一些其他我认为从长远来看会有所帮助并解决问题的事情。-}

import Control.Applicative
import Data.Maybe
import Network.CGI.Protocol (maybeRead)

{- Control.Applicative 让我可以使用它们<$><*>它们是处理大量事物的非常方便的功能(稍后会详细介绍)。我打算maybeRead以后用。不知道为什么不在Data.Maybe

先说数据结构。我已经导出了 show 所以我们可以打印 Mydata。-}

data Mydata = Mydata {valA :: Float, 
                      valB :: Int, 
                      valC :: Float}
  deriving Show

{- 我somedata在主体中做了一个定义(你有let a =inside main),因为我觉得你严重过度使用了 IO monad。在纯世界中尽可能多地尝试是值得的,因为它使调试变得更加容易。也许在你的实际问题中,你会somedata从某个地方读到,但是对于编写函数,有一些像这样的测试数据是一个很大的好处。(尝试somedata在这里只提及一次定义,这样你就不会得到大量的全局常量!)-}

somedata = [["12.345", "1", "4.222111"],
            ["31.2", "12", "9.1234"],
            ["43.111111", "3", "8.13"],
            ["156.112121", "19", "99.99999"]]

somewrong = [ ["1",   "2",   "3"     ],    -- OK
              ["1.0", "2",   "3.0"   ],    -- OK, same value as first one
              ["1",   "2.0", "3"     ],    -- wrong, decimal for valB
              ["",    "two",  "3.3.3"] ]   -- wrong, wrong, wrong.

{- 让我们编写一个函数来读取单个Mydata,但是Maybe Mydata如果它不起作用,我们可以优雅地恢复。 maybeRead :: Read a => String -> Maybe a, 所以它将字符串变成Just你想要的,或者Nothing如果它不能给你。这比简单地因错误消息而崩溃要好。(最好还是返回Either一条错误消息来解释问题或Right答案,但我今天将跳过它。)

我打算写这三种方式,越来越好。-}

readMydata_v1 :: [String] -> Maybe Mydata
readMydata_v1 [as, bs, cs] = case (maybeRead as, maybeRead bs, maybeRead cs) of
   (Just a, Just b, Just c) -> Just $ Mydata a b c
   _                        -> Nothing
readMydata_v1 _ = Nothing    -- anything else is the wrong number of Strings

{- 所以我们看一下(maybeRead as, maybeRead bs, maybeRead cs),如果它们都有效,我们就用Mydata它们做 a,然后返回Just正确的答案,但如果发生其他事情,其中​​一个是 a Nothing,所以我们不能做 a Mydata,所以我们得到Nothing了整体.

map readMydata_v1 somedata使用和在 gchi 中尝试一下map readMydata_v1 somewrong

请注意,因为我使用了表达式,Mydata a b c所以它强制在模式中使用abto cbeFloat的类型。该模式是的输出,它强制三种用途的类型是正确的——我不需要给出单独的类型签名。类型签名真的很方便,但它们在函数中间并不漂亮。IntFloat(Just a, Just b, Just c)(maybeRead as, maybeRead bs, maybeRead cs)maybeRead

现在我喜欢使用Maybe,但我不喜欢case在彼此之间编写大量语句,所以我可以Maybe使用Monad. 请参阅Learn You a Haskell for Great Good http://learnyouahaskell.com了解有关 monad 的更多详细信息,但出于我的目的,我可以将Maybe值视为它的值,IO即使它不是。-}

readMydata_v2 :: [String] -> Maybe Mydata
readMydata_v2 [as,bs,cs] = do
      a <- maybeRead as
      b <- maybeRead bs
      c <- maybeRead cs
      return $ Mydata a b c
readMydata_v2 _ = Nothing    -- anything else is the wrong number of Strings

{- 我好像没有写错误处理代码!Maybe单子不是很好!在这里,我们拿走a我们能从中得到的maybeRead as一切,b从阅读中得到的bs 一切c,从我们得到的一切cs,如果这一切都奏效了,我们就会得到Just $ Mydata a b cMaybemonad 处理Nothing我们通过停止和返回得到的任何 s ,Nothing并将任何正确答案包装在Just.

虽然这真的很好,但它感觉不是很函数式编程,所以让我们全力以赴并完成它ApplicativeApplicative您应该在http://learnyouahaskell.com中阅读有关内容,但现在,让我们使用它。

每当你发现自己在写作

foo x y z = do
   thing1 <- something x
   thing2 <- somethingelse y
   thing3 <- anotherthing x z
   thing4 <- yetmore y y z
   return $ somefunction thing1 thing2 thing3 thing4

这意味着当您可以更干净地使用“应用函子”时,您正在使用 monad。这意味着在实践中你可以写成

foo x y z = somefunction  <$>  something x  <*>  somethingelse y  <*>  anotherthing x z  <*>  yetmore y y z

或者,如果您愿意,

foo x y z = somefunction  <$>  something x  
                          <*>  somethingelse y  
                          <*>  anotherthing x z  
                          <*>  yetmore y y z

这更好,因为(a)它感觉更像是普通的函数应用程序(注意,它的<$>工作方式$<*>工作方式都像一个空格)和(b)你不需要发明名称thing1等。

这意味着找出 and 的结果something x,然后somethingelse y应用于结果。anotherthing x zyetmore y y zsomefunction

让我们做readMydata应用方式:-}

readMydata_nice :: [String] -> Maybe Mydata
readMydata_nice [a,b,c] = Mydata <$> maybeRead a <*> maybeRead b <*> maybeRead c
readMydata_nice _       = Nothing

{- Aaaahhhhh,如此干净,如此实用,如此简单。嗯。:) 多想,少写。

这意味着将maybeRead aandmaybeRead bmaybeRead cand 的结果应用于Mydata结果,但是因为一切都是Maybe,如果沿途有任何事情是Nothing,答案将是Nothing

同样,您可以在 ghci 中使用map readMydata_nice somedataor进行测试map readMydata_nice somewrong

无论如何,让我们 write main,它现在也更实用了。-}

main = mapM_ print $ catMaybes $ map readMydata_nice somedata

{- 这会将每个字符串列表somedata读取为Maybe Mydata,然后丢弃Nothings 并将它们转换为IO print命令并一个接一个地执行。 mapM_工作有点像map,但IO它创造的每一个。因为是几个prints,所以每一个都是单独的一行,这样阅读起来要容易得多。

在这里,我决定使用catMaybes忽略这些Nothing值并仅打印有效的值。在一个真正的程序中,我会Either像我说的那样使用,这样我就可以传递一条错误消息,而不是默默地忽略错误的数据。我们使用的所有技巧Maybe也适用于Either. -}

于 2012-08-30T00:46:49.897 回答
1

在定义中

where
  xs = Mydata (read xs!!0 :: Float) (read xs!!1 :: Int) (read xs!!2 :: Float)

xs在定义的左侧和右侧使用。因此,它必须在两侧具有相同的类型。编译器假定的类型xsMyData并且您不能应用于read该类型的值。

于 2012-08-29T15:19:43.453 回答