如何比较深度嵌套的歧视联合?
How to compare deeply nested discriminated unions?
我想对 returns 和 Result
的函数进行单元测试(见下文)。
我的问题是:如何轻松检查结果在数值上是否等于预期值?
这是完全匹配的版本。
type QuadraticResult =
| ComplexResult of Complex * Complex
| DoubleResult of float
| TwoResults of float * float
type Result=
| QuadraticResult of QuadraticResult
| LinearResult of LinearFormulaSolver.Result
/// Solves a x² + bx + c = 0
let Compute (a,b,c) : Result =
[<Fact>]
member test.``the solution for x² = 0.0 is a double 0.0`` ()=
let result = Compute (1.0, 0.0, 0.0)
let expected = Result.QuadraticResult (DoubleResult 0.0)
// only exact match, I'd like to test if difference is below a certain threshold
Assert.Equal (result, expected)
这是我目前使用的解决方案。
它基于 Andreys 解决方案,但针对允许的距离、结果排列和线性情况进行了扩展。
:
let ComplexEquality distance (x : Complex) (y : Complex )=
let dx = x.Real - y.Real
let dy = x.Imaginary - y.Imaginary
abs (dx) < distance && abs(dy) < distance
let QuadraticEquality distance x y = match (x,y) with
| (ComplexResult (a,b),ComplexResult(c,d)) -> (ComplexEquality distance a c && ComplexEquality distance b d) || (ComplexEquality distance a d && ComplexEquality distance b c)
| (DoubleResult a,DoubleResult b) -> abs (a - b) < distance
| (TwoResults (a,b),TwoResults(c,d)) -> (abs(a - c) < distance && (b - d) < distance) || (abs(a - d) < distance && (b - c) < distance)
| _ -> false
let LinearEquality distance x y = match (x , y) with
| (SingleResult a, SingleResult b) -> abs (a-b) < distance
| (NoResults, NoResults) | (InfiniteResults, InfiniteResults) -> true
| _ -> false
let ResultEquality distance x y = match (x,y) with
| (QuadraticResult a,QuadraticResult b) -> QuadraticEquality distance a b
| (LinearResult a,LinearResult b) -> LinearEquality distance a b
| _ -> false
[<Fact>]
member test.``the solution for x² = 0 is a double 0`` ()=
let result = QuadraticFormulaSolver.Compute (1.0, 0.0, 0.0)
let expected = Result.QuadraticResult (QuadraticFormulaSolver.DoubleResult 0.00001)
Assert.True( ResultEquality 0.001 result expected)
我认为没有任何 "magic trick" 可以让您自动执行此操作。我想你有三个选择:
编写自定义函数来对现有类型进行相等性测试,并对所有嵌套的 float
值执行特殊类型的比较
在 float
上编写一个实现自定义比较的包装器,然后在可区分的联合中使用此类型
编写一些基于反射的魔术来执行自定义相等性测试。
其中,我认为 (1) 可能是最简单的选项 - 尽管这意味着需要输入更多内容。如果您想在程序中的任何地方使用此自定义比较,选项 (2) 可能会很有趣。如果你有很多不同的嵌套类型,最后 (3) 可能有意义,但它也是最容易出错的选项。
我写了 (2) 的最小演示,但我仍然认为 (1) 可能是更好的方法:
[<Struct; CustomComparison; CustomEquality>]
type ApproxFloat(f:float) =
member x.Value = f
override x.GetHashCode() = f.GetHashCode()
override x.Equals(another) =
match another with
| :? ApproxFloat as y -> abs (x.Value - y.Value) <= 0.001
| _ -> false
interface System.IComparable with
member x.CompareTo(another) =
match another with
| :? ApproxFloat as y -> compare x.Value y.Value
| _ -> failwith "Cannot compare"
type Complex =
| Complex of ApproxFloat * ApproxFloat
type Result =
| Result of Complex
Result(Complex(ApproxFloat(1.0), ApproxFloat(1.0))) =
Result(Complex(ApproxFloat(1.0001), ApproxFloat(1.0001)))
我认为您只需要编写辅助函数即可。例如:
open System.Numerics
type QuadraticResult =
| ComplexResult of Complex * Complex
| DoubleResult of float
| TwoResults of float * float
type Result=
| QuadraticResult of QuadraticResult
| LinearResult of int
let QuadraticEquality x y = match (x,y) with
| (ComplexResult (a,b),ComplexResult(c,d)) -> (a.Equals c) && (b.Equals d)
| (DoubleResult a,DoubleResult b) -> a = b
| (TwoResults (a,b),TwoResults(c,d)) -> (a = b) && (c = d)
| _ -> false
let ResultEquality x y = match (x,y) with
| (QuadraticResult a,QuadraticResult b) -> QuadraticEquality a b
| (LinearResult a,LinearResult b) -> a = b
| _ -> false
并且在测试中容易编写
Assert.IsTrue(ResultEquality result expected);
我想对 returns 和 Result
的函数进行单元测试(见下文)。
我的问题是:如何轻松检查结果在数值上是否等于预期值?
这是完全匹配的版本。
type QuadraticResult =
| ComplexResult of Complex * Complex
| DoubleResult of float
| TwoResults of float * float
type Result=
| QuadraticResult of QuadraticResult
| LinearResult of LinearFormulaSolver.Result
/// Solves a x² + bx + c = 0
let Compute (a,b,c) : Result =
[<Fact>]
member test.``the solution for x² = 0.0 is a double 0.0`` ()=
let result = Compute (1.0, 0.0, 0.0)
let expected = Result.QuadraticResult (DoubleResult 0.0)
// only exact match, I'd like to test if difference is below a certain threshold
Assert.Equal (result, expected)
这是我目前使用的解决方案。 它基于 Andreys 解决方案,但针对允许的距离、结果排列和线性情况进行了扩展。 :
let ComplexEquality distance (x : Complex) (y : Complex )=
let dx = x.Real - y.Real
let dy = x.Imaginary - y.Imaginary
abs (dx) < distance && abs(dy) < distance
let QuadraticEquality distance x y = match (x,y) with
| (ComplexResult (a,b),ComplexResult(c,d)) -> (ComplexEquality distance a c && ComplexEquality distance b d) || (ComplexEquality distance a d && ComplexEquality distance b c)
| (DoubleResult a,DoubleResult b) -> abs (a - b) < distance
| (TwoResults (a,b),TwoResults(c,d)) -> (abs(a - c) < distance && (b - d) < distance) || (abs(a - d) < distance && (b - c) < distance)
| _ -> false
let LinearEquality distance x y = match (x , y) with
| (SingleResult a, SingleResult b) -> abs (a-b) < distance
| (NoResults, NoResults) | (InfiniteResults, InfiniteResults) -> true
| _ -> false
let ResultEquality distance x y = match (x,y) with
| (QuadraticResult a,QuadraticResult b) -> QuadraticEquality distance a b
| (LinearResult a,LinearResult b) -> LinearEquality distance a b
| _ -> false
[<Fact>]
member test.``the solution for x² = 0 is a double 0`` ()=
let result = QuadraticFormulaSolver.Compute (1.0, 0.0, 0.0)
let expected = Result.QuadraticResult (QuadraticFormulaSolver.DoubleResult 0.00001)
Assert.True( ResultEquality 0.001 result expected)
我认为没有任何 "magic trick" 可以让您自动执行此操作。我想你有三个选择:
编写自定义函数来对现有类型进行相等性测试,并对所有嵌套的
float
值执行特殊类型的比较在
float
上编写一个实现自定义比较的包装器,然后在可区分的联合中使用此类型编写一些基于反射的魔术来执行自定义相等性测试。
其中,我认为 (1) 可能是最简单的选项 - 尽管这意味着需要输入更多内容。如果您想在程序中的任何地方使用此自定义比较,选项 (2) 可能会很有趣。如果你有很多不同的嵌套类型,最后 (3) 可能有意义,但它也是最容易出错的选项。
我写了 (2) 的最小演示,但我仍然认为 (1) 可能是更好的方法:
[<Struct; CustomComparison; CustomEquality>]
type ApproxFloat(f:float) =
member x.Value = f
override x.GetHashCode() = f.GetHashCode()
override x.Equals(another) =
match another with
| :? ApproxFloat as y -> abs (x.Value - y.Value) <= 0.001
| _ -> false
interface System.IComparable with
member x.CompareTo(another) =
match another with
| :? ApproxFloat as y -> compare x.Value y.Value
| _ -> failwith "Cannot compare"
type Complex =
| Complex of ApproxFloat * ApproxFloat
type Result =
| Result of Complex
Result(Complex(ApproxFloat(1.0), ApproxFloat(1.0))) =
Result(Complex(ApproxFloat(1.0001), ApproxFloat(1.0001)))
我认为您只需要编写辅助函数即可。例如:
open System.Numerics
type QuadraticResult =
| ComplexResult of Complex * Complex
| DoubleResult of float
| TwoResults of float * float
type Result=
| QuadraticResult of QuadraticResult
| LinearResult of int
let QuadraticEquality x y = match (x,y) with
| (ComplexResult (a,b),ComplexResult(c,d)) -> (a.Equals c) && (b.Equals d)
| (DoubleResult a,DoubleResult b) -> a = b
| (TwoResults (a,b),TwoResults(c,d)) -> (a = b) && (c = d)
| _ -> false
let ResultEquality x y = match (x,y) with
| (QuadraticResult a,QuadraticResult b) -> QuadraticEquality a b
| (LinearResult a,LinearResult b) -> a = b
| _ -> false
并且在测试中容易编写
Assert.IsTrue(ResultEquality result expected);