2

私は 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)])]))

私の目には、これら2つは同一ですが、そうではないようです。Expr と Expr<'t> を混同したと思いますが、よくわかりません。実装コードの一部は、コンパイルするために試行錯誤を繰り返しました。

微妙なエラーがここにあるかもしれないアイデアはありますか?

ソリューションで更新

@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 @>

期待どおりに動作します。

4

1 に答える 1

4

F#のVar引用符で使用される型は、構造上の等価性をサポートしていません。実際、どちらも実装していません。参照の等価性をチェックIEquatable<'T>するメソッドのオーバーライドを提供するだけです。Equals

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

したがって、テストが失敗する理由はy、引用符内の変数が「等しい」と認識されていないためです。頭に浮かぶ最初の解決策はIEqualityComparer<'T>、変数の等価性を希望どおりに処理するカスタム実装を作成し、それを使用して生成された値に対して期待値をチェックすることです。

于 2013-03-10T20:39:56.223 に答える