FsUnit и проверка равенства чисел с плавающей точкой - PullRequest
3 голосов
/ 08 июля 2010

Я начал использовать FsUnit для проверки кода F #.Это позволяет выразить утверждение в стиле F #, например:

[<Test>]
member this.``Portugal voted for 23 countries in 2001 Eurovision contest``() =
    this.totalVotes 
    |> getYearVotesFromCountry "Portugal" 2001
    |> Seq.length
    |> should equal 23

Обратите внимание, что «должно равняться 23», которое я получаю из FsUnit.Вот как FsUnit определяет это:

let = x = new EqualConstraint (x)

С числами с плавающей запятой это не так просто.Я должен использовать EqualConstraint с внутри метода.Это естественно подходит для C #:

Assert.That(result).Is.EqualTo(1).Within(0.05);

Конечно, я бы хотел написать на F #:

result |> should equal 1 within 0.05

Но это не работает.В итоге я определил новую функцию:

let almostEqual x = (new EqualConstraint(x)).Within(0.01)

или, если я хочу параметризовать точность, я могу указать ее в качестве второго аргумента:

let equalWithin x y = (new EqualConstraint(x)).Within(y)

Но ни одна из них не хороша.Я хотел бы определить «внутри» функцию более естественным образом для F #, чтобы она могла использоваться вместе с равными.F # не поддерживает перегрузку методов, так что, похоже, я не могу определить его таким образом, чтобы «равно» можно было использовать отдельно или вместе с «внутри».

Есть идеи?

Ответы [ 2 ]

12 голосов
/ 08 июля 2010

Это интересная проблема!Я не думаю, что вы можете каким-либо образом добавить within 0.05 к существующему определению should equal.Для этого вам нужно добавить параметр в функцию should, но для этого нужно иметь фиксированное количество параметров в библиотеке.

Один из способов элегантно написать это в F # - создать пользовательский оператор+/-.Обратите внимание, что вам все еще нужно использовать скобки, но это выглядит довольно аккуратно:

0.9 |> should equal (1.0 +/- 0.5)

Оператор просто создает некоторое значение специального типа, которое необходимо явно обработать в функции equal.Вот реализация:

type Range = Within of float * float
let (+/-) (a:float) b = Within(a, b)

let equal x = 
  match box x with 
  | :? Range as r ->
      let (Within(x, within)) = r
      (new EqualConstraint(x)).Within(within)
  | _ ->
    new EqualConstraint(x)
0 голосов
/ 08 июля 2010

Примечание по терминологии: F # поддерживает метод перегрузка;он не поддерживает перегрузку функций с привязкой let («свободные» функции определены в модулях).

Например, вы можете сделать его should.equal с точкой, так что equal будетметод объекта should, который вы можете перегрузить.Хотя это все равно не поможет, так как вы не можете перегружать аргументы карри.Хммм.

Хорошо, я не могу предложить много полезного.Лично мне не нравятся эти типы синтаксических сахаров в библиотеках.

...