4

I hope this isn't too off-topic/opinion-based, I'm new here.

I want to pass three elements of the same type into a function. The three elements are unique cards of a deck of cards, so they're not intrinsically connected (unlike, say, the coordinates of a point). The array or tuple will only exist for the sake of comparing these three cards in a function.

I see three potential options:

  1. Pass them in an array or other Collection
  2. Pass them as a tuple
  3. Pass them all separately, and have three parameters for the function call

My question is, when (if ever) should arguments be passed in separately, as an array, or as a tuple, and are there pros/cons of each?

I'm using Swift in this particular example, but if there are general principles that apply, those are welcome too.

Edit: I simplified the problem to get to the root of my question, but in case it might be helpful, here's some more context. I'm programming a game of Set. The cards aren't a standard 52-card deck, but instead a deck of 81 unique cards. Each card has shapes on it, which differ in type of shape, number of shapes, color, and shading/pattern. A player chooses three cards to try to make a set, so the comparison I'm making is just to compare each of those four characteristics for equality. So I'm essentially passing in three cards to the function (specifically, I'm passing the cards' characteristics property, which is a struct).

10
  • 10
    4. Create a class for this type with three properties for cards and give this class a meaningful name.
    – Rik D
    Jul 3, 2023 at 17:35
  • True, that's another option. I guess the reason I didn't consider that is that in this specific case is, would it merit creating a class for this one function call/comparison? My thought is no, but I definitely could be wrong.
    – David
    Jul 3, 2023 at 17:48
  • The general rule is to avoid tuples, they are lazy
    – Ewan
    Jul 3, 2023 at 19:42
  • 1
    If this group of cards has a name in your business domain, it probably deserves a class @David. If it doesn't but the 3 cards have a name individually, tuple or array are not the best choice, just use 3 parameters. Jul 4, 2023 at 8:03
  • 1
    @Rik Tuples are often used as ad hoc structs. Unless you have a reason why you want a type with its own name creating a class is pointless. On the other hand, just passing individual parameters may be just fine. Tuples as return values are more useful because you can only return one value.
    – gnasher729
    Jul 5, 2023 at 20:54

4 Answers 4

3

Consider how the calling function will hold these objects - will it need to construct an array or tuple just to call this new function, or will it already have them in an array or tuple for other reasons? If the calling function needs to construct an array just for this new function, it may be best to pass the arguments in separately.

Is there a logical grouping for these objects - are they cards in a poker hand, for instance? In this situation, putting them into a new class (with a good name!) altogether is probably best, but a tuple/array is another good option.

Could you foresee a need to have more than 3 objects passed in (maybe you want to choose the highest-value of n cards)? Three arguments is not unmanageable, but many more than that can be. If you have good reason to think that you'll need to handle more arguments, passing them in separately is not a good idea (although some languages support an indeterminate-number of individual arguments, like C# params, which can be appropriate - depending on how the function is normally called)

Does the ordering of the arguments matter? Is there a meaningful difference between the first and third card? Will the ordering be switched (e.g., is this function sorting the cards in some way)? In those cases, arrays can be helpful as they lend themselves to an ordering / sorting.

If however, the cards represent the highest cards of each of 3 players, for example, you don't want to change the order at all; a new class is best there, to explicitly define what each card represents.

In the end, there's no one-size-fits-all approach.

1
  • You have practically zero additional source code to construct an array or tuple parameter. I think it’s just [] or () around the three values.
    – gnasher729
    Jul 5, 2023 at 20:50
3

Does position matter or order?

If you accept an array I expect the logic to gracefully handle arrays of different lengths (longer or shorter). Insisting that the array is sorted is fine. But caring about such and such being the 3rd element is a bit surprising and weird.

But I don’t expect any of that from a tuple. I expect tuples to be all about what is in what position. I expect code to break if they grow. I never expect them to be sorted.

So pick the expectations that work best for what you’re doing.

Tuples work best when you can give the positions' names.

Arrays work best when you can give them numbers.

Oh, and objects work best if you’re in an object-oriented paradigm. But you kids today sure like your functional data.

1

I heard you say that from a single (52-card) deck we've been dealt three cards.

The three elements are unique cards of a deck of cards, so they're not intrinsically connected ...

That sounds like a set() to me.

If you move on to blackjack with several decks in the dealer's shoe, change it to a multiset. Probably the name you want to give your class is a Hand.

1
  • I edited my post, in my specific case I'm using three cards from an 81-card deck for Set. In my context, the cards start in an array, but when selected by the player, I'm checking if they form a set according to the game's rules.
    – David
    Jul 5, 2023 at 15:21
1

You can always replace a Tuple with a struct or class. in this case you might have

Set
{
   Card Card1
   Card Card2
   Card Card3
}

I think it would be common for this to be considered a prefered approach in all cases of tuple usage (see: https://stackoverflow.com/questions/13640322/what-and-when-to-use-tuple)

In your specific case I would say a collection is best for the following reasons.

  1. The user selects cards one at a time to build a set. So at some points in time the set has 0, 1, 2 or 3 cards.

  2. It is also an obvious variation on the game to have larger sets of more varied cards.

  3. You can imagine a generic compare function that works for any set, incomplete or not if you accept a collection of cards. ie a List or Array etc

However, its really a style choice. You could apply the same logic to Card and make it a collection of properties, But I think people would also object to a program ignoring classes and just passing around arrays of arrays to everything.

In fact if we do some digging on that post i linked we can find this article, which explains the driving for behind Tuple was interoperability with F#

While C# and VB.NET languages do not have the concept of a tuple as part of the core language, it is a common feature of many functional languages. When targeting such languages to the Micrososft.NET Framework, language developers had to define a managed representation of a tuple, which leads to unnecessary duplication. One language suffering that problem was F#, which previously had defined its own tuple type in FSharp.Core.dll but will now use the tuple added in Microsoft .NET Framework 4.

https://learn.microsoft.com/en-us/archive/msdn-magazine/2009/brownfield/building-tuple

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.