2017-01-28 7 views
0

私は非常に単純な引数パーザをthis exampleでパターン化しようとしています。 最初の引数はdoubleでなければならず、2番目の引数は整数でなければなりません。両方の型でなければelseで指定された両方のデフォルト引数を使いたいと思います。私はApplicativeを使用してoptparseは風デザインと引数を解析するために、より洗練された方法があります理解getArgsの結果を解析する際のシンプルなタイプのエラー

parseArgs :: [String] -> (Double, Integer) 
parseArgs args = do 
    if length(args) == 2 then do 
    let v1 = read (args !! 0) :: Double 
    let v2 = read (args !! 1) :: Integer 
    return (v1, v2) 
    else do 
    let v1 = read ("0.5") :: Double 
    let v2 = read ("5") :: Integer 
    return (v1, v2) 

が、私はまだありませんよ。ここに私が持っているものです。

myscript.hs:186:5-31: error: … 
    • Couldn't match type ‘(Double, Integer)’ with ‘Integer’ 
     Expected type: (Double, Integer) 
     Actual type: (Double, (Double, Integer)) 
    • In a stmt of a 'do' block: return (v1, v2) 

私はこのことを理解していない:

は、ここに私が取得エラーです。 getArgsのシグネチャを見ると、私は自分のintを返すことができないという奇妙なものは見当たりません。それともIntegerではなく(Double,Integer)を返さなければなりません。

これを正しく行うにはどうすればよいですか?

+2

'return'は'からの機能である - 'メートル> ma'、 'はモナドです。これは 'do'構文の一部ではありません。値 '(v1、v2) 'を得るために'(v1、v2) 'を書くだけです。 – Ryan

+0

ありがとうございます。私はlet文しか持っていなかったので、コンパイラはdoブロックの最後の行を式にしなければならず、コンパイルすることができたと私に言いました。私は以下の答えに基づいて、今この行動をよりよく理解しています。 – Mittenchops

+1

与えられた文字列が適切な型に解析できないとき、 'read'はエラーをスローし(そしてプログラムをクラッシュさせることに注意してください)。それを改善したいなら、 '' ReadMaybe'(https://hackage.haskell.org/package/base-4.9.1.0/docs/Text-Read.html#v:readMaybe)を 'Text .Read'は、失敗したときに 'Nothing'を出力します。 – duplode

答えて

5

それはあなたがreturnは、関数からコンテンツを返すために、キーワードのようだ「不可欠」世界でいくつかの経験を持っているように私には見えます。

ただし、Haskellでは、returnステートメントを使用してモナドを定義/使用します。途中でdoブロックと同じです。あなたのタイプ(Int,Double)は、モナディックタイプとして使用できます(そのためには@duplode)。しかし、この文脈では、単純な関数のように見えるので、モナドを使いたい/しなければならないようには見えません。

だから、あなたが問題を解決することができます:だからあなたはあなたが式の中などv1v2を、使用することを言ってinを使用

parseArgs :: [String] -> (Double, Integer) 
parseArgs args = 
    if length(args) == 2 then 
    let v1 = read (args !! 0) :: Double 
     v2 = read (args !! 1) :: Integer 
    in (v1, v2) 
    else 
    let v1 = read ("0.5") :: Double 
     v2 = read ("5") :: Integer 
    in (v1, v2) 

。それでもそれはあまり「Haskell-ish」ではありません。これを行うには良い方法は、警備員を使用している(if-elseをドロップ):

parseArgs :: [String] -> (Double, Integer) 
parseArgs args | length args == 2 = 
        let v1 = read (args !! 0) :: Double 
         v2 = read (args !! 1) :: Integer 
        in (v1, v2) 
       | otherwise = 
        let v1 = read ("0.5") :: Double 
         v2 = read ("5") :: Integer 
        in (v1, v2) 

あなたは、これらすべてのlet文を使用して、とにかく種類を指定し、なぜ最後に、私は実際には表示されません。

これで、まだ完了していません。 argsは長さが2であるため、形状が[a,b]であることを意味します。私たちは、頭の中でそのパターンを使用することができますチェックやマッチングはいわば同時に行われます

parseArgs :: [String] -> (Double, Integer) 
parseArgs [a,b] = (read a, read b) 
parseArgs _  = (read "0.5", read "5") 

利点は、あなたがもはや番目の要素を取得するために(!!)を使用する必要はありませんということです。私が提案する

最後の改善は、第二のケースでreadを省略することです:あなたは、単に0.55を入力することができます。

parseArgs :: [String] -> (Double, Integer) 
parseArgs [a,b] = (read a, read b) 
parseArgs _  = (0.5, 5) 
+2

実際には、 'Writer'のように動作する' Monoid a => Monad((、)a) 'インスタンスがあります。これは、GHCがネストされたペアであると報告された "実際のタイプ"で、OPが持っているcounterintuitveタイプエラーを説明します。 – duplode

+1

@duplode:ああ、説明しています。これは純粋な関数なので、ここでもモナドを使うのは直観に反します。ありがとう。 –

+1

確かに、OPには確かに作家のモナドは必要ありません。 – duplode

6

ここでは、モナドタイプを扱うため、do表記を使用する必要はありません。あなただけの入力リストに一致させることができます。

parseArgs :: [String] -> (Double, Integer) 
parseArgs [d, i] = (read d, read i) 
parseArgs _ = (0.5, 5) 
関連する問題