it-swarm.com.de

Tauschen Sie zwei Variablen aus, ohne eine temporäre Variable zu verwenden

Ich möchte zwei Variablen austauschen können, ohne eine temporäre Variable in C # zu verwenden. Kann das gemacht werden?

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

// Swap each:
//   startAngle becomes: 355.87
//   stopAngle becomes: 159.9
57
Sreedhar

Der Austausch ohne temporäre Variable in einer Sprache als C # ist zunächst eine sehr schlechte Idee.

Aber zur Antwort können Sie diesen Code verwenden:

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

Bei der Abrundung können jedoch Probleme auftreten, wenn die beiden Zahlen stark voneinander abweichen. Dies liegt an der Art der Fließkommazahlen.

Wenn Sie die temporäre Variable ausblenden möchten, können Sie eine Utility-Methode verwenden:

public static class Foo {

    public static void Swap<T> (ref T lhs, ref T rhs) {
        T temp = lhs;
        lhs = rhs;
        rhs = temp;
    }
}
98

Die Möglichkeit, zwei Variablen auszutauschen, lautet right:

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

Mit anderen Worten, verwenden Sie eine temporäre Variable.

Hier hast du es. Keine cleveren Tricks, keine Betreuer Ihres Codes, die Sie jahrzehntelang verfluchen, keine Einträge in The Daily WTF und keine Zeit zu viel, um herauszufinden, warum Sie ihn sowieso in einer Operation gebraucht haben, zumindest Selbst die komplizierteste Sprachfunktion besteht aus einer Reihe einfacher Operationen.

Nur eine sehr einfache, lesbare, leicht verständliche t = a; a = b; b = t;-Lösung.

Meiner Meinung nach versuchen Entwickler, die versuchen, mithilfe von Tricks beispielsweise "Variablen ohne Temp. Austauschen" oder "Duffs Gerät" auszutauschen, einfach nur, um zu zeigen, wie klug sie sind (und scheitern).

Ich vergleiche sie mit denen, die hochkarätige Bücher lesen, nur um auf Partys interessanter zu wirken (anstatt Ihren Horizont zu erweitern).

Lösungen, bei denen Sie hinzufügen oder subtrahieren, oder die auf XOR basierenden, sind weniger lesbar und höchstwahrscheinlich langsamer als eine einfache "temporäre Variable" -Lösung (arithmetische/boolesche Operationen anstelle von einfachen Bewegungen auf Assembly-Ebene).

Machen Sie sich und anderen einen Dienst, indem Sie lesbaren Code in guter Qualität schreiben.

Das ist mein Schrei. Danke fürs Zuhören :-)

Abgesehen davon bin ich mir durchaus bewusst, dass dies Ihre spezifische Frage nicht beantwortet (und ich werde mich dafür entschuldigen), aber es gibt viele Präzedenzfälle in SO, wo die Leute gefragt haben, wie sie etwas tun sollen, und die richtige Antwort ist "Tu es nicht".

209
paxdiablo

Ja, benutze diesen Code:

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

Das Problem ist schwieriger für beliebige Werte. :-)

71
Paul Sonier

In C # 7 wurden tuples eingeführt, mit denen zwei Variablen ohne temporäre Variablen ausgetauscht werden können:

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

Dadurch werden b der a und a der b zugewiesen.

54
TimothyP
int a = 4, b = 6;
a ^= b ^= a ^= b;

Funktioniert für alle Arten, einschließlich Strings und Floats. 

42

BenAlabaster zeigte eine praktische Möglichkeit, eine variable Änderung vorzunehmen, aber die try-catch-Klausel ist nicht erforderlich. Dieser Code reicht aus.

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

Die Verwendung ist dieselbe wie er gezeigt hat:

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

Sie können auch eine Erweiterungsmethode verwenden:

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

Verwenden Sie es so:

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

Beide Methoden verwenden eine temporäre Variable in der Methode. Sie benötigen jedoch nicht die temporäre Variable, an der Sie den Swap durchführen.

19
Marcus

Ein binärer XOR Swap mit einem detaillierten Beispiel:

XOR Wahrheitstabelle:

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

Eingabe:

a = 4;
b = 6;

Schritt 1 : a = a ^ b

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

Schritt 2 : b = a ^ b

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

Schritt 3 : a = a ^ b

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

Ausgabe:

a = 6;
b = 4;
15
Steven Muhr

Im Interesse der zukünftigen Lernenden und der Menschheit übermittle ich diese Korrektur an die aktuell ausgewählte Antwort.

Wenn Sie die Verwendung von temporären Variablen vermeiden möchten, gibt es nur zwei sinnvolle Optionen , die zuerst die Leistung und dann die Lesbarkeit berücksichtigen.

  • Verwenden Sie eine temporäre Variable in einer generischen Swap -Methode. (Absolut beste Leistung, neben Inline-Temp-Variable)
  • Verwenden Interlocked.Exchange . (5,9-mal langsamer auf meinem Computer, aber dies ist Ihre einzige Option, wenn mehrere Threads diese Variablen gleichzeitig austauschen.)

Dinge, die Sie niemals tun sollten:

  • Verwenden Sie niemals Gleitkomma-Arithmetik. (langsame Rundungs- und Überlauffehler, schwer zu verstehen)
  • Verwenden Sie niemals nicht-primitive Arithmetik. (Langsam, Überlauffehler, schwer zu verstehen) Decimal ist kein CPU-Grundelement und führt zu weitaus mehr Code als Sie denken.
  • Verwenden Sie niemals die arithmetische Periode. Oder ein bisschen hackt. (langsam, schwer zu verstehen) Das ist die Aufgabe des Compilers. Es kann für viele verschiedene Plattformen optimiert werden.

Da jeder harte Zahlen liebt, ist hier ein Programm, das Ihre Optionen vergleicht. Führen Sie es im Release-Modus außerhalb von Visual Studio aus, sodass Swap eingebettet ist. Ergebnisse auf meinem Computer (Windows 7 64-Bit i5-3470):

Inline:      00:00:00.7351931
Call:        00:00:00.7483503
Interlocked: 00:00:04.4076651

Code:

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();
    }
}
12
jnm2

Nicht in C #. In nativem Code können Sie möglicherweise den Triple-XOR-Swap-Trick verwenden, jedoch nicht in einer typensicheren Hochsprache. (Wie auch immer, ich habe gehört, dass der Trick XOR tatsächlich langsamer ist als die Verwendung einer temporären Variablen in vielen gängigen CPU-Architekturen.)

Sie sollten nur eine temporäre Variable verwenden. Es gibt keinen Grund, warum Sie keinen verwenden können. Es ist nicht so, als gäbe es einen begrenzten Vorrat.

11
Jens Alfke

<veraltet>

Sie können dies in drei Zeilen mit grundlegender Mathematik tun - in meinem Beispiel habe ich die Multiplikation verwendet, aber eine einfache Addition würde auch funktionieren.

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

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

Bearbeiten: Wie in den Kommentaren erwähnt, würde dies nicht funktionieren, wenn y = 0 wäre, da dies eine Division durch Nullfehler erzeugen würde, die ich nicht berücksichtigt hatte. Die alternativ vorgestellte +/- Lösung wäre also der beste Weg.

</ veraltet>


Um meinen Code sofort verständlich zu halten, würde ich eher so etwas tun. [Denken Sie immer an den armen Kerl, der Ihren Code beibehalten muss]:

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

Und dann can tun Sie es in einer Codezeile:

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

Oder...

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

Fertig wie das Abendessen ... Sie können jetzt jede Art von Objekt passieren und umschalten ...

7
BenAlabaster

Wenn Sie von decimal zu double wechseln können, können Sie die Interlocked-Klasse verwenden. Auch etwas lesbarer als XOR.

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

Msdn: Interlocked.Exchange-Methode (Double, Double)

6
Robert Fricke

Der Vollständigkeit halber sei hier der binäre XOR Swap angegeben:

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

Dies funktioniert für alle atomaren Objekte/Referenzen, da es sich direkt auf die Bytes bezieht, es kann jedoch ein unsicherer Kontext erforderlich sein, um mit Dezimalzahlen oder, wenn Sie sich wirklich verdreht fühlen, Zeigern zu arbeiten. Unter Umständen ist sie auch langsamer als eine temporäre Variable.

5
thecoop

Hüte dich vor deiner Umgebung!

Zum Beispiel scheint dies in ECMAscript nicht zu funktionieren

y ^= x ^= y ^= x;

Aber das tut

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

Mein rat Nehmen Sie so wenig wie möglich an.

5
Codzart

Mit C # 7 können Sie die Tuple-Dekonstruktion verwenden, um den gewünschten Swap in einer Zeile zu erreichen, und es ist klar, was los ist. 

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

(startAngle, stopAngle) = (stopAngle, startAngle);
5
jdphenix

In C # 7:

(startAngle, stopAngle) = (stopAngle, startAngle);

Die einfache Möglichkeit, 2 Zahlen in nur einer Zeile auszutauschen:

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

zB: a = 1, b = 2

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

Schritt 2: a = 3-1 

=> a = 2 und b = 1


Effizienter Weg ist zu verwenden:

C-Programmierung: (x ^= y), (y ^= x), (x ^= y);

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

Python: x, y = y, x 

Hinweis: Die häufigsten Fehler, die von Benutzern gemacht werden: // Mit bitweisem XOR austauschen (falsche Lösung in C/C++)

x ^= y ^= x ^= y; 

Quelle: GeeksforGeek

3
Utsav Dusad
a = a + b
b = a - b
a = a - b

َ

3
srinivasan

Ich hoffe das könnte helfen ...

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
PalakM

Für binäre Typen können Sie diesen funky Trick verwenden:

a %= b %= a %= b;

Solange a und b nicht exakt dieselbe Variable sind (z. B. Aliasnamen für denselben Speicher), funktioniert es.

2
BCS

Mit Tupeln

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

(startAngle, stopAngle) = (stopAngle, startAngle);
1
Zu1779

wir können das durch einen einfachen Trick tun

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);
1
cammando

Wenn Sie 2 String-Variablen austauschen möchten:

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

Eine Hilfsmethode entsprechend:

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

Die Nutzung wäre dann:

string a="Test 1";
string b="Test 2";
Foo.SwapString(a, b);
1
HGMamaci
startAngle = (startAngle + stopAngle) - (stopAngle = startAngle);
1
kokabi

Hier ein weiterer Ansatz in einer Zeile:

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

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

Hier ist ein anderer Prozess, um zwei Variablen auszutauschen 

//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
A.A Noman