F#中的重载+运算符

时间:2011-10-08 07:23:42

标签: f# operator-overloading

所以我有这个:

open System
open System.Linq
open Microsoft.FSharp.Collections
type Microsoft.FSharp.Collections.List<'a> with
    static member (+) (First : List<'a>) (Second : List<'a>) =
        First.Concat(Second)

let a = [1; 2; 3; 4; 54; 9]
let b = [3; 5; 6; 4; 54]


for x in List.(+) a b do
    Console.WriteLine(x)

我希望将最后一行转换为

for x in a + b do
    Console.WriteLine(x)

但这样做会给我一个

The type 'int list' does not support any operands named '+'

网络上的文档和示例很有趣,尽管我的google-fu我还是无法让它工作。基本上,来自python背景,我想让我的列表操作语法像我习惯的那样简洁:它不应该在中缀表示法中需要超过1个字符。

5 个答案:

答案 0 :(得分:8)

请注意,@已经是一个1-char中缀运算符,用于连接列表。

答案 1 :(得分:7)

实际上,有一种方法可以使用静态约束和重载来“重新连接”现有的运算符。

type ListExtension = ListExtension with
    static member        (?<-) (ListExtension, a , b) = a @ b
    static member inline (?<-) (ListExtension, a , b) = a + b

let inline (+) a b = (?<-) ListExtension a b

// test

let lst = [1;2] + [3;4]
// val lst : int list = [1; 2; 3; 4]

let sum = 1 + 2 + 3 + 4
// val sum : int = 10

通过使用三元运算符,将自动推断静态约束,另一种选择是创建方法并手动编写约束。 第一个重载覆盖了您要添加的情况(列表),第二个覆盖了现有的定义。

现在,您可以在代码中执行以下操作:

for x in (+) a b do
    Console.WriteLine(x)

这不会破坏数字类型的现有(+)

答案 2 :(得分:5)

首先,覆盖运算符应该以元组形式声明,而不是以携带形式声明。在你的情况下:

type Microsoft.FSharp.Collections.List<'a> with
    static member (+) (first: List<'a>, second: List<'a>) =
        first.Concat(second)

其次,在您修复之后,编译器会引发"Extension members cannot provide operator overloads. Consider defining the operator as part of the type definition instead."警告。有一些解决方法已经在Overload operator in F#: (/)中进行了详尽的讨论。

答案 3 :(得分:3)

正如其他答案所指出的,您无法将+的实现添加到现有类型,因为会忽略扩展成员并且独立let绑定会隐藏默认(重载)实现。

如果你想使用+(因为F#库包含operator @而不是真的需要),你必须编写直接支持运算符的F#list包装器:

open System.Collections
open System.Collections.Generic

/// Wrapper for F# list that exposes '+' operator and 
/// implements 'IEnumerable<_>' in order to work with 'for'
type PlusList<'T>(list : list<'T>) =
  member x.List = list
  static member (+) (first : PlusList<'a>, second : PlusList<'a>) =
    first.List @ second.List
  interface IEnumerable with
    member x.GetEnumerator() = (list :> IEnumerable).GetEnumerator()
  interface IEnumerable<'T> with
    member x.GetEnumerator() = (list :> IEnumerable<_>).GetEnumerator()

// Simple function to wrap list
let pl l = PlusList<_>(l)

let a = pl [1; 2; 3; 4; 54; 9]
let b = pl [3; 5; 6; 4; 54]

for x in a + b do
  System.Console.WriteLine(x)

答案 4 :(得分:0)

我认为使用扩展方法的运算符重载不起作用。你可以做的是使用:

为列表(+)定义一个全局运算符重载
let inline (+) (f : List<'a>) (s : List<'a>) = f.Concat(s)