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

まずデータ構造。Mydata を印刷できるように、show を派生させました。-}

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

{-あなたが IO モナドをひどく使いすぎていると感じたのでsomedata、定義を本体に入れました (let a =中にありました)。mainデバッグがはるかに簡単になるため、できるだけ純粋な世界で行うことを試みる価値があります。実際の問題では、どこかから読み込んでいるかもしれませんがsomedata、関数を記述するためには、このようなテスト データを少し置いておくと大きなメリットがあります。(ただし、ここでは定義として 1 回だけ言及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 MydatamaybeRead :: Read a => String -> Maybe aJustNothingEitherRight

私はこれを 3 つの方法で書いていきます。-}

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を返しJustますが、何か他のことが起こった場合、そのうちの 1 つが であったNothingためMydataNothing.

map readMydata_v1 somedatagchi でと を試してみてくださいmap readMydata_v1 somewrong

という式を使用したため、パターン内で 、、およびMydata a b cの型がabおよびcであることに注意してください。そのパターンは の出力であり、 の 3 つの用途の型を強制的に正しいものにします - 個々の型シグネチャを与える必要はありません。型シグニチャは非常に便利ですが、関数の途中にあるわけではありません。FloatIntFloat(Just a, Just b, Just c)(maybeRead as, maybeRead bs, maybeRead cs)maybeRead

今は を使うのが好きですが、お互いMaybeにたくさんのステートメントを書くのは好きではないので、 . である面を使うことができました。モナドの詳細については、Learn You a Haskell for Great Good http://learnyouahaskell.comを参照してください。ただし、ここでの私の目的のために、そうでなくても値をそのように扱うことができるようにしています。-}caseMaybeMonad MaybeIO

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をすべて取得し、それがすべて機能した場合は を取得します。モナドは停止して戻ることで得られるすべての を処理し、正解をでラップします。bbsccsJust $ Mydata a b cMaybeNothingNothingJust

これは非常に優れていますが、あまり関数型プログラミングApplicativeとは思えません。http://learnyouahaskell.comApplicativeを読む必要がありますが、今はそのまま使用してみましょう。

書いている自分を見つけたときはいつでも

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

「アプリカティブファンクター」をよりきれいに使用できるときにモナドを使用していることを意味します。これが実際に意味することは、あなたがそれを書くことができたということです

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

something xand somethingelse yandの結果を見つけて、その結果に適用することを意味anotherthing x zyetmore y y zますsomefunction

応用的な方法でやってみましょうreadMydata: -}

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

{- ああああ、とてもきれいで、とても機能的で、とても簡単です。うーん。:) もっと考えて、書くのを減らしましょう。

これは、andmaybeRead aの結果を取得し、結果に適用することを意味しますが、すべてがであるため、途中で何かが である場合、答えは になります。maybeRead bmaybeRead cMydataMaybeNothingNothing

map readMydata_nice somedata繰り返しますが、またはを使用して ghci でこれをテストできます。map readMydata_nice somewrong

とにかく、mainこれもより機能的になりました。-}

main = mapM_ print $ catMaybes $ map readMydata_nice somedata

{- これは、文字列の各リストを取り込んでsomedataとして読み取り、 s をMaybe Mydata捨ててコマンドに変換し、次々と実行します。 少し似ていますが、作成するすべてのことを行います。複数の s であるため、それぞれが別の行に表示され、読みやすくなっています。NothingIO printmapM_mapIOprint

ここでは、値catMaybesを無視して、機能したものだけを出力することにしました。Nothing実際のプログラムでは、私が言ったEitherように使用して、間違ったデータを黙って無視する代わりにエラー メッセージを渡すことができるようにします。私たちが使用したすべてのトリックはMaybeEither. -}

于 2012-08-30T00:46:49.897 に答える
1

定義では

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

xs定義の左側と右側で使用しています。したがって、両側で同じタイプにする必要があります。xsコンパイラは の型がisであると想定し、その型の値にMyData適用することはできません。read

于 2012-08-29T15:19:43.453 に答える