Intercambia dos variables sin usar una variable temporal

Me gustaría poder intercambiar dos variables sin el uso de una variable temporal en C #. Se puede hacer esto?

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

En primer lugar, intercambiar sin una variable temporal en un idioma como C # es una muy mala idea .

Pero por el bien de la respuesta, puede usar este código:

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

Sin embargo, pueden ocurrir problemas con el redondeo si los dos números difieren en gran medida. Esto se debe a la naturaleza de los números de coma flotante.

Si desea ocultar la variable temporal, puede usar un método de utilidad:

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

La forma correcta de intercambiar dos variables es:

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

En otras palabras, use una variable temporal.

Ahí tienes. No hay trucos ingeniosos, ni mantenedores de tu código que te maldigan en las próximas décadas, no hay entradas en The Daily WTF , y no pasas demasiado tiempo tratando de descubrir por qué lo necesitas en una sola operación, ya que, en el nivel más bajo, incluso el La función de lenguaje más complicada es una serie de operaciones simples.

Solo un muy simple, legible, fácil de entender, t = a; a = b; b = t; t = a; a = b; b = t; solución.

En mi opinión, los desarrolladores que intentan usar trucos para, por ejemplo, “intercambiar variables sin usar una temperatura” o “el dispositivo de Duff” solo intentan mostrar lo inteligentes que son (y fallar miserablemente).

Los comparo con aquellos que leen libros importantes solo con el propósito de parecer más interesantes en las fiestas (en lugar de expandir tus horizontes).

Las soluciones en las que agrega y resta, o las basadas en XOR, son menos legibles y probablemente más lentas que una simple solución de “variable temporal” (operaciones aritméticas / booleanas en lugar de simples movimientos a nivel de conjunto).

Haga usted mismo, y otros, un servicio escribiendo un código legible de buena calidad.

Esa es mi diatriba. Gracias por su atención 🙂

Por otro lado, soy muy consciente de que esto no responde a tu pregunta específica (y me disculparé por ello), pero hay muchos precedentes en SO en los que las personas han preguntado cómo hacer algo y la respuesta correcta es “No lo hagas”. hazlo”.

Sí, usa este código:

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

El problema es más difícil para valores arbitrarios. 🙂

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

Funciona para todos los tipos, incluidas cadenas y flotadores.

C # 7 introdujo tuplas que permiten intercambiar dos variables sin una temporal:

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

Esto asigna b a a y a a b .

BenAlabaster mostró una forma práctica de hacer un cambio de variable, pero la cláusula try-catch no es necesaria. Este código es suficiente.

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

El uso es el mismo que se muestra:

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

También puedes usar un método de extensión:

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

Úselo así:

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

Ambas formas usa una variable temporal en el método, pero no necesita la variable temporal donde realiza el intercambio.

Un intercambio XOR binario con un ejemplo detallado:

Tabla de verdad de XOR :

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

Entrada:

 a = 4; b = 6; 

Paso 1 : a = a ^ b

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

Paso 2 : b = a ^ b

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

Paso 3 : a = a ^ b

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

Salida:

 a = 6; b = 4; 

No en C #. En el código nativo, es posible que pueda utilizar el truco de intercambio triple-XOR, pero no en un lenguaje de tipo seguro de alto nivel. (De todos modos, he oído que el truco de XOR termina siendo más lento que usar una variable temporal en muchas architectures de CPU comunes.)

Solo debes usar una variable temporal. No hay razón por la que no puedas usar uno; no es que haya un suministro limitado.

Por el bien de los futuros alumnos y la humanidad, presento esta corrección a la respuesta seleccionada actualmente.

Si desea evitar el uso de variables temp, solo hay dos opciones sensatas que tienen en cuenta el primer rendimiento y la legibilidad.

  • Use una variable temp en un método genérico Swap . (Mejor rendimiento absoluto, junto a la variable de temperatura en línea)
  • Use Interlocked.Exchange . (5.9 veces más lento en mi máquina, pero esta es su única opción si varios hilos intercambiaran estas variables simultáneamente).

