簡體   English   中英

通用操作字典出現問題

[英]Issue with a generic dictionary of operations

我有一個操作字典:

type INumerics<'T> =
  abstract Zer : 'T
  abstract Add : 'T * 'T -> 'T
  abstract Sub : 'T * 'T -> 'T
  abstract Mul : 'T * 'T -> 'T
  abstract Div : 'T * 'T -> 'T
  abstract Neq : 'T * 'T -> bool

具有輔助功能:

let inline add (x : 'T) (y : 'T) : 'T   = (+)  x y
let inline sub (x : 'T) (y : 'T) : 'T   = (-)  x y
let inline mul (x : 'T) (y : 'T) : 'T   = (*)  x y
let inline div (x : 'T) (y : 'T) : 'T   = (/)  x y
let inline neq (x : 'T) (y : 'T) : bool = (<>) x y 

然后我們有一個使用MailboxProcessor代理的簡單計算器:

type Agent<'T> = MailboxProcessor<'T>

type CalculatorMsg<'T> =
    | Add of 'T * 'T * AsyncReplyChannel<'T>
    | Sub of 'T * 'T * AsyncReplyChannel<'T> 
    | Mul of 'T * 'T * AsyncReplyChannel<'T>  
    | Div of 'T * 'T * AsyncReplyChannel<'T>

type CalculatorAgent< ^T when ^T : (static member get_Zero : unit -> ^T) 
                         and  ^T : (static member Zero : ^T) 
                         and  ^T : (static member (+)  : ^T * ^T -> ^T)
                         and  ^T : (static member (-)  : ^T * ^T -> ^T)
                         and  ^T : (static member (*)  : ^T * ^T -> ^T)
                         and  ^T : (static member (/)  : ^T * ^T -> ^T)
                         and  ^T : equality >() =
    let agent =
        let ops = 
            { new INumerics<'T> with 
                member ops.Zer       = LanguagePrimitives.GenericZero<'T> 
                member ops.Add(x, y) = (x, y) ||> add  
                member ops.Sub(x, y) = (x, y) ||> sub
                member ops.Mul(x, y) = (x, y) ||> mul   
                member ops.Div(x, y) = (x, y) ||> div   
                member ops.Neq(x, y) = (x, y) ||> neq }

        Agent<CalculatorMsg<'T>>.Start(fun inbox ->
            let rec loop () =
                async {
                    let! msg = inbox.TryReceive()
                    if msg.IsSome then
                        match msg.Value with 
                        | Add (x, y, rep) ->
                            printfn "Adding %A and %A ..." x y
                            let res = ops.Add(x, y)
                            res |> rep.Reply  
                            return! loop()
                        | Sub (x, y, rep) -> 
                            printfn "Subtracting %A from %A ..." y x
                            let res = ops.Sub(x, y) 
                            res |> rep.Reply  
                            return! loop()
                        | Mul (x, y, rep) ->
                            printfn "Multiplying %A by %A ... " y x
                            let res = ops.Mul(x, y)
                            res |> rep.Reply  
                            return! loop()
                        | Div (x, y, rep) ->
                            printfn "Dividing %A by %A ..." x y
                            if ops.Neq(y, ops.Zer) then 
                                let res = ops.Div(x, y)
                                res |> rep.Reply  
                            else
                                printfn "#DIV/0" 
                            return! loop()
                    else 
                        return! loop()
                }
            loop()
        )

    // timeout = infinit => t = -1
    let t = 1000

    member inline this.Add(x, y) =
        agent.PostAndTryAsyncReply((fun rep -> Add (x, y, rep)), t)
        |> Async.RunSynchronously
    member inline this.Subtract(x, y) =
        agent.PostAndTryAsyncReply((fun rep -> Sub (x, y, rep)), t)
        |> Async.RunSynchronously
    member inline this.Multiply(x, y) =
        agent.PostAndTryAsyncReply((fun rep -> Mul (x, y, rep)), t)
        |> Async.RunSynchronously
    member inline this.Divide(x, y) =
        agent.PostAndTryAsyncReply((fun rep -> Div (x, y, rep)), t)
        |> Async.RunSynchronously

作為一個使用示例,我們有:

let calculatorAgentI = new CalculatorAgent<int>()

(2, 1) |> calculatorAgentI.Add 
(2, 1) |> calculatorAgentI.Subtract
(2, 1) |> calculatorAgentI.Multiply
(2, 1) |> calculatorAgentI.Divide
(2, 0) |> calculatorAgentI.Divide

問題是加法和乘法以及最后的分度工作正常:

> 
Adding 2 and 1 ...
val it : int option = Some 3

> 
Multiplying 1 by 2 ... 
val it : int option = Some 2

> 
Dividing 2 by 0 ...
#DIV/0
val it : int option = None

一旦我們使用Subtract和first Divide會返回int option = None ,我就會遇到麻煩,以下是我從任何操作中獲得的唯一輸出:

> 
val it : int option = None

就我所考慮的時間和辛苦,我無法弄清楚“減” /“除”部分或“接收” /“答復”操作是否存在問題。

在附加了調試器的情況下運行此代碼,當您嘗試從代理內部運行sub功能時,會看到獲得System.NotSupportedException:

System.NotSupportedException occurred
  HResult=0x80131515
  Message=Specified method is not supported.
  Source=FSI-ASSEMBLY
  StackTrace:
  at FSI_0002.ops@60.FSI_0002-INumerics`1-Sub(T X1, T X2) 

設置val it : int option = None的原因val it : int option = None是因為您指定了1秒的超時,並且在引發異常后將其擊中。

直接調用sub可以正常工作,但不能通過CalculatorAgent類調用它。 這是因為在這種情況下,類型參數是在類上定義的,並且在F#中對類使用結構類型約束存在限制。 我建議閱讀靜態解析類型參數及其限制。

該行為似乎是違規行為或錯誤。 我還期望所有操作的行為相同。

無論如何,您都可以通過捕獲靜態內聯成員中的ops來解決此問題(而不是在類型上使用靜態解析的類型參數)。 以下對我有用:

type CalculatorAgent<'T>(ops:INumerics<'T>) = 
  let agent = 
    Agent<CalculatorMsg<'T>>.Start(fun inbox ->
      let rec loop () = async {
        let! msg = inbox.TryReceive()
        match msg with 
        | Some(Add (x, y, rep)) ->
            printfn "Adding %A and %A ..." x y
            let res = ops.Add(x, y)
            res |> rep.Reply  
            return! loop()
        | Some(Sub (x, y, rep)) -> 
            printfn "Subtracting %A from %A ..." y x
            let res = ops.Sub(x, y) 
            res |> rep.Reply  
            return! loop()
        | Some(Mul (x, y, rep)) ->
            printfn "Multiplying %A by %A ... " y x
            let res = ops.Mul(x, y)
            res |> rep.Reply  
            return! loop()
        | Some(Div (x, y, rep)) ->
            printfn "Dividing %A by %A ..." x y
            if ops.Neq(y, ops.Zer) then 
                let res = ops.Div(x, y)
                res |> rep.Reply  
            else
                printfn "#DIV/0" 
            return! loop()
        | _ ->
            return! loop() }
      loop() )

  // timeout = infinit => t = -1
  let t = 1000

  member this.Add(x, y) =
    agent.PostAndTryAsyncReply((fun rep -> Add (x, y, rep)), t)
    |> Async.RunSynchronously
  member this.Subtract(x, y) =
    agent.PostAndTryAsyncReply((fun rep -> Sub (x, y, rep)), t)
    |> Async.RunSynchronously
  member this.Multiply(x, y) =
    agent.PostAndTryAsyncReply((fun rep -> Mul (x, y, rep)), t)
    |> Async.RunSynchronously
  member this.Divide(x, y) =
    agent.PostAndTryAsyncReply((fun rep -> Div (x, y, rep)), t)
    |> Async.RunSynchronously

type CalculatorAgent = 
  static member inline Create() = 
    let ops = 
      { new INumerics<_> with 
          member ops.Zer = LanguagePrimitives.GenericZero<_> 
          member ops.Add(x, y) = x + y
          member ops.Sub(x, y) = x - y
          member ops.Mul(x, y) = x * y
          member ops.Div(x, y) = x / y
          member ops.Neq(x, y) = x <> y }
    CalculatorAgent<_>(ops)

let calculatorAgentI = CalculatorAgent.Create<int>()

(2, 1) |> calculatorAgentI.Add 
(2, 1) |> calculatorAgentI.Subtract
(2, 1) |> calculatorAgentI.Multiply
(2, 1) |> calculatorAgentI.Divide
(2, 0) |> calculatorAgentI.Divide

就是說,我認為真正需要通用數字代碼的情況很少見-所以我懷疑最好完全避免引入所有這些復雜性,而只為特定數字類型編寫代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM