【F#从入门到实战】03. F#自定义操作符

举报
jackwangcumt 发表于 2021/07/13 21:26:36 2021/07/13
【摘要】 F#语言支持自定义操作符,特别是对于数学相关领域的应用来说,自定义类型上的操作符可以大大简化代码,提高程序可读性。本文就通过示例讲解全局自定义操作符和附属于特定类型的操作符。

       欢迎大家来到【F#从入门到实战】,在这里我将分享关于F#编程语言的系列文章,带大家一起去学习和成长,并探索函数编程语言F#这个有趣的世界。所有文章都会结合示例代码和笔者的经验进行讲解,真心想把十余年的IT经验分享给大家,希望对您有所帮助,文章中也定有不足之处,请海涵!本系统文章将从F#基本语法入手,逐步通过自定义类型来实现数学表达式的各种常见解析操作,如对表达式进行求值、化简、展开、求导和求积分等。此系统博文也是了解和实现一个简易的计算机代数系统的基础。

      下面给出【F#从入门到实战】系统专题文章的目录:

【F#从入门到实战】01. F#语言快速入门 
【F#从入门到实战】02. F#数组常见用法   
【F#从入门到实战】03. F#自定义操作符 
【F#从入门到实战】04. F#5.0新特征总结 
【F#从入门到实战】05. F#表达式求值     
【F#从入门到实战】06. F#表达式化简    
【F#从入门到实战】07. F#表达式展开    
【F#从入门到实战】08. F#大整数阶乘    
【F#从入门到实战】09. F#表达式求导   
【F#从入门到实战】10. F#表达式积分   
【F#从入门到实战】11. F#库FParsec入门  
【F#从入门到实战】12. F#库FParsec解析表达式   
【F#从入门到实战】13. F#库FParsec实现求导符号计算   
【F#从入门到实战】14. F#实现分部积分法   

     下面将正式开始本文的介绍: 

     我们知道,不少编程语言都支持自定义操作符,特别是对于数学相关领域的应用来说,自定义类型上的操作符可以大大简化代码,提高程序可读性。对于F#来说,支持全局的自定义操作符和附属于特定类型的操作符。这里需要注意的就是,全局自定义操作符需要慎用,他可能会覆盖默认的操作符。

    下面我们定义一个全局操作符,具体示例如下:

//全局操作符,覆盖默认的
let inline (+)(a : float)(b : float) = 2. * a * b ;;

    在F#交互环境下(;;为结束标志),此示例执行如下:

1.jpg

    此时,输入  2. + 3. 的计算结果为12.0 。其中的2.是2.0的简写,代表float类型。另外,如果此时输入2 + 3 则提示错误,因为此为int类型,而定义的操作符参数类型是float,不兼容。

    还有一类就是对于自定义类型上的自定义操作符。这种比较常见,下面给出一个自定义复数类型,并给出自定义操作符的示例。

module YdMath 
//https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/operator-overloading
    type Complex =  
        {
            Real : float 
            Imaginary : float 
        }

        static member (+) (f1 : Complex, f2 : Complex) =
           let cr = f1.Real + f2.Real 
           let ci = f1.Imaginary + f2.Imaginary
           {Real=cr ; Imaginary = ci}

        static member (-) (f1 : Complex, f2 : Complex) =
            let cr = f1.Real - f2.Real 
            let ci = f1.Imaginary - f2.Imaginary
            {Real=cr ; Imaginary = ci}

        override this.ToString() =
           if (this.Imaginary = 0.) then this.Real.ToString()
           else this.Real.ToString() + "+" + this.Imaginary.ToString()+"i"

    从数学上来说,复数(Complex)具有一个实部(Real)和一个虚部(Imaginary) ,这里的话,用float类型来代表每个部分的数值类型。其中的static member定义了一个静态成员方法,但它的函数名是一个操作符,可实现自定义操作符功能。这里简单的定义了一个复数的加法和减法操作。并override重写了ToString方法。最后,我们用如下代码段进行自定义操作符测试:

let c1 = { Real = 3. ; Imaginary = 5. }
let c2 = { Real = 2. ; Imaginary = 3. }
let c3 = c1 + c2
let c4 = c1 - c2
printfn "(%s) + (%s) = %s" (c1.ToString()) (c2.ToString()) (c3.ToString())
printfn "(%s) - (%s) = %s" (c1.ToString()) (c2.ToString()) (c4.ToString())

    运行此示例,结果如下:

(3+5i) + (2+3i) = 5+8i
(3+5i) - (2+3i) = 1+2i

   最后,再给出一个根据官方文档示例修改的自定义操作符:

    let rec hcf a b =
      if a = 0. then b
      elif a<b then hcf a (b - a)
      else hcf (a - b) b
    
    //分数
    type Fract =
       {
          fz : float  //分子
          fm : float //分母
       }
       static member (+) (f1 : Fract, f2 : Fract) =
          let fz  = f1.fz * f2.fm + f2.fz * f1.fm
          let fm = f1.fm * f2.fm
          let cf = hcf fz fm
          { fz = fz / cf; fm = fm / cf }
    
       static member (-) (f1 : Fract, f2 : Fract) =
           let fz  = f1.fz * f2.fm - f2.fz * f1.fm
           let fm = f1.fm * f2.fm
           let cf = hcf fz fm
           { fz = fz / cf; fm = fm / cf }

       static member (*) (f1 : Fract, f2 : Fract) =
          let fz = f1.fz * f2.fz
          let fm = f1.fm * f2.fm
          let cf = hcf fz fm
          { fz = fz / cf; fm = fm / cf }
    
       static member (/) (f1 : Fract, f2 : Fract) =
          let fz = f1.fz * f2.fm
          let fm = f2.fz * f1.fm
          let cf = hcf fz fm
          { fz = fz / cf; fm = fm / cf }

    
        override this.ToString() =
           if (this.fm = 1.)
             then this.fz.ToString()
             else this.fz.ToString() + "/" + this.fm.ToString()

执行测试代码示例如下:

    let f1 = { fz = 2.; fm = 5.}
    let f2 = { fz = 1.; fm = 6.}
    let f = f1 + f2 
    printfn "(%s) + (%s) = %s" (f1.ToString()) (f2.ToString()) (f.ToString())
    let f = f1 - f2 
    printfn "(%s) - (%s) = %s" (f1.ToString()) (f2.ToString()) (f.ToString())
    let f = f1 * f2 
    printfn "(%s) * (%s) = %s" (f1.ToString()) (f2.ToString()) (f.ToString())
    let f = f1 / f2 
    printfn "(%s) / (%s) = %s" (f1.ToString()) (f2.ToString()) (f.ToString())

执行示例结果如下:

(2/5) + (1/6) = 17/30
(2/5) - (1/6) = 7/30
(2/5) * (1/6) = 1/15
(2/5) / (1/6) = 12/5
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。