天天看點

F#表達式求導

      Matlab可以對數學公式進行求導操作,這裡用F#實作一個簡單的表達式求導推理。首先還是定義一個自定義的Expr資料類型:

type Expr = 
  | CstF of float
  | Var of string
  | Add of Expr * Expr  // +
  | Sub of Expr * Expr // -
  | Mul of Expr * Expr // *
  | Div of Expr * Expr // / 
  | Pow of Expr * Expr // ^ 
  | Sin of Expr  
  | Cos of Expr  
  | Neg of Expr       

   定義一個遞歸的求導函數diff,由于求導的規則比較多,這裡就簡單的實作幾個場景的求導公式:

let rec diff e  =
    match e with
    | CstF f                -> CstF 0.0
    | Var x                 -> CstF 1.0 
    | Add(CstF a, Var x)   ->  CstF 1.0 
    | Add(e1, e2)  -> Add(diff e1, diff e2)
    | Sub(e1, e2)  -> Sub(diff e1, diff e2)
    | Mul(CstF a, Var x) ->  CstF a 
    | Mul(e1, e2) ->  Mul(diff e1, diff e2)
    | Pow(Var x,CstF a) -> Mul(CstF a,Pow(Var x,CstF (a - 1.)))
    | Pow(e1,e2)  -> Mul(e2,Pow(e1, Sub(e2,CstF 1.)))
    | Sin(e1)  -> Mul(Cos(e1),diff e1)
    | Cos(e1)  -> Mul(Neg(Sin(e1)),diff e1)
    | Neg(e1)  -> Neg(diff e1)
    | e          -> e ;;      

   為了更到的輸出DSL到控制台,定義一個列印表達式的函數:

let rec printExpr2 e =
    match e with
    | CstF f            -> string f
    | Var x             ->  x 
    | Add(e1 , e2) ->  "(" + (printExpr2 e1) + "+" + (printExpr2 e2) + ")"
    | Sub(e1 , e2) ->  "(" + (printExpr2 e1) + "-" + (printExpr2 e2) + ")"
    | Mul(e1 , e2) ->  "(" + (printExpr2 e1) + "*" + (printExpr2 e2) + ")"
    | Div(e1 , e2) ->  "(" + (printExpr2 e1) + "/" + (printExpr2 e2) + ")"
    | Pow(e1 , e2) ->  "(" + (printExpr2 e1) + "^" + (printExpr2 e2) + ")"
    | Sin(e1) ->  "sin(" + (printExpr2 e1) + ")"
    | Cos(e1) ->  "cos(" + (printExpr2 e1) + ")"
    | Neg(e1) ->  "-(" + (printExpr2 e1) + ")"
    | _          -> failwith "unknown operation";;      

    嚴格來講,每個操作的前後用()進行分組應該更加嚴謹,但是過多的()嵌套會讓數學表達式不容易了解。但,關于括号的嵌套化簡問題,還需要一定的工作才能工作,這裡先不進行處理。下面定義一個求導示例:

let e1 = Sin(Sub(Pow(Var "x", CstF 3.0), Var "x")) ;; 
let e2 = diff e1;;
printExpr2 e2;;      

  執行如上代碼,則輸出如下圖所示:

F#表達式求導

 這與網上的求導工具(

https://zh.numberempire.com/derivativecalculator.php

)求出的一緻:

F#表達式求導

繼續閱讀