Scambia due variabili senza utilizzare una variabile temporanea

Mi piacerebbe essere in grado di scambiare due variabili senza l’uso di una variabile temporanea in C #. Può essere fatto?

decimal startAngle = Convert.ToDecimal(159.9); decimal stopAngle = Convert.ToDecimal(355.87); // Swap each: // startAngle becomes: 355.87 // stopAngle becomes: 159.9 

Prima di tutto, scambiare senza una variabile temporanea in una lingua come C # è una pessima idea .

Ma per ragioni di risposta, puoi usare questo codice:

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

Tuttavia, i problemi possono verificarsi con arrotondamento se i due numeri differiscono ampiamente. Ciò è dovuto alla natura dei numeri in virgola mobile.

Se si desidera hide la variabile temporanea, è ansible utilizzare un metodo di utilità:

 public static class Foo { public static void Swap (ref T lhs, ref T rhs) { T temp = lhs; lhs = rhs; rhs = temp; } } 

Il modo giusto per scambiare due variabili è:

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

In altre parole, usa una variabile temporanea.

Ecco qua. Nessun trucco intelligente, nessun manutentore del tuo codice ti maledice per decenni a venire, nessuna voce al Daily WTF , e non spendere troppo tempo a cercare di capire perché ne hai avuto bisogno in una sola operazione poiché, al livello più basso, anche il la caratteristica linguistica più complicata è una serie di semplici operazioni.

Semplicemente semplice, leggibile, facile da capire, t = a; a = b; b = t; t = a; a = b; b = t; soluzione.

A mio parere, gli sviluppatori che cercano di usare trucchi per, ad esempio, “scambiare le variabili senza usare un temp” o “dispositivo Duff” stanno solo cercando di mostrare quanto siano intelligenti (e fallendo miseramente).

Li paragono a quelli che leggono libri intellettuali unicamente allo scopo di sembrare più interessanti alle feste (al contrario di espandere i propri orizzonti).

Le soluzioni in cui si aggiungono e si sottraggono, o quelle basate su XOR, sono meno leggibili e molto probabilmente più lente di una semplice soluzione “temp variable” (operazioni aritmetiche / booleane anziché semplici mosse a livello di assieme).

Fai da te e da altri un servizio scrivendo un codice leggibile di buona qualità.

Questo è il mio rant. Grazie per aver ascoltato 🙂

Per inciso, sono abbastanza consapevole che questo non risponde alla tua domanda specifica (e mi scuserò per questo) ma ci sono molti precedenti su SO in cui le persone hanno chiesto come fare qualcosa e la risposta corretta è “Non farlo fallo”.

Sì, usa questo codice:

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

Il problema è più difficile per i valori arbitrari. 🙂

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

Funziona per tutti i tipi, inclusi archi e galleggianti.

Il C # 7 ha introdotto tuple che consentono di scambiare due variabili senza una temporanea:

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

Questo assegna b a ae a b .

BenAlabaster ha mostrato un modo pratico di fare un interruttore variabile, ma la clausola try-catch non è necessaria. Questo codice è sufficiente.

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

L’uso è lo stesso che ha mostrato:

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

Puoi anche utilizzare un metodo di estensione:

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

Usalo in questo modo:

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

Entrambi i modi utilizza una variabile temporanea nel metodo, ma non è necessaria la variabile temporanea in cui si effettua lo scambio.

Uno scambio XOR binario con un esempio dettagliato:

Tabella della verità XOR :

 aba^b 0 0 0 0 1 1 1 0 1 1 1 0 

Ingresso:

 a = 4; b = 6; 

Passaggio 1 : a = a ^ b

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

Passaggio 2 : b = a ^ b

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

Passaggio 3 : a = a ^ b

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

Produzione:

 a = 6; b = 4; 

Non in C #. Nel codice nativo potresti essere in grado di utilizzare il trucco swap triple-XOR, ma non in un linguaggio sicuro di alto livello. (In ogni caso, ho sentito che il trucco XOR in realtà finisce per essere più lento rispetto all’utilizzo di una variabile temporanea in molte architetture di CPU comuni.)

Dovresti semplicemente usare una variabile temporanea. Non c’è motivo per cui non puoi usarne uno; non è come se ci fosse un’offerta limitata.

Per il bene dei futuri studenti e dell’umanità, invio questa correzione alla risposta attualmente selezionata.

Se si desidera evitare l’uso di variabili temporanee, esistono solo due opzioni sensate che prendono in considerazione le prime prestazioni e quindi la leggibilità.

  • Utilizzare una variabile temporanea in un metodo Swap generico. (Miglior rendimento assoluto, accanto alla variabile temporanea incorporata)
  • Usa Interlocked.Exchange . (5 volte più lento sulla mia macchina, ma questa è l’unica opzione se più thread scambieranno queste variabili simultaneamente).

Cose che non dovresti mai fare:

  • Non usare mai aritmetica in virgola mobile. (errori lenti, di arrotondamento e di overflow, difficili da capire)
  • Non usare mai aritmetica non primitiva. (lento, errori di overflow, difficile da capire) Decimal non è una CPU primitiva e produce molto più codice di quello che ti rendi conto.
  • Non usare mai il periodo aritmetico. O bit hack. (lento, difficile da capire) Questo è il lavoro del compilatore. Può ottimizzare per molte piattaforms diverse.

Perché tutti amano i numeri difficili, ecco un programma che mette a confronto le tue opzioni. Eseguirlo in modalità di rilascio dall’esterno di Visual Studio in modo che Swap sia in linea. Risultati sulla mia macchina (Windows 7 64-bit i5-3470):

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

Codice:

 class Program { static void Swap(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(); } } 

Puoi farlo in 3 righe usando la matematica di base – nel mio esempio ho usato la moltiplicazione, ma anche l’aggiunta semplice funzionerebbe.

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

Modifica: Come notato nei commenti, questo non funzionerebbe se y = 0 poiché genererebbe un errore di divisione per zero che non avevo considerato. Quindi la soluzione +/- presentata in alternativa sarebbe il modo migliore per andare.


Per mantenere il mio codice immediatamente comprensibile, sarei più propenso a fare qualcosa del genere. [Pensa sempre al povero ragazzo che dovrà mantenere il tuo codice]:

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

E poi puoi farlo in una riga di codice:

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

O…

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

Fatto come la cena … ora puoi passare qualsiasi tipo di object e cambiarlo …

Se è ansible passare dall’uso del decimal al double è ansible utilizzare la class Interlocked . Presumibilmente questo sarà un buon modo per scambiare le prestazioni delle variabili con saggezza. Inoltre leggermente più leggibile rispetto a XOR.

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

Msdn: Interlocked.Exchange Method (Double, Double)

Per completezza, ecco lo scambio XOR binario:

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

Questo funziona per tutti gli oggetti / riferimenti atomici, poiché tratta direttamente con i byte, ma potrebbe richiedere un contesto non sicuro per lavorare su decimali o, se vi sentite davvero contorti, puntatori. E potrebbe anche essere più lento di una variabile temporanea in alcune circostanze.

Attenzione al tuo ambiente!

Ad esempio, questo non sembra funzionare in ECMAscript

 y ^= x ^= y ^= x; 

Ma questo sì

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

Il mio consiglio? Assumi il meno ansible.

Con C # 7, è ansible utilizzare la decostruzione della tupla per ottenere lo scambio desiderato in una riga, ed è chiaro cosa sta succedendo.

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

In C # 7:

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

Il modo semplice per scambiare 2 numeri in una sola riga:

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

ad es .: a = 1, b = 2

Passaggio 1: a = (1 + 2) – (b = 1)

Passaggio 2: a = 3-1

=> a = 2 eb = 1


Il modo efficiente è usare:

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

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

Python: x, y = y, x

Nota: gli errori più comuni che le persone fanno: // scambiano usando bit XOR bit (soluzione errata in C / C ++)

 x ^= y ^= x ^= y; 

Fonte: GeeksforGeek

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

Per i tipi binari puoi usare questo trucco originale:

 a %= b %= a %= b; 

Finché aeb non sono la stessa identica variabile (es. Alias ​​per la stessa memoria) funziona.

Spero che questo possa aiutare …

 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); } } 
 startAngle = (startAngle + stopAngle) - (stopAngle = startAngle); 

Se si desidera scambiare 2 variabili stringa:

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

Un metodo di supporto di conseguenza:

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

L’utilizzo sarebbe quindi:

 string a="Test 1"; string b="Test 2"; Foo.SwapString(a, b); 

Ecco un altro approccio in una riga:

 decimal a = 159.9m; decimal b = 355.87m; a = b + (b = a) - b; 

possiamo farlo facendo un semplice trucco

 a = 20; b = 30; a = a+b; // add both the number now a has value 50 b = ab; // here we are extracting one number from the sum by sub a = ab; // 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); 

Ecco alcuni processi diversi per scambiare due variabili

 //process one a=b+a; b=ab; a=ab; printf("a= %db= %d",a,b); //process two a=5; b=10; a=a+b-(b=a); printf("\na= %db= %d",a,b); //process three a=5; b=10; a=a^b; b=a^b; a=b^a; printf("\na= %db= %d",a,b); //process four a=5; b=10; a=b-~a-1; b=a+~b+1; a=a+~b+1; printf("\na= %db= %d",a,b); 

Con le tuple

 decimal startAngle = Convert.ToDecimal(159.9); decimal stopAngle = Convert.ToDecimal(355.87); (startAngle, stopAngle) = (stopAngle, startAngle); 
 var a = 15; var b = -214; a = b | !(b = a); 

Funziona alla grande

Codice molto semplice per lo scambio di due variabili:

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

Puoi provare il seguente codice. È molto meglio dell’altro codice.

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