QuickCheck ваш друг. Сейчас самое время начать изучать его.
Вы спрашивали о генерации случайных деревьев. В терминах QuickCheck мы генерируем произвольных деревьев, используя класс типов Arbitrary
.
class Arbitrary a where
arbitrary :: Gen a
coarbitrary :: a -> Gen b -> Gen b
Gen
- это класс типов для генераторов тестовых данных.
Генераторы тестовых данных: Тип Gen
Данные испытаний формируются генераторами данных испытаний. QuickCheck определяет
генераторы по умолчанию для большинства типов, но вы можете использовать свои собственные с
forAll
, и нужно будет определить ваши собственные генераторы для любых новых
типы, которые вы вводите.
Генераторы имеют типы вида Gen a
; это генератор для
значения типа a
. Тип Gen
- это монада, поэтому Haskell's do
Синтаксис и стандартные монадические функции могут быть использованы для определения
генераторы.
Генераторы построены поверх функции
choose :: Random a => (a, a) -> Gen a
, который делает случайный выбор значения из интервала с
равномерное распределение. Например, чтобы сделать случайный выбор между
элементы списка, используйте
do i <- choose (0, length xs-1)
return (xs !! i)
В своем вопросе вы говорите, что хотите генерировать деревья с определенным количеством узлов. Arbitrary
экземпляр для этого
instance Arbitrary Tree where
arbitrary = sized tree'
where tree' 0 = return Empty
tree' n | n > 0 = do
lsize <- choose (0, n - 1)
l <- tree' lsize
r <- tree' (n - lsize - 1)
return $ Node l r
Этот экземпляр учитывает параметр внешнего размера благодаря sized
.
Размер тестовых данных
Генераторы тестовых данных имеют неявный параметр size
; быстрая проверка
начинается с генерации небольших тестовых случаев и постепенно увеличивает
размер по мере тестирования. Различные генераторы тестовых данных интерпретируют
параметр размера по-разному: некоторые игнорируют его, а список
генератор, например, интерпретирует его как верхнюю границу длины
сгенерированных списков. Вы можете использовать его по своему усмотрению.
собственные генераторы тестовых данных. Вы можете получить значение размера
параметр с использованием
sized :: (Int -> Gen a) -> Gen a
sized g
вызывает g
, передавая ему текущий размер в качестве параметра. За
Например, чтобы сгенерировать натуральные числа в диапазоне от 0 до n, используйте
sized $ \n -> choose (0, n)
Внутренний генератор tree'
берет количество оставшихся узлов и создает дерево, создавая Empty
, когда параметр равен нулю, или создавая Node
, чье левое поддерево имеет размер от 0 до n
- 1 ( меньше 1 для учета самого узла) и чье правое поддерево получает остальные элементы.
Скажем, у вас есть функция
nodes Empty = 0
nodes (Node l r) = 1 + nodes l + nodes r
и мы хотим видеть, что он правильно подсчитывает узлы в 10-узловых деревьях. После определения свойства
prop_nodes_count = forAll (resize 10 arbitrary) (\t -> nodes t == 10)
мы успешно протестировали его в GHCi с
λ> quickCheck prop_nodes_count
+++ OK, passed 100 tests.
Приведенный выше код использует resize
для определения размера генерируемых деревьев.
Чтобы продемонстрировать, что вы можете создать чистый список деревьев, , т.е. , [Tree]
, а не IO [Tree]
, мы будем использовать простой standin.
print_trees :: [Tree] -> IO ()
print_trees = print
В конечном счете, генераторы QuickCheck являются случайными и, следовательно, сохраняют состояние, поэтому генерировать их из вашего действия main
очень просто.
main :: IO ()
main = do
trees <- sample' (resize 0 arbitrary)
print_trees $ take 1 trees
trees' <- sample' (resize 1 arbitrary)
print_trees $ take 1 trees'
trees'' <- sample' (resize 2 arbitrary)
print_trees $ take 2 trees''
Выполнение работы здесь sample'
с типом Gen a -> IO [a]
. То есть при запуске внутри действия IO
, sample'
создает список того, что вы хотите сгенерировать, произвольные деревья в этом случае. Связанная функция sample
имеет тип Show a => Gen a -> IO ()
, что означает, что она идет вперед и печатает сгенерированные тестовые данные.
Вывод вышеуказанной программы после добавления deriving Show
к вашему определению Tree
равен
[Empty]
[Node Empty Empty]
[Node (Node Empty Empty) Empty,Node Empty (Node Empty Empty)]