是否可以为F#元组编写扩展方法?例如,要添加实例方法.Item1和.Item2(如System.Tuple),它们等同于为2元组调用fst和snd?
答案 0 :(得分:5)
F#内部表示(2元素)元组的System.Tuple<'T1, 'T2>
类型实际上已经具有属性Item1
和Item2
,但这些类型被F#编译器隐藏。将扩展成员添加到元组的一种显而易见的方法不起作用,所以我不希望这样做(但可能有一些我不知道的解决方法)。
一般来说,我认为模式匹配比Item1
,Item2
等成员更受欢迎(C#3.0程序员在使用元组时经常要求模式匹配支持: - ))。
原因是模式匹配迫使您命名。比较这两个代码段:
let (width, height) = tuple
width * height
以及使用属性的版本:
tuple.Item1 * tuple.Item2
第二个更短,但可读性更低。
答案 1 :(得分: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元组而无需修改函数体。相反,我们必须修改参数类型声明。它更轻松。
答案 2 :(得分: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所指出的,您不能将属性命名为Item1
和Item2
,因为它们已存在于System.Tuple<'T1, 'T2>
中。试图这样做会导致错误:
错误FS2014:写入二进制文件[filename]时出现问题:类型[...]的pass2出错,错误:类型MyTuple`2的pass2出错,错误:属性表中重复条目'Item1'
答案 3 :(得分:0)
您还可以使用fst
和snd
函数来获取您想要的值(如果您真的想要,显然可以为第三,第四等编写自己的值。)
答案 4 :(得分: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()
但我同意通过方法访问元组的元素不是一个好主意,模式匹配是最好的方法。