4

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

4

5 回答 5

5

在 F# 中内部表示(2 元素)元组的System.Tuple<'T1, 'T2>类型实际上已经具有属性Item1Item2,但是这些被 F# 编译器隐藏了。一种将扩展成员添加到元组的明显方法并不能解决问题,所以我不希望这会起作用(但可能有一些我不知道的解决方法)。

一般来说,我认为模式匹配比Item1,Item2等成员更可取(并且 C# 3.0 程序员在使用元组时经常要求模式匹配支持:-))。

原因是模式匹配迫使你命名事物。比较这两个代码片段:

let (width, height) = tuple
width * height

和使用属性的版本:

tuple.Item1 * tuple.Item2

第二个有点短,但绝对不那么可读。

于 2013-01-02T00:04:06.047 回答
2

不完美,但我正在使用它。(我从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 元组,而无需修改函数体。相反,我们必须修改参数类型声明。它更加轻松。

于 2016-09-15T02:18:18.843 回答
0

我认为,您要问的不是很实用的方式。您可以使用实例方法创建自己的类型,但同时您会丢失函数式编程的许多方面,例如模式匹配。

除此之外,DU似乎是要走的路:

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>. 尝试这样做会导致错误:

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

于 2013-01-02T00:14:01.403 回答
0

您还可以使用fstandsnd函数来获取您想要的值(如果您真的想的话,显然可以为第三、第四等编写您自己的值)。

于 2013-01-02T09:10:27.853 回答
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()

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

于 2017-12-10T08:25:18.400 回答