3

okay, this might be a silly question.

So I have some tuples of size 4 so like (int,int,int,int)

If it were a 2 tuple I could use fst(myTuple) to refer to the first element. How could I, say, refer to the third element of a 4 tuple?

jeff
  • 968
  • 1
  • 6
  • 13

4 Answers4

9

Use pattern matching:

let tup = 1, 2, 3, 4
let _,_,third,_ = tup
printfn "%d" third // displays "3"

This is described directly in the MSDN documentation for tuples: Tuples (F#)

ildjarn
  • 62,044
  • 9
  • 127
  • 211
  • If you have an irrational fear of pattern matching you can use the `ItemN` property. – Daniel Mar 09 '11 at 22:04
  • 5
    @Daniel : I don't believe those properties are accessible within F# without either using reflection or boxing then downcasting to the appropriate `System.Tuple` type first. – ildjarn Mar 09 '11 at 22:07
  • Strange. Never noticed that. I assumed it works since they're represented as `System.Tuple`s. – Daniel Mar 09 '11 at 22:10
  • 2
    @Daniel - I think F# hides them since it supports tuples of infinite length: .NET tuples only go up to 8. Under the hood, F# nests tuples in the 8th position to represent long tuples. – Stephen Swensen Mar 09 '11 at 23:06
  • Awesome, thanks! I knew it had to be something simple like this. – jeff Mar 09 '11 at 23:29
5

Here's a version of @Daniels novel solution which calculates Rest offsets of the underlying Tuple representation to support position-based access on arbitrarily long tuples. Error handling omitted.

let (@) t idx =
    let numberOfRests = (idx - 1) / 7
    let finalIdx = idx - 7 * numberOfRests
    let finalTuple =
        let rec loop curTuple curRest =
            if curRest = numberOfRests then curTuple
            else loop (curTuple.GetType().GetProperty("Rest").GetValue(curTuple, null)) (curRest+1)
        loop t 0

    finalTuple
     .GetType()
     .GetProperty(sprintf "Item%d" finalIdx)
     .GetValue(finalTuple, null) 
     |> unbox

//fsi usage:
> let i : int = (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36)@36;;

val i : int = 36
Stephen Swensen
  • 22,107
  • 9
  • 81
  • 136
4

For the sheer novelty, here's an overloaded operator that works for tuples of any* size.

let (@) t idx =
    match t.GetType().GetProperty(sprintf "Item%d" idx) with
    | null -> invalidArg "idx" "invalid index"
    | p -> p.GetValue(t, null) |> unbox

//Usage
let t = 4, 5, 6
let n1 : int = t@1 //4
let i = 2
let n2 = t@i //5

* Any, in this context, has a more limited meaning, specifically, up to 7.

Daniel
  • 47,404
  • 11
  • 101
  • 179
1

If you want random access to a generally sized tuple, then it is not possible. For any given size, you can follow ildjarn's answer (extending it for four, five, etc.), but that it the only (functional) way.

A possibility for tuples in general, is to convert it to a list first, as found here, but that's not too pretty as it requires reflection.

Community
  • 1
  • 1
Ken Wayne VanderLinde
  • 18,915
  • 3
  • 47
  • 72