F#元组的扩展方法

5

是否可以为F#元组编写扩展方法?例如,添加实例方法.Item1和.Item2(类似于System.Tuple),这些方法等效于对2元组调用fst和snd?

5个回答

5
在F#中,System.Tuple<'T1, 'T2>类型实际上已经具有Item1Item2属性来表示(二元)元组,但这些属性被F#编译器隐藏。一个明显的方法是添加扩展成员到元组,但这并不奏效,因此我不指望这种方法可行(但可能有我不知道的解决方法)。
通常,我认为模式匹配比像Item1Item2等成员更可取(C#3.0程序员在使用元组时通常会要求模式匹配支持 :-))。
原因是模式匹配强制您命名事物。比较这两个代码片段:
let (width, height) = tuple
width * height

使用属性的版本:

tuple.Item1 * tuple.Item2

第二个长度稍短,但可读性明显较差。

谢谢Tomas。我同意,但我仍然想知道是否有一种解决方法(而不是像bytebuster下面建议的那样使用新类型)? - mpeac

3

虽然不是完美的,但我正在使用这个。 (我从http://www.fssnip.net/6V借用了原始代码,并进行了小修改。)

[<AutoOpen>]
module TupleExtensions =
  type System.Tuple with
    static member Item1(t) = let (x,_) = t in x
    static member Item1(t) = let (x,_,_) = t in x
    static member Item1(t) = let (x,_,_,_) = t in x
    static member Item1(t) = let (x,_,_,_,_) = t in x
    static member Item1(t) = let (x,_,_,_,_,_) = t in x
    static member Item1(t) = let (x,_,_,_,_,_,_) = t in x

    static member Item2(t) = let (_,x) = t in x
    static member Item2(t) = let (_,x,_) = t in x
    static member Item2(t) = let (_,x,_,_) = t in x
    static member Item2(t) = let (_,x,_,_,_) = t in x
    static member Item2(t) = let (_,x,_,_,_,_) = t in x
    static member Item2(t) = let (_,x,_,_,_,_,_) = t in x

    static member Item3(t) = let (_,_,x) = t in x
    static member Item3(t) = let (_,_,x,_) = t in x
    static member Item3(t) = let (_,_,x,_,_) = t in x
    static member Item3(t) = let (_,_,x,_,_,_) = t in x
    static member Item3(t) = let (_,_,x,_,_,_,_) = t in x

    static member Item4(t) = let (_,_,_,x) = t in x
    static member Item4(t) = let (_,_,_,x,_) = t in x
    static member Item4(t) = let (_,_,_,x,_,_) = t in x
    static member Item4(t) = let (_,_,_,x,_,_,_) = t in x

    static member Item5(t) = let (_,_,_,_,x) = t in x
    static member Item5(t) = let (_,_,_,_,x,_) = t in x
    static member Item5(t) = let (_,_,_,_,x,_,_) = t in x

    static member Item6(t) = let (_,_,_,_,_,x) = t in x
    static member Item6(t) = let (_,_,_,_,_,x,_) = t in x

    static member Item7(t) = let (_,_,_,_,_,_,x) = t in x  

如何使用:

let t = (1, 2, 3)
let item1 = Tuple.Item1(t)

这里定义的Tuple.Item1相比fst具有优势:它对于项数是多态的。一旦我们使用这些扩展方法编写了使用n元组的函数,我们就可以在不修改函数体的情况下将其扩展到n+1元组。相反,我们必须修改参数类型声明。这更加轻松。


0

解决方法是使用C#风格的扩展定义。

这将完美地工作:

open System.Runtime.CompilerServices

[<Extension>]
type TupleExtensions () = 
    [<Extension>] static member First((a,b)) = a
    [<Extension>] static member First((a,b,c)) = a

let x = (1,2).First()
let y = (1,2,3).First()

但我同意这并不是一个好主意,通过方法访问元组的元素,模式匹配是最好的方式。


0

我认为你所问的并不是非常函数化的方法。你可以使用实例方法来创建自己的类型,但同时你会失去许多函数式编程的方面,例如模式匹配。

除此之外,联合数据似乎是正确的方式:

type MyTuple<'T, 'U> =
    | MyTuple of 'T * 'U
    with
    member this.MyItem1 = match this with | MyTuple(x,y) -> x
    member this.MyItem2 = match this with | MyTuple(x,y) -> y

let x = MyTuple(42, "foo")
let y1 = x.MyItem1    // 42
let y2 = x.MyItem2    // "foo"

正如Tomas Petricek所指出的那样,您不能将属性命名为Item1Item2,因为它们已经存在于System.Tuple<'T1, 'T2>中。尝试这样做会导致错误:

error FS2014:编写二进制文件[filename]时出现问题:类型[...]在pass2中出错,错误:MyTuple`2的pass2中出错,错误:属性表中存在重复条目'Item1'


0

你也可以使用 fstsnd 函数来获取你想要的值(如果你真的想的话,还可以自己编写第三个、第四个等函数)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接