Cosas que nunca debes hacer:

  • Nunca use la aritmética de coma flotante. (errores lentos, de redondeo y desbordamiento, difíciles de entender)
  • Nunca use aritmética no primitiva. (lento, errores de desbordamiento, difíciles de entender) El Decimal no es una primitiva de CPU y resulta en mucho más código de lo que cree.
  • Nunca use el período aritmético. O bit hacks. (lento, difícil de entender) Ese es el trabajo del comstackdor. Se puede optimizar para muchas plataformas diferentes.

Porque a todos les gustan los números duros, aquí hay un progtwig que compara sus opciones. Ejecútelo en modo de lanzamiento desde fuera de Visual Studio para que Swap esté en línea. Resultados en mi máquina (Windows 7 64-bit i5-3470):

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

Código:

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

Puedes hacerlo en 3 líneas usando matemáticas básicas; en mi ejemplo, utilicé la multiplicación, pero la adición simple también funcionaría.

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

Editar: Como se señaló en los comentarios, esto no funcionaría si y = 0, ya que generaría un error de división por cero que no había considerado. Entonces, la solución +/- presentada alternativamente sería la mejor manera de hacerlo.


Para que mi código sea inmediatamente comprensible, es más probable que haga algo como esto. [Siempre piensa en el pobre tipo que va a tener que mantener tu código]:

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

Y luego puedes hacerlo en una línea de código:

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

Hecho como la cena … ahora puedes pasar cualquier tipo de objeto y cambiarlos …

Si puede cambiar de usar decimal a double , puede usar la clase Interlocked . Presumiblemente, esta será una buena forma de intercambiar variables en cuanto al rendimiento. También es un poco más legible que XOR.

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

Msdn: Interlocked.Exchange Method (Double, Double)

Para completar, aquí está el intercambio XOR binario:

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

Esto funciona para todos los objetos / referencias atómicas, ya que trata directamente con los bytes, pero puede requerir un contexto inseguro para trabajar con decimales o, si se siente realmente retorcido, con punteros. Y puede ser más lento que una variable de temperatura en algunas circunstancias también.

¡Ten cuidado con tu entorno!

Por ejemplo, esto no parece funcionar en ECMAscript

 y ^= x ^= y ^= x; 

Pero esto no

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

Mi consejo? Asume lo menos posible.

Con C # 7, puede usar la deconstrucción de tuplas para lograr el intercambio deseado en una línea, y está claro lo que está sucediendo.

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

En C # 7:

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

La manera simple de intercambiar 2 números en una sola línea:

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

por ejemplo: a = 1, b = 2

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

Paso 2: a = 3-1

=> a = 2 yb = 1


Manera eficiente es usar:

C Progtwigción: (x ^= y), (y ^= x), (x ^= y);

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

Python: x, y = y, x

Nota: La mayoría de los errores comunes que cometen las personas: // Intercambian utilizando XOR bit a bit (Solución incorrecta en C / C ++)

 x ^= y ^= x ^= y; 

Fuente: GeeksforGeek

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

1.

Para los tipos binarios puedes usar este truco funky:

 a %= b %= a %= b; 

Siempre y cuando a y b no sean exactamente la misma variable (por ejemplo, alias para la misma memoria) funciona.

Espero que esto ayude…

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

Si quieres intercambiar 2 variables de cadena:

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

Un método auxiliar en consecuencia:

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

El uso sería entonces:

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

Aquí otro enfoque en una línea:

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

podemos hacer eso haciendo un simple truco

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

Aquí hay algunos procesos diferentes para intercambiar dos variables

 //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 tuplas

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

Esto funciona genial

Código muy simple para intercambiar dos variables:

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

Puedes probar el siguiente código. Es mucho mejor que el otro código.

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