F#:如何在不牺牲运行时性能的情况下为元组,三元组和四元组提供fst

cit*_*kid 4 f# tuples inline

对于具有2个元素的基本元组,我们有fst和snd:

let t2 = (2, 3)
fst t2 // = 2
snd t2 // = 3
Run Code Online (Sandbox Code Playgroud)

简单.现在有3个元素

let t3 = (2, 3, 4)
Run Code Online (Sandbox Code Playgroud)

我们如何访问第3个元素?msdn有一个答案(http://msdn.microsoft.com/en-us/library/dd233200.aspx):

let third (_, _, c) = c
Run Code Online (Sandbox Code Playgroud)

还很容易.但实际上是欺骗性的,因为我们不能在这样的三元组上使用fst和snd:

fst t3

error FS0001: Type mismatch. Expecting a int * int but given a int * int * int    
The tuples have differing lengths of 2 and 3
Run Code Online (Sandbox Code Playgroud)

因此,具有挑战性的问题:我们怎样才能提供的功能FST元组,三人间和不sacrifying运行时性能四倍?

方法不起作用:

A)简单地增加的FST( ,,_)

let fst (a,_,_) = a // hides fst (a,b), so tuples dont work anymore
Run Code Online (Sandbox Code Playgroud)

B)任何反射技巧,因为性能受到影响或棘手的功能组合导致在jitted代码级别的额外调用.

C)扩展方法(没有引导我任何地方,也许别人有更好的想法)

type Tuple<'T1, 'T2, 'T3> with
    member o.fst(a,b,c) = a
    static member fst(a,b,c) = a
Run Code Online (Sandbox Code Playgroud)

除了有其他签名(t3.fst())我甚至无法使用Tuples扩展,尽管它们是类(不是结构).

d)

let fst = function 
          | (a,b) -> a
          | (a,b,c) -> a // FS0001: Type mismatch
Run Code Online (Sandbox Code Playgroud)

这个问题意味着它的制定,而不是解决方法.实际上我相信答案是不可能提供在元组上运行的常用功能.

Gus*_*Gus 9

使用这种方法重载技术与内联函数相结合在运行时完全没有任何损失,因为重载解析在编译时发生,并且重载函数在调用站点处内联.

有更复杂的方法可以在库中组织这些通用元组函数,@ MaicicioScheffer已经发布了一些链接,显示了更多使用基本相同技术的实验.

这是一个非常短的独立代码片段,用于重载函数fst以使其与其他元组大小一起使用:

type Fst = Fst with
    static member ($) (Fst, (x1,_)) = x1
    static member ($) (Fst, (x1,_,_)) = x1
    static member ($) (Fst, (x1,_,_,_)) = x1
    // more overloads

let inline fst x = Fst $ x
Run Code Online (Sandbox Code Playgroud)

  • 很酷,这正是我想要的.性能显然略微落后于Operators.fst(20%到50%的开销),但它在相同的维度上并且很好.将尝试将Operators.fst附加到第一个案例,但无论如何都不是什么大问题.谢谢! (2认同)

Tom*_*cek 5

正如其他人已经提到的那样,有(或多或少精心设计和丑陋)的方法可以做到这一点,但我认为这里应该说的重要一点是,如果你正在使用F#,你就不需要这样了.

如果你需要一个可以有两个,三个或四个元素的数据结构,那么你应该使用一个列表而不是使用元组(在其他语言中,元组有时用于此,而不是在F#中).

如果您正在使用F#中的元组,那么您通常会使用少量元素(两个或三个),然后使用模式匹配来分解它们更具可读性.比较以下内容:

// Pattern matching makes you name things, so this is quite clear
// (we don't need the third element, so we're ignoring it)
let width, height, _ = tuple
width * height

// If you had fst3 and snd3, then you could write it using just
// a single line, but the code becomes hard to follow
(fst3 tuple) * (snd3 tuple)
Run Code Online (Sandbox Code Playgroud)

所以,我认为你根本不应该这样做.现在,如果您使用更复杂的数据结构(具有更多元素),那么最好使用类似记录的东西.如果您的第一个元素始终是"名称",并且您希望具有访问名称的功能,则可以使用接口(或区分联合和模式匹配).

type INamed = 
  abstract Name : string

type Person = 
  { SomeName : string }
  interface INamed with
    member this.Name = this.SomeName

type FancyPerson = 
  { SomeName : string; Title : string }
  interface INamed with
    member this.Name = this.SomeName
Run Code Online (Sandbox Code Playgroud)

正确的方法取决于你正在做什么.但无论如何,最有可能更好地表达您的需求.