tags:

views:

2996

answers:

10

I'd like to be able to swap out two variables without the use of a temp variable in C#. Can this be done?

decimal startAngle = Convert.ToDecimal(159.9);
decimal stopAngle = Convert.ToDecimal(355.87);

//swap each:
//startAngle becomes: 355.87
//stopAngle becomes: 159.9
+6  A: 

Not in C#. In native code you might be able to use the triple-XOR swap trick, but not in a high level type-safe language. (Anyway, I've heard that the XOR trick actually ends up being slower than using a temporary variable in many common CPU architectures.)

You should just use a temporary variable. There's no reason you can't use one; it's not like there's a limited supply.

Jens Alfke
Most types that the XOR thing works for would fit in a register so the compiler shouldn't allocate stack space for it anyway.
BCS
True, but it's more complex than that. You rarely need to swap values on a assembler level. The swapping can often be done as a side-effect of other arithmetic. Most of the time the swap is just required to express things in a high level language. After compiling the swap is no more and thus costs no time at all :-)
Nils Pipenbrinck
+16  A: 

yep, use this code:

startAngle = startAngle+stopAngle;
stopAngle = startAngle-stopAngle;
startAngle = startAngle-stopAngle;
CommuSoft
Yes, this does the trick
Nev_Rahd
That's fine for integers or fixed-point numbers. With floating point numbers you'll end up with minute rounding errors. They may or may not be big enough to matter, depends on how you're using the numbers.
Kennet Belenky
As long as you don't run into overflow issues, that works just fine
patjbs
@Kennet Belenky: indeed, but i don't see another work around, except a temporary value. So I think it's the only "good" way to solve this problem. (Sorry for my English, if I made mistakes)
CommuSoft
The only *good* way to solve this problem is to use a temp variable. "Clever" code like this (and, by "clever", I mean "stupid") is far less readable and obvious than the temp-variable solution. If I saw code like this from one of my minions, they'd be subject to sanctions and sent back to do it right. I'm not having a go at you specifically, @CommuSoft (since you answered the question), but the question itself was rubbish.
paxdiablo
@Pax - tell us how you really feel :P
BenAlabaster
No, no, that was me being tactful. You should hear me rant when the two kids have kept me up all night :-)
paxdiablo
That works for all the wrong reasons imaginable...
Marc Gravell
How in the world is this accepted as answer!
this. __curious_geek
A: 

for binary types you can use this funky trick:

a %= b %= a %= b;

As long as a and b are not the exact same variable (e.g. aliases for the same memory) it works.

BCS
also see Jens Alfke answer
BCS
+16  A: 

Yes, use this code:

    stopAngle = Convert.ToDecimal(159.9);
    startAngle = Convert.ToDecimal(355.87);

The problem is harder for arbitrary values. :-)

McWafflestix
lol - and how is that useful? :P
BenAlabaster
he didn't ask for useful! :-)
McWafflestix
What's with the downvotes? Are people really that humorless?
McWafflestix
If I use humor, @McW, I tend to mix it with a useful answer as well (or I do humor-only content in comments). The popup text for a downvote is "This answer is not helpful" which I guess, technically, some consider this answer isn't (it *is* helpful in terms of making us laugh (well, me anyway, I can't speak for the rest of the swarm), but not in terms of answering the question).
paxdiablo
That's good insight, Pax; in this case, though, the inherent question was flawed (as you yourself pointed out), and the example problem was underspecified (while being overspecific). The answer that got the nod (CommuSoft's) works perfectly well for the problem as stated, but what if the values to be swapped included one very close to MAX_DECIMAL or the like? I like to use humor to point out that for the question asked, the answer can be trivial; but the implication in this case was that the oversimplification extended beyond the answer to the question.
McWafflestix
+3  A: 

<deprecated>

You can do it in 3 lines using basic math - in my example I used multiplication, but simple addition would work also.

float startAngle = 159.9F;
float stopAngle = 355.87F;

startAngle = startAngle * stopAngle;
stopAngle = startAngle / stopAngle;
startAngle = startAngle / stopAngle;

Edit: As noted in the comments, this wouldn't work if y = 0 as it would generate a divide by zero error which I hadn't considered. So the +/- solution alternatively presented would be the best way to go.

</deprecated>


To keep my code immediately comprehensible, I'd be more likely to do something like this. [Always think about the poor guy that's gonna have to maintain your code]:

static bool Swap<T>(ref T x, ref T y)
{
    try
    {
        T t = y;
        y = x;
        x = t;
        return true;
    }
    catch
    {
        return false;
    }
}

And then you can do it in one line of code:

float startAngle = 159.9F
float stopAngle = 355.87F
Swap<float>(ref startAngle, ref stopAngle);

Or...

MyObject obj1 = new MyObject("object1");
MyObject obj2 = new MyObject("object2");
Swap<MyObject>(ref obj1, ref obj2);

Done like dinner...you can now pass in any type of object and switch them around...

BenAlabaster
little bit equal to my solution, but notice that multiplications and division operation cost a lot of CPU-time. And the upper- and lower-bounds of a float are more easy to reach with multiplication.
CommuSoft
What do you mean with "Decimal doesn't allow for decimal places"? That sounds confusing as the decimal type *does* represent real numbers with a high precision (decimal d = 9.1m; is perfectly valid in C#).
0xA3
and what if one of the variables is zero (or both), the second division will cause an error (division by zero). And the function created could better return false to warn the user, that the swap operation wasn't completed.
CommuSoft
@divo: I stand corrected... I must've missed that class, I'd been trying to assign Decimal n = 9.1; and it just gives me a compiler error. Missed the M on the end :P Answer modified accordingly.
BenAlabaster
@CommuSoft: Point taken, I hadn't considered that. In that case, the +/- would surely be the better way to go.
BenAlabaster
What's up with your swap method? Why does it return a bool, why is that bool always true (if it exists)? Why does it swallow all exceptions (which could only be a ThreadAbortException in this case, I believe, since it doesn't allocate memory or enlarge the call stack)?
Doug McClean
+28  A: 

At the risk of downvotes, the right way to swap two variables is:

decimal startAngle = Convert.ToDecimal(159.9);
decimal stopAngle = Convert.ToDecimal(355.87);

decimal tempDecimal = startAngle;
startAngle = stopAngle;
stopAngle = tempDecimal;

In other words, use a temporary variable.

There you have it. No clever tricks, no maintainers of your code cursing you for decades to come, no entries to The Daily WTF, and no spending too much time trying to figure out why you needed it in one operation anyway since, at the lowest level, even the most complicated language feature is a series of simple operations.

Just a very simple, readable, easy to understand, t = a; a = b; b = t; solution.

In my opinion, developers who try to use tricks to, for example, "swap variables without using a temp" or "Duff's device" are just trying to show how clever they are (and failing miserably).

I liken them to those who read highbrow books solely for the purpose of seeming more interesting at parties (as opposed to expanding your horizons).

Solutions where you add and subtract, or the XOR-based ones, are less readable and most likely slower than a simple "temp variable" solution (arithmetic/boolean-ops instead of plain moves at an assembly level).

Do yourself, and others, a service by writing good quality readable code.

That's my rant. Thanks for listening :-)

As an aside, I'm quite aware this doesn't answer your specific question (and I'll apologise for that) but there's plenty of precedent on SO where people have asked how to do something and the correct answer is "Don't do it".

paxdiablo
+1 Great advice.
Steve Rowe
+1; and for more reasons: with the +/- (etc) tricks you are doing unnecessary arithmetic. With integers, that may be just about acceptable *at a push* (the rounding/overflow aren't issues, and the CPU cost is virtually nil), but with decimals, add and subtract are non-trivial operations. It can't even use the FPU, as they aren't float/double. So use a temp variable already!!!
Marc Gravell
of course this is the best way, but it was explicit asked without temp variable
CommuSoft
I doubt you'll get many downvotes for this; apparently the way to get downvotes is to post something that requires a sense of humor. :( +1 for the good advice, though; I agree COMPLETELY with your opinion on "tricky developers". They're almost as bad as humorless ones. :-)
McWafflestix
+1 I agree with you. When you start complicating simple stuff you end up with all sort of problems to solve in the next few years...
Nelson Reis
It's just a question. No need to take things personally.
kervin
+4  A: 

For completeness, here is the binary XOR swap:

int x = 42;
int y = 51236;
x ^= y;
y ^= x;
x ^= y;

This works for all atomic objects/references, as it deals directly with the bytes, but may require an unsafe context to work on decimals or, if you're feeling really twisted, pointers. And it may be slower than a temp variable in some circumstances as well.

thecoop
A: 

a=a+b b=a-b a=a-b

+2  A: 
int a = 4, b = 6;

a^= b^= a^= b;

Works for all types including strings and floats.

this. __curious_geek
I hope the XOR swap will be forgotten one day.
Helper Method
A: 

Beware of your environment!

For example, this doesn’t seem to work in ECMAscript

y^=x^=y^=x;

But this does

x^=y^=x;y^=x;

My advise? Assume as little as possible.

Codzart