Zamień dwie zmienne bez użycia zmiennej tymczasowej

Chciałbym móc zamienić dwie zmienne bez użycia zmiennej tymczasowej w C#. Czy można to zrobić?

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

// Swap each:
//   startAngle becomes: 355.87
//   stopAngle becomes: 159.9
Author: Peter Mortensen, 2009-04-30

28 answers

Po pierwsze, Zamiana bez zmiennej tymczasowej w języku C# jest bardzo złym pomysłem.

Ale ze względu na odpowiedź, możesz użyć tego kodu:

startAngle = startAngle + stopAngle;
stopAngle = startAngle - stopAngle;
startAngle = startAngle - stopAngle;

Problemy mogą jednak wystąpić z zaokrągleniem, jeśli obie liczby znacznie się różnią. Wynika to z natury liczb zmiennoprzecinkowych.

Jeśli chcesz ukryć zmienną tymczasową, możesz użyć metody użytkowej:

public static class Foo {

    public static void Swap<T> (ref T lhs, ref T rhs) {
        T temp = lhs;
        lhs = rhs;
        rhs = temp;
    }
}
 94
Author: Willem Van Onsem,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-16 15:52:36

The right sposób zamiany dwóch zmiennych to:

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

Innymi słowy, Użyj zmiennej tymczasowej.

Proszę bardzo. Żadnych sprytnych sztuczek, żadnych opiekunów Twojego kodu przeklinających Cię przez dziesięciolecia, żadnych wpisów do The Daily WTF , i żadnego spędzania zbyt wiele czasu próbując dowiedzieć się, dlaczego potrzebujesz go w jednej operacji, ponieważ na najniższym poziomie, nawet najbardziej skomplikowana funkcja językowa jest serią prostych operacji.

Po prostu bardzo proste, czytelne, łatwe do zrozumienia, t = a; a = b; b = t; rozwiązanie.

Moim zdaniem programiści, którzy próbują użyć sztuczek, aby na przykład "zamienić zmienne bez użycia temp" lub "urządzenie Duffa", próbują po prostu pokazać, jak sprytni są(i nieudolnie).

Porównuję ich do tych, którzy czytają książki highbrow wyłącznie po to, aby wydawać się ciekawszym na imprezach (w przeciwieństwie do poszerzania horyzontów).

Rozwiązania, w których dodajesz i odejmujesz, lub oparte na XOR, są mniej czytelne i najprawdopodobniej wolniejsze niż proste rozwiązanie "temp variable" (arytmetyka/boolean-ops zamiast zwykłych ruchów na poziomie montażu).

Zrób sobie i innym usługę, pisząc dobrej jakości czytelny kod. To mój żart. Dzięki za wysłuchanie: -)

Na marginesie, jestem całkiem świadomy, że to nie odpowiada na twoje konkretne pytanie (i za to przepraszam), ale jest wiele precedensów NA więc gdzie ludzie pytali, jak coś zrobić, a prawidłowa odpowiedź to "Nie rób tego".

 204
Author: paxdiablo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-05 08:32:40

Tak, użyj tego kodu:

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

Problem jest trudniejszy dla dowolnych wartości. :-)

 71
Author: Paul Sonier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-12 15:29:55

C # 7 wprowadził krotki która umożliwia zamianę dwóch zmiennych bez tymczasowej:

int a = 10;
int b = 2;
(a, b) = (b, a);

To przypisuje b do a i a do b.

 42
Author: TimothyP,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-25 10:04:54
int a = 4, b = 6;
a ^= b ^= a ^= b;

Działa dla wszystkich typów, w tym ciągów i pływaków.

 39
Author: this. __curious_geek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-12 15:30:08

BenAlabaster pokazał praktyczny sposób wykonania przełącznika zmiennej, ale klauzula try-catch nie jest potrzebna. Ten kod wystarczy.

static void Swap<T>(ref T x, ref T y)
{
     T t = y;
     y = x;
     x = t;
}

