3

過去の紙の問題が私に尋ねました。リスト内の2つのアイテムごとに交換する関数p::[a]->[a]を定義します。関数は、最初の項目を2番目の項目と交換し、3番目の項目を4番目の項目と交換する必要があります。リスト内包表記によって1つを定義し、再帰によってもう1つを定義します。

さて、これは私が思いついたものです:

import Test.QuickCheck
p :: [a] -> [a]
p [] = []
p xs = concat [ [y,x] | ((x,y),i) <- zip (zip xs (tail xs)) [1..], odd i]
q :: [a] -> [a]
q [] = []
q (x:y:zs) | even (length zs) = [y,x] ++ q zs
           | otherwise = error "The list provided is not of even length"
prop_2 xs = even (length xs) ==> p xs == q xs
check2 = quickCheck prop_2

関数は正常に動作しますが、2つが同一であるかどうかを確認したかったので、以下にquickCheckを配置しました。しかし、これは何らかの理由で「prop_2の使用から生じる曖昧な型変数[a0]」というエラーを私に与えます

私はここで何が悪いのか理解していません、私は私には完全に賢明に見えます... Haskellは正確に何を不平を言っていますか?

4

1 に答える 1

9

コメントアウトしcheck2てGHCiにタイプを尋ねることから始めましょうprop_2

*Main> :t prop_2
prop_2 :: Eq a => [a] -> Property

さて、あなたが書いたようにprop_2、それは等式クラスにある任意の要素タイプのリストに対して機能します。

prop_2ここで、関数に渡しquickCheckます。次のタイプを見てみましょうquickCheck

*Main> :t quickCheck
quickCheck :: Testable prop => prop -> IO ()

この関数は実際には非常に一般的なタイプです。クラスにあるものなら何でも動作しTestableます。Testableでは、このクラスはどのように機能しますか?ここには、いくつかの基本インスタンスがあります。次に例を示します。

instance Testable Bool
instance Testable Property  -- this is a simplification, but it's more or less true

これらのインスタンスはQuickCheckライブラリで定義されており、定数ブール値とタイプPropertyの要素をクイックチェックできることを示しています。

現在、入力に依存しないプロパティのテストは特に興味深いものではありません。興味深い例はこれです:

instance (Arbitrary a, Show a, Testable prop) => Testable (a -> prop)

Arbitrary aつまり、特定のタイプのランダムな値を生成する方法( )とそのタイプの値を表示する方法()を知っている場合は、そのタイプからすでにテスト可能なタイプまでShow a関数をテストすることもできます。aprop

なんで?それがQuickCheckの動作方法だからです。このような状況では、QuickCheckはArbitraryインスタンスを調べて、タイプのランダムなテストケースを考え出しa、それぞれに関数を適用して、結果が肯定的かどうかを確認します。いずれかのテストが失敗すると、テストの失敗を通知するメッセージが出力され、テストケースが出力されます(そのため、Show要件もあります)。

さて、私たちの状況では、これはクイックチェックができるはずであることを意味しますprop_2:それは結果としてProperty。重要なことは、関数の引数(保持[a]されている限りの型)がクラスのメンバーであり、クラスのメンバーであるということです。Eq aArbitraryShow

ここで、エラーの原因に到達します。入手可能な情報は、その結論を出すのに十分ではありません。最初に言ったように、prop_2同等性を認めるあらゆる要素タイプのリストに対して機能します。これらすべてのタイプがとであるという組み込みのルールはありませArbitraryShow。しかし、あったとしても、QuickCheckはどのようなリストを生成する必要がありますか?ブール値のリスト、ユニットタイプのリスト、関数のリスト、文字のリスト、整数のリストを生成する必要がありますか?非常に多くのオプションがあり、要素タイプの選択は、バグを見つけるかどうかに影響を与える可能性があります。(GHCが1つの要素だけでユニットタイプを選択することを考慮して()ください。そうすると、プロパティは任意の2つの関数pq入力リストが目的のスワッピングプロパティを持っているかどうかに関係なく、入力リストの長さを保持します。)

これが、リストに使用する要素タイプを解決できるように、GHCに追加のタイプ情報を提供する必要がある理由です。そうすることは十分に簡単です。prop_2自分自身に注釈を付けることができます。

prop_2 :: [Integer] -> Property

または、それを望まない場合(再実装せずにさまざまな種類のリストでテストを実行したい場合があるためprop_2)、呼び出し時に型アノテーションを追加できますquickCheck

check2 = quickCheck (prop_2 :: [Integer] -> Property)

これでコードがコンパイルされ、次のコマンドを実行できますcheck2

Main*> check2
+++ OK, passed 100 tests.
于 2012-12-15T01:46:49.810 に答える