2013-03-10 29 views
2

我有f#中微積分工具箱的開頭。對於學習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 <'t>做了某種混合,但我不確定。一些實現代碼是試驗和錯誤來編譯它。

任何想法是什麼微妙的錯誤可能在這裏?

更新與解決方案

@Jack是正確的,無功實現參考平等,使得它很難用標準的平等與檢查代碼的報價。出於測試目的,它「足夠正確」來比較字符串。爲了使這一美味,我創建了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 @> 

,並預期它會工作。

回答

4

用於F#報價的Var類型不支持結構相等。事實上,它沒有實現IEquatable<'T>要麼 - 它僅提供了Equals方法,檢查參考平等的覆蓋:

https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/quotations.fs#L122

因此,它看起來像你的測試失敗的原因是因爲y您的報價中的變量未被識別爲「相等」。首先想到的第一個解決方案是創建一個自定義的實現IEqualityComparer<'T>,它將以您想要的方式處理變量相等性,然後使用它來檢查期望值與生成的值。

+0

我想我只是比較ToString並做它。這僅用於測試,如果字符串匹配,那麼它可能是相同的表達式。 – bradgonesurfing 2013-03-11 18:39:49