Użycie jest takie samo jak pokazano:

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

Możesz również użyć metody rozszerzenia:

static class SwapExtension
{
    public static T Swap<T>(this T x, ref T y)
    {
        T t = y;
        y = x;
        return t;
    }
}

Użyj go tak:

float startAngle = 159.9F;
float stopAngle = 355.87F;
startAngle = startAngle.Swap(ref stopAngle);

Oba sposoby używają zmiennej tymczasowej w metodzie, ale nie potrzebujesz zmiennej tymczasowej w miejscu wymiany.

 19
Author: Marcus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-05 19:33:15

Binarny swap XOR ze szczegółowym przykładem:

tabela prawdy xor :

a b a^b
0 0  0
0 1  1
1 0  1
1 1  0

Input:

a = 4;
b = 6;

Krok 1: a = A ^ b

a  : 0100
b  : 0110
a^b: 0010 = 2 = a

Krok 2: b = A ^ B

a  : 0010
b  : 0110
a^b: 0100 = 4 = b

Krok 3: a = A ^ b

a  : 0010
b  : 0100
a^b: 0110 = 6 = a

Wyjście:

a = 6;
b = 4;
 15
Author: Steven Muhr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-16 03:01:29

Nie w C#. W kodzie natywnym możesz używać Triple-XOR swap trick, ale nie w języku wysokiego poziomu bezpieczeństwa typu. (W każdym razie, słyszałem, że sztuczka XOR faktycznie kończy się wolniej niż użycie tymczasowej zmiennej w wielu popularnych architekturach procesorów.)

Należy po prostu użyć zmiennej tymczasowej. Nie ma powodu, dla którego nie można go użyć; to nie jest tak, że jest ograniczona podaż.

 11
Author: Jens Alfke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-29 23:13:58

Ze względu na przyszłych uczących się i ludzkość, zamieszczam tę poprawkę do aktualnie wybranej odpowiedzi.

Jeśli chcesz uniknąć używania zmiennych temp, istnieją tylko dwie rozsądne opcje , które biorą pod uwagę najpierw wydajność, a następnie czytelność.

  • użyj zmiennej temp w ogólnej metodzie Swap. (Absolutna najlepsza wydajność, obok inline temp variable)
  • użycie Interlocked.Exchange. (5.9 razy wolniej na mojej maszynie, ale to jest twój jedyny opcja, jeśli wiele wątków będzie wymieniać te zmienne jednocześnie.)

Rzeczy, które powinieneś nigdy robić:

  • nigdy nie używaj arytmetyki zmiennoprzecinkowej. (błędy powolne, zaokrąglenia i przepełnienia, trudne do zrozumienia)
  • nigdy nie używaj nie-prymitywnej arytmetyki. (powolne, przepełnione błędy, trudne do zrozumienia) Decimal nie jest prymitywny procesor i powoduje znacznie więcej kodu, niż zdajesz sobie sprawę.
  • nigdy nie używaj okresu arytmetycznego. lub bit hacki. (powolny, trudny do zrozumienia) to zadanie kompilatora. Może zoptymalizować dla wielu różnych platform.
Ponieważ wszyscy kochają twarde liczby, oto program, który porównuje Twoje opcje. Uruchom go w trybie release spoza Visual Studio, Aby Swap był inlined. Wyniki na moim komputerze (Windows 7 64-bit i5-3470):
Inline:      00:00:00.7351931
Call:        00:00:00.7483503
Interlocked: 00:00:04.4076651

Kod:

class Program
{
    static void Swap<T>(ref T obj1, ref T obj2)
    {
        var temp = obj1;
        obj1 = obj2;
        obj2 = temp;
    }

    static void Main(string[] args)
    {
        var a = new object();
        var b = new object();

        var s = new Stopwatch();

        Swap(ref a, ref b); // JIT the swap method outside the stopwatch

        s.Restart();
        for (var i = 0; i < 500000000; i++)
        {
            var temp = a;
            a = b;
            b = temp;
        }
        s.Stop();
        Console.WriteLine("Inline temp: " + s.Elapsed);


        s.Restart();
        for (var i = 0; i < 500000000; i++)
        {
            Swap(ref a, ref b);
        }
        s.Stop();
        Console.WriteLine("Call:        " + s.Elapsed);

        s.Restart();
        for (var i = 0; i < 500000000; i++)
        {
            b = Interlocked.Exchange(ref a, b);
        }
        s.Stop();
        Console.WriteLine("Interlocked: " + s.Elapsed);

        Console.ReadKey();
    }
}
 11
Author: jnm2,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-22 14:02:15

Można to zrobić w 3 linijkach za pomocą podstawowej matematyki - w moim przykładzie użyłem mnożenia, ale proste dodawanie też by działało.

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

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

Edit: jak wspomniano w komentarzach, to nie zadziała, jeśli y = 0, ponieważ wygeneruje błąd dzielenia przez zero, którego nie brałem pod uwagę. Tak więc alternatywnie przedstawione rozwiązanie +/- byłoby najlepszym rozwiązaniem.


Aby mój kod był natychmiast zrozumiały, byłbym bardziej skłonny zrobić coś takiego jak to. [Zawsze pomyśl o biedaku, który będzie musiał utrzymać Twój kod]:

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

I wtedy możesz zrobić to w jednej linijce kodu:

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

Lub...

MyObject obj1 = new MyObject("object1");
MyObject obj2 = new MyObject("object2");
Swap<MyObject>(ref obj1, ref obj2);
Zrobione jak kolacja...możesz teraz przejść w dowolnym rodzaju obiektu i przełączać je wokół...
 7
Author: BenAlabaster,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-20 13:01:53

Jeśli możesz zmienić z decimal na double możesz użyć klasy Interlocked. Prawdopodobnie będzie to dobry sposób na zamianę wydajności zmiennych. Również nieco bardziej czytelny niż XOR.

var startAngle = 159.9d;
var stopAngle = 355.87d;
stopAngle = Interlocked.Exchange(ref startAngle, stopAngle);

Msdn: Interlocked.Metoda Wymiany (Double, Double)

 6
Author: Robert Fricke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-30 07:39:45

Dla kompletności, oto binarny swap XOR:

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

Działa to dla wszystkich obiektów/odniesień atomowych, ponieważ dotyczy bezpośrednio bajtów, ale może wymagać niebezpiecznego kontekstu do pracy z dziesiętnikami lub, jeśli czujesz się naprawdę Pokręcony, wskaźnikami. I może być wolniejszy niż zmienna temp w niektórych okolicznościach, jak również.

 5
Author: thecoop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-01 09:42:37

Uważaj na otoczenie!

Na przykład, to nie działa w ECMAscript
y ^= x ^= y ^= x;

Ale to robi

x ^= y ^= x; y ^= x;
Moja rada? Zakładaj jak najmniej.
 5
Author: Codzart,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-16 03:00:12

W C # 7, możesz użyć tuple deconstruction, aby osiągnąć żądany swap w jednej linii, i jest jasne, co się dzieje.

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

(startAngle, stopAngle) = (stopAngle, startAngle);
 4
Author: jdphenix,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-22 01:12:41

W C # 7:

(startAngle, stopAngle) = (stopAngle, startAngle);
 4
Author: tomasz_kajetan_stanczak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-29 13:39:25

Prosty sposób zamiany 2 liczb w jednej linii:

a=(a+b)-(b=a);

Eg: a= 1, b=2

Krok 1: a=(1+2) - (b=1)

Krok 2: a=3-1

= > a = 2 i b = 1


Efektywnym sposobem jest użycie:

C Programowanie: (x ^= y), (y ^= x), (x ^= y);

Java: x = x ^ y ^ (y = x);

Python: x, y = y, x

Uwaga: najczęstsze błędy popełniane przez ludzi: //Swap using bitwise XOR (Wrong Solution in C / C++)

x ^= y ^= x ^= y; 

Źródło: GeeksforGeek

 3
Author: Utsav Dusad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-05 21:12:41
a = a + b
b = a - b
a = a - b

َ

 3
Author: Peter Mortensen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-16 02:58:21

Dla typów binarnych możesz użyć tej funky trick:

a %= b %= a %= b;

Dopóki a i b nie są dokładnie tą samą zmienną (np. aliasy dla tej samej pamięci) to działa.

 2
Author: BCS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-16 02:55:35

Mam nadzieję, że to pomoże...

using System;

public class Program
{
    public static void Main()
    {
        int a = 1234;
        int b = 4321;

        Console.WriteLine("Before: a {0} and b {1}", a, b);

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

        Console.WriteLine("After: a {0} and b {1}", a, b);
    }
}
 2
Author: PalakM,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-16 03:03:33
startAngle = (startAngle + stopAngle) - (stopAngle = startAngle);
 1
Author: kokabi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-14 09:30:18

Jeśli chcesz zamienić 2 zmienne łańcuchowe:

a = (a+b).Substring((b=a).Length);

Metoda pomocnicza:

public static class Foo {
    public static void SwapString (ref string a, ref string b) {
       a = (a+b).Substring((b=a).Length);
    }
}

Użycie będzie wtedy:

string a="Test 1";
string b="Test 2";
Foo.SwapString(a, b);
 1
Author: HGMamaci,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-08 13:48:48

Tutaj kolejne podejście w jednej linii:

decimal a = 159.9m;
decimal b = 355.87m;

a = b + (b = a) - b;
 0
Author: fubo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-24 12:46:34

Możemy to zrobić wykonując prostą sztuczkę

a = 20;
b = 30;
a = a+b; // add both the number now a has value 50
b = a-b; // here we are extracting one number from the sum by sub
a = a-b; // the number so obtained in above help us to fetch the alternate number from sum
System.out.print("swapped numbers are a = "+ a+"b = "+ b);
 0
Author: cammando,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-30 17:37:17

Oto jakiś inny proces zamiany dwóch zmiennych

//process one
a=b+a;
b=a-b;
a=a-b;
printf("a= %d  b=  %d",a,b);

//process two
a=5;
b=10;
a=a+b-(b=a);
printf("\na= %d  b=  %d",a,b);

//process three
a=5;
b=10;
a=a^b;
b=a^b;
a=b^a;
printf("\na= %d  b=  %d",a,b);

//process four
a=5;
b=10;
a=b-~a-1;
b=a+~b+1;
a=a+~b+1;
printf("\na= %d  b=  %d",a,b);
 0
Author: A.A Noman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-11 19:16:40

Z krotkami

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

(startAngle, stopAngle) = (stopAngle, startAngle);
 0
Author: Zu1779,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-04 07:22:52
var a = 15;
var b = -214;
a = b | !(b = a);
To działa świetnie.
 -1
Author: segavu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-30 13:52:05

Bardzo prosty kod do zamiany dwóch zmiennych:

static void Main(string[] args)
{
    Console.WriteLine("Prof.Owais ahmed");
    Console.WriteLine("Swapping two variables");

    Console.WriteLine("Enter your first number ");
    int x = Convert.ToInt32(Console.ReadLine());

    Console.WriteLine("Enter your first number ");
    int y = Convert.ToInt32(Console.ReadLine());

    Console.WriteLine("your vlaue of x is="+x+"\nyour value of y is="+y);

    int z = x;
    x = y;
    y = z;

    Console.WriteLine("after Swapping value of x is="+x+"/nyour value of y is="+y);
    Console.ReadLine();
}
 -3
Author: owais ahmed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-09 08:42:01

Możesz wypróbować następujący kod. Jest o wiele lepszy niż inny kod.

a = a + b;
b = a - b;
a = a - b;
 -3
Author: as7,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-16 03:04:44