Как использовать QuickCheck для проверки завершения функции?

Я хотел бы использовать QuickCheck для проверки функции, чтобы убедиться, что она завершается (без бесконечной рекурсии, исключений и т. д.). Вот что я делаю в данный момент:

f :: Int -> Int -> Int

prop_fTerminates :: Int -> Int -> Bool   -- say
prop_fTerminates x y = f x y `seq` True

Есть ли лучший (более выразительный и идиоматический) способ?


person Arek' Fu    schedule 31.08.2016    source источник


Ответы (2)


Я, вероятно, не стал бы беспокоиться, я бы просто проверил другие свойства его вывода.

Любое свойство, которое каким-либо образом исследует вывод f x y, будет выполнять по крайней мере столько же проверок завершения, сколько и ваше prop_fTerminates, так зачем тратить время на удвоение этой проверки?

Если по какой-то причине «f x y прекращается» — это единственное свойство, которое вы можете проверить для f, то то, что у вас есть, кажется разумным.

person Ben    schedule 31.08.2016
comment
Да, проблема именно в том, что я не могу выразить какой-либо разумный инвариант относительно выхода f, который на самом деле является результатом вычисления Монте-Карло. - person Arek' Fu; 31.08.2016

Это проблема остановки. Не существует алгоритма, способного сказать вам, завершается ли функция или нет.

В частности, вы можете получить положительный результат (т. е. если функция завершится, об этом вам сообщит это предложение), если вы готовы ждать достаточно долго. Но просто подождав, вы никогда не узнаете разницу между эта функция не завершается и эта функция еще не завершена.

Вы можете реализовать проверку, например, заканчивается ли эта функция за время T, что может подойти для ваших нужд.


Изменить Как написано, ваша функция не делает того, что вам нужно. Рассмотреть возможность

> let f = 1:f
> f `seq` True
True

Причина в том, что seq оценивается только как нормальная форма слабой головы. Вместо этого вы можете использовать deepseq, который глубоко оценивает структуры данных,

> import Control.DeepSeq (deepseq)
> f `deepseq` True
* never returns *
person Chris Taylor    schedule 31.08.2016
comment
Я не хочу доказывать, что функция завершается (это проблема остановки), я просто хочу убедиться, что я не допустил ошибки в реализации, которая привела бы к бесконечной рекурсии. Я хотел бы написать свой тест таким образом, чтобы он ясно выражал мои намерения. - person Arek' Fu; 31.08.2016
comment
См. мое редактирование, которое объясняет, почему seq здесь не подходит. - person Chris Taylor; 31.08.2016
comment
Ну, f возвращает Int, так что WHNF и NF — это одно и то же, верно? Но в общем случае вы безусловно правы. - person Arek' Fu; 31.08.2016
comment
Вы правы, что в общем случае это неразрешимо, но есть способы доказать завершение многих функций. Это приходится делать многим средствам доказательства теорем и системам проверки программного обеспечения. Для функционального языка общая идея состоит в том, чтобы определить четко определенный порядок входного термина, а затем показать с помощью индукции, что аргументы рекурсивных вызовов уменьшаются в этом порядке. Отсюда следует, что функция должна завершиться. Конечно, эти системы работают не для каждой функции, но достаточно часто. - person Jens; 31.08.2016
comment
Да, если вы всегда возвращаете только Int, вам не нужно беспокоиться о seq и deepseq. - person Chris Taylor; 31.08.2016