F#中运算符的定义规则 - 面向对象网 f# 学习 对象 - 面向对象技术开发

面向对象技术开发

会员投稿 投稿指南 站长资讯通告:
您的位置: 首页 > OOP语言 > F# > 正文

F#中运算符的定义规则

来源: www.bianceng.cn 阅读:

F#允许开发人员定义或重载各类运算符,合理利用这一规则可以让编程变得方便,让代码更容易阅读。例如,在使用F#的MailboxProcessor的时候,我会习惯于定义一个运算符来代替显式的Post操作:

let (>>) m (agent: MailboxProcessor<_>) = agent.Post m

这样便可以这样发送消息:

let agent = MailboxProcessor.Start(fun o -> async { o |> ignore });

"hello world" >> agent

不过,F#的运算符定义规则较为复杂,要搞清楚编译器的整体处理方式着实花费了一番功夫。比较奇怪的是,即便是《Expert F#》中对于这个话题也没有详细的叙述——更夸张的是MSDN的文档也相当马虎,甚至有代码缺失以及与试验不符情况(因为还没有正式发布?)。于是我连看带试,最终打算总结一番,作为备忘的同时也算是补充互联网资源。

运算符重载

F#中允许在global级别重载一个运算符,甚至“覆盖”原有的定义。例如,我们可以写一个Operator模块,其中只有一个“加号”的定义:

// operator.fs

#light

module Operator

let (+) (a:int) (b:int) = a * b

我们可以在另一个模块中引入Operator模块,于是两个整数的“加法”便可以得出乘法的效果了:

1 + 2 |> printfn "%i" // 2

从中也可以看出,胡乱重载运算符实在是一种没事找事的方式。因此,现在这篇文章纯粹都是在“谈技术”,所有的内容,包括示例都不代表“最佳实践”。

运算符的组成

在F#中,自定义运算符可以由以下字符组成:

! % & * + - . / < = > ? @ ^ | ~

目前在MSDN中,《Operator Overloading (F#)》一文(http://msdn.microsoft.com/en-us/library/dd233204(VS.100).aspx)写到“$”也可以作为运算符的组成,不过最新的F#编译器(v1.9.7.4)中会对此作出“警告”,表示以后它将成为一个F#的保留字,不允许用作运算符。

在F#中,每个运算符不限长度。也就是说,如果您喜欢的话,完全可以定义这样的一个运算符来表示整数乘法:

let (!%&*+-./<=>?@^|~!%&*+-./<=>?@^|~) (x : int) (y : int) = x * y

F#会将运算符编译为程序集中具体的方法,其命名遵循一定规则。不过在使用时我们并不需要关心这些。如果您对这方面的具体信息感兴趣,可以参考MSDN中《Operator Overloading (F#)》一文(http://msdn.microsoft.com/en-us/library/dd233204(VS.100).aspx)。

前缀与中缀运算符

前缀(prefix)运算符,表示运算符出现在运算数之前,例如“负号”便是个前缀运算符:

let a = 1

-a |> printfn "%i" // -1

中缀(postfix)运算符,表示运算符出现在两个运算数之间,例如最常见的“加法”便是个中缀运算符:

1 + 2 |> printfn "%i" // 3

在自定义运算符时,F#并不允许我们指定某个运算符是前缀还是中缀运算符,编译器会自动根据运算符的“首字母”来决定它是前缀还是中缀的。例如,首字母为“感叹号”的运算符便是“前缀”运算符:

let (!+) (x:int) (y:int) = x + y

根据这个规则,我们只能将“!+”作为前缀运算符来使用:

1 (!+) 2 |> printfn "%i" // 编译失败!

!+ (!+ 1 2) 3 |> printfn "%i" // 6

关于某个字母表示前缀还是中缀运算符,您可以参考《Operator Overloading (F#)》一文中的表格。可以发现,大部分运算符都是中缀的,而只有少数是前缀运算符。至于后缀运算符……F#并不支持后缀运算符。

Tags:
相关文章列表: