Не зная точно, что такое эпсилон Гильберта, я бы выбрал более фундаментальный подход и использовал Arbitrary
и Gen
для выбора используемых функций.
Во-первых, определите базовый класс для функций, которые вы собираетесь создать. В общем, можно генерировать функции с неопределенными результатами (например, деление на ноль), поэтому мы будем использовать PartialFunction
в качестве нашего базового класса.
trait Fn[A, B] extends PartialFunction[A, B] {
def isDefinedAt(a: A) = true
}
Теперь вы можете предоставить некоторые реализации. Переопределите toString
, чтобы сообщения об ошибках ScalaCheck были понятными.
object Identity extends Fn[Int, Int] {
def apply(a: Int) = a
override def toString = "a"
}
object Square extends Fn[Int, Int] {
def apply(a: Int) = a * a
override def toString = "a * a"
}
// etc.
Я решил генерировать унарные функции из бинарных функций, используя классы case, передавая конструктору дополнительные аргументы. Не единственный способ сделать это, но я считаю его самым простым.
case class Summation(b: Int) extends Fn[Int, Int] {
def apply(a: Int) = a + b
override def toString = "a + %d".format(b)
}
case class Quotient(b: Int) extends Fn[Int, Int] {
def apply(a: Int) = a / b
override def isDefinedAt(a: Int) = b != 0
override def toString = "a / %d".format(b)
}
// etc.
Теперь вам нужно создать генератор Fn[Int, Int]
и определить его как неявный Arbitrary[Fn[Int, Int]]
. Вы можете продолжать добавлять генераторы до посинения (многочлены, составление сложных функций из простых и т. д.).
val funcs = for {
b <- arbitrary[Int]
factory <- Gen.oneOf[Int => Fn[Int, Int]](
Summation(_), Difference(_), Product(_), Sum(_), Quotient(_),
InvDifference(_), InvQuotient(_), (_: Int) => Square, (_: Int) => Identity)
} yield factory(b)
implicit def arbFunc: Arbitrary[Fn[Int, Int]] = Arbitrary(funcs)
Теперь вы можете определить свои свойства. Используйте intG.isDefinedAt(a)
, чтобы избежать неопределенных результатов.
property("left identity simple funcs") = forAll { (a: Int, intG: Fn[Int, Int]) =>
intG.isDefinedAt(a) ==> (fCat.compose(fCat.id[Int])(intG)(a) == intG(a))
}
property("right identity simple funcs") = forAll { (a: Int, intG: Fn[Int, Int]) =>
intG.isDefinedAt(a) ==> (fCat.compose(intG)(fCat.id)(a) == intG(a))
}
Хотя то, что я показал, только обобщает тестируемую функцию, надеюсь, это даст вам представление о том, как использовать расширенные хитрости системы типов для обобщения типов.
person
leedm777
schedule
21.05.2012