2009-03-26 18 views
5

私は 'print'関数呼び出しをhaskell関数に入れようとしています。haskell:関数内でputStrLnを呼び出そうとしたときにエラーが発生しました

(単純なデバッグメッセージ)。

以下は私のコードとコンパイラのエラーメッセージです(ghc 6.10)。

なぜputstr呼び出しと空の配列を束縛しているのかよくわかりません。

空の配列は、その特定のケースの戻り値です(プリントアウトメッセージは実際にはスタブです)。

これはなぜ機能していないのでしょうか?

おかげ

マイコード:

 
isAFactor :: Integer -> Integer -> Bool 
isAFactor x y = x `mod` y == 0 

findFactors :: Integer -> Integer -> [Integer] 
findFactors counter num = 
    let quotient = div num 2 
    in 
     if(counter > quotient) 
      then do 
       putStrLn ("factorList is : " ++ show quotient) (*** Line 10***) 
       [] 
     else if(isAFactor num counter) 
      then [counter] ++ [quotient] ++ findFactors (counter + 1) num 
     else 
      findFactors (counter + 1) num 

エラーGHC

 
    test.hs:10:4: 
    Couldn't match expected type `[a] -> [Integer]' 
      against inferred type `IO()' 
    In the expression: 
     putStrLn ("factorList is : " ++ show quotient) [] 
    In the expression: 
     do putStrLn ("factorList is : " ++ show quotient) [] 
    In the expression: 
     if (counter > quotient) then 
      do putStrLn ("factorList is : " ++ show quotient) [] 
     else 
      if (isAFactor num counter) then 
        [counter] ++ [quotient] ++ findFactors (counter + 1) num 
      else 
       findFactors (counter + 1) num 
+0

これは、findFactorsは純粋な関数なので、副作用(IOなど)がない可能性があるためだと思います。これがハスケルのことです。私はあなたの関数をデバッグする方法を知りませんでした。 C++では、常にデバッグログを使用しています。 –

+0

"do"は、あなたが何を意味するかを意味するものではありません。モナドシーケンシングのシンタックスシュガーです。あなたは実際にハスケルで何かを "行う"ことはできません。 :-) – ShreevatsaR

答えて

18

からHaskellはpure関数型言語であることを覚えておくことが重要です。これは、デバッグメッセージを画面に出力するなど、機能に何らかの副作用がないことを意味します。

しかし、この純度を壊すことは可能ですし、デバッグにも役立ちます。モジュールDebug.Traceを見てください。ここには、関数trace :: String -> a -> aがあります。あなたはこのようなあなたのコードでそれを使用することができます。

import Debug.Trace 

isAFactor :: Integer -> Integer -> Bool 
isAFactor x y = x `mod` y == 0 

findFactors :: Integer -> Integer -> [Integer] 
findFactors counter num = 
    let quotient = div num 2 
    in 
     if(counter > quotient) 
       then trace ("factorList is: " ++ show quotient) [] 
     else if(isAFactor num counter) 
      then [counter] ++ [quotient] ++ findFactors (counter + 1) num 
     else 
      findFactors (counter + 1) num 

のコメントが示唆したように:

Haskellはまたlazy言語です。 Expressionは、結果が実際に必要となる前に評価されません。トレース機能を使用すると、レイジー設定で少し混乱する可能性があります。なぜなら、トレースメッセージがスクリーンに印刷されたとき(常に印刷されていれば)理解するのは容易ではないからです。

haskellは非常に異なる種類の言語なので、プログラムを同じように異なる方法で試してみることが最善の方法です。 traceと同様の "不確実な"構成を使用するのではなく、機能について推論してみてください。 haskellsの強力な型システムを利用し、(例えば)QuickCheckを使用して、型チェッカーに合格した後に関数をテストする方法を学びます。

+0

ハスケルは怠惰な言語なので、これらの 'trace'呼び出しも遅延して発生することに注意する価値があります。オペレーションの明示的なシーケンシングがないので、 'trace'への呼び出しは順不同であるか、まったく起こっていないように見えるかもしれません。デバッグ時にも、怠惰な言語を扱っていることを忘れないでください。 –

3

問題はHaskellではIOモナドがあるということである明確化

で更新、doで始まるブロックは、モナドの演算子と単項表現(時々文と呼ば)を結合するためのシンタックスシュガーです。この場合、問題のモナドは、putStrLnの呼び出しから推測できるように、IOモナドです。 doブロックの2行目の[]は、実際にはであり、doブロック全体の値はではなく、むしろputStrLnの最後の引数として解釈されます。 2番目の引数を受け入れるわけではありませんが、引用符で囲んだ型のエラーで先に終了するため、コンパイラはこれを理解することさえできません。その行をコマンドにするには、別のモナド関数をその前に置く必要があります(例:return [])。私は、これがあなたの問題を解決するのに役立つとは言っていません。

IOエラーは、IOモナド式のタイプが常にIO _であることに起因します。あなたのケースでは、doブロックにもこのタイプがあります。これは明らかに署名で指定したタイプ[Integer]と互換性がありません。

一般的に、HaskellはモナドIOを備えた純粋な関数言語であるため、IOモナドの中にいったん入ってしまえば、そこから抜け出すことはできません。すなわち、ある関数にIO操作を伴うdoブロックがある場合、その署名は必ずIO _型を含み、この関数を呼び出す他のすべての関数の署名など​​も含みます(他のモナドは "exit"関数を提供しますが、 IOモナドはありません。)

+0

実際にputStrLnの最後の引数として解釈されていませんか?それは(>> =)の第2引数です。 doブロックは必ずしもIO()であるとは限りません。ブロックにはさまざまなタイプがあります。これには[a]! – cthulahoops

+0

ここにはいくつかの誤解があります。私は本物のポストをdownvoteすることは嫌いですが、これはそれが明らかにする以上にあいまいです。 –

+0

@cthulahoops:エラーメッセージ 'In the expression' putStrLn( "factorList is:" ++ show quotient)[] "'は私の解釈が正しいことを示すようです。 –

9

Jonasのポストはあなたの質問をうまくカバーしていますので、私はあなたのfindFactors関数を慣用的に書き直していきます。私が最初に学んだとき、それは私のために役立つことが分かった。

ですから、n/2まで1から各番号を見て、それはnの要因だかどうかをチェックするとしているもののリストを構築することにより、与えられた数nのすべての要因を見つけたいです。

あなたのバージョン(それが仕事を得るために、最小限の変更で):

findFactors :: Integer -> Integer -> [Integer] 
findFactors counter num = 
    let quotient = div num 2 
    in 
     if(counter > quotient) 
      then [] 
     else if(isAFactor num counter) 
      then [counter] ++ findFactors (counter + 1) num 
     else 
      findFactors (counter + 1) num 

それは少し読みやすくするために変更を書式設定のカップル:

findFactors :: Integer -> Integer -> [Integer] 
findFactors counter num 
    | counter > div num 2 = [] 
    | otherwise = if num `isAFactor` counter 
       then counter:findFactors (counter+1) num 
       else findFactors (counter + 1) num 

これは素晴らしいですが、それですいくつかの方法で理想よりも少ない。まず、findFactorsが呼び出されるたびに商を再計算します。これはn/2の除算です(ただし、ghc -O2はこれを実現して1回だけ計算するようです)。第二に、どこでもそのカウンタ変数を処理しなければならないのはちょっと面倒です。第三に、それはまだ不可欠です。

問題を見るもう一つの方法は、整数のリストを1からn/2までとし、係数がnのものだけをフィルタすることです。これはかなり直接Haskellに変換されます:

findFactors :: Integer -> [Integer] 
findFactors num = filter (isAFactor num) [1..(num `div` 2)] 

これは、上記のバージョンと同じパフォーマンス特性を持つことがわかりました。 Haskellはリスト全体のメモリを一度にn/2まで割り当てる必要はなく、必要に応じて各値を生成することができます。

7

他の人があなたのコードを説明してくれているので、エラーメッセージの解読を手伝ってください。

Couldn't match expected type `[a] -> [Integer]' 
     against inferred type `IO()' 
In the expression: 
    putStrLn ("factorList is : " ++ show quotient) [] 

私は他のすべての "表現の中の部分"を見逃してしまった。彼らは文脈をより包括的に示しています。

ハスケルのすべてが式なので、すべてが型を持っています。これには "putStrLn"のようなものが含まれます。あなたが入力した場合「:putStrLn T」GHCiの中に、あなたはそれが返事表示されます。

putStrLn :: String -> IO() 

putStrLnは文字列を取り、この場合の動作である「IOアクション」、返す関数であることを意味していますメッセージを画面に表示します。あなたのコードでは、 "putStrLn"に文字列を指定したので、コンパイラは "putStrLn(stuff)"という型が "IO()"という表現を持つと推測しました。これは、コンパイラエラーメッセージの「推論された型」の部分です。

一方、コンパイラはまた、中に外部から、他の方向に型推論をしていた。とりわけ、それがこの「putStrLn(もの)」という表現があり、空のリストに適用されるように見えたことに気づきました"[a]"(つまり、何かのリスト、私たちは何を知らないか)をタイプします。さらに、式全体の結果は "[Integer]"型でなければなりません。したがって、「putStrLn(stuff)」という表現は、[[]] - > [Integer]と書かれた整数のリストに "[]"を変換する関数でなければなりません。これは、エラーメッセージの「予想されるタイプ」の部分です。

この時点で、コンパイラはこれらの2つのタイプを一致させることができないと判断し、エラーを報告しました。

「推論されたタイプに対する期待型 『フー』と一致しませんでした 『バー』」は、おそらくその価値それを読み、それを理解しようとしているので、Haskellのをコンパイルしようとしたとき、あなたが得る最も一般的なエラーメッセージです。推論された型を見て、引用された式のどの部分がその型を持っているか把握してみてください。次に、コンパイラが周囲のコードを見て何か他のことを期待していた理由を理解しようとします。

+0

+1、エラーメッセージの良い説明。コードがどのようにエラーを報告したのか分かりませんでした。質問者が「行10」のコメントを挿入するために改行を挿入したことがわかりました。 –

関連する問題