我在f#中开始使用微积分工具箱。学习f#就像在最后使用一些有用的东西在我想到的其他一些项目中一样。基本和不完整的代码是
namespace BradGoneSurfing.Symbolics
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns
open Microsoft.FSharp.Quotations.DerivedPatterns
open System
open Microsoft.FSharp.Reflection
open Microsoft.FSharp.Quotations
open FSharpx.Linq.QuotationEvaluation
open Microsoft.FSharp.Linq.RuntimeHelpers
module Calculus =
let rec der_impl param quotation =
let (|X|_|) input = if input = param then Some(X) else None
match quotation with
| SpecificCall <@ (*) @> (_,types,l::r::[]) ->
let dl = der_impl param l
let dr = der_impl param r
<@@ (%%dl:double) * (%%r:double) + (%%l:double) * (%%dr:double) @@>
| SpecificCall <@ Math.Sin @> (_,_types, arg::_) ->
let di = der_impl param arg
<@@ Math.Cos( (%%arg:double) ) @@>
| ExprShape.ShapeVar v ->
match v with
| X -> <@@ 1.0 @@>
| _ -> (Expr.Var v)
| ExprShape.ShapeLambda (v,expr) -> Expr.Lambda (v,der_impl param expr)
| ExprShape.ShapeCombination (o, exprs) -> ExprShape.RebuildShapeCombination (o,List.map (fun e -> der_impl param e ) exprs)
let rec der expr =
match expr with
| Lambda(param, body) ->
Expr.Lambda(param, (der_impl param body))
| _ -> failwith "oops"
我有一个NUnit / FSUnit测试证明了我的第一部分代码
namespace BradGoneSurfing.Symbolics.Test
open FsUnit
open NUnit.Framework
open Microsoft.FSharp.Quotations
open BradGoneSurfing.Symbolics.Calculus
open FSharpx.Linq.QuotationEvaluation
open System
open Microsoft.FSharp.Linq.RuntimeHelpers
[<TestFixture>]
type ``This is a test for symbolic derivatives`` ()=
[<Test>]
member x.``Derivative should work`` ()=
let e = <@ (fun (y:double) -> y * y) @>
let d = der e
let x = <@ fun (y:double) -> 1.0 * y + y * 1.0 @>
d |> should equal x
测试类型有效,但没有。结果说
Expected:
Lambda (y,
Call (None, op_Addition,
[Call (None, op_Multiply, [Value (1.0), y]),
Call (None, op_Multiply, [y, Value (1.0)])]))
But Was:
Lambda (y,
Call (None, op_Addition,
[Call (None, op_Multiply, [Value (1.0), y]),
Call (None, op_Multiply, [y, Value (1.0)])]))
现在我觉得这两个是相同的,但似乎没有。我猜我已经与Expr vs Expr&lt; t&gt;进行了某种混合。但我不确定。一些实现代码是反复试验以进行编译。
任何想法可能会出现微妙的错误吗?
使用解决方案更新
@Jack是正确的,Var实现了引用相等性,并且很难使用带代码引用的标准相等性检查。出于测试目的,比较字符串是“足够正确的”。为了使这个可口,我为FsUnit / NUnit创建了一个自定义匹配器,如下所示
type EqualsAsString (e:obj)=
inherit NUnit.Framework.Constraints.Constraint()
let expected = e
override x.Matches(actual:obj)=
x.actual <- actual
x.actual.ToString() = expected.ToString()
override x.WriteDescriptionTo(writer)=
writer.WriteExpectedValue(expected)
override x.WriteActualValueTo(writer)=
writer.WriteActualValue(x.actual)
和FSUnit包装器
let equalExpr (x:Expr<'t>) = new EqualsAsString(x)
所以我可以做到
d |> should equalExpr <@ fun y -> y * y @>
它将按预期工作。
答案 0 :(得分:4)
与F#引用一起使用的Var
类型不支持结构相等。实际上,它也没有实现IEquatable<'T>
- 它只提供了覆盖参考相等性的Equals
方法的覆盖:
https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/quotations.fs#L122
因此,看起来您的测试失败的原因是因为引号中的y
变量未被识别为“相等”。我想到的第一个解决方案是创建一个IEqualityComparer<'T>
的自定义实现,它以你想要的方式处理变量相等,然后用它来检查生成值的预期值。