Intercambiar 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
Author: Peter Mortensen, 2009-04-30

28 answers

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

Pero en aras de la respuesta, puedes 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<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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-16 15:52:36

La derecha forma de intercambiar dos variables es:

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

En otras palabras, usa una variable temporal.

Ahí lo tienen. Sin trucos inteligentes, sin mantenedores de tu código maldiciéndote durante las próximas décadas, sin entradas a El Diario WTF, y sin gastar demasiado tiempo tratando de averiguar por qué lo necesitabas en una operación de todos modos ya que, en el nivel más bajo, incluso la función de lenguaje más complicada es una serie de operaciones simples.

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

En mi opinión, los desarrolladores que intentan usar trucos para, por ejemplo, "intercambiar variables sin usar una temperatura" o "dispositivo de Duff" solo están tratando de mostrar lo inteligentes que son (y fallan miserablemente).

Los comparo con aquellos que leen libros intelectuales únicamente con el propósito de parecer más interesantes en las fiestas (en lugar de expandir sus horizontes).

Las soluciones donde se suman y restan, o las basadas en XOR, son menos legible y probablemente más lento que una solución simple de" variable temporal " (aritmética/boolean-ops en lugar de movimientos simples a nivel de ensamblaje).

Hágase usted mismo, y a los demás, un servicio escribiendo código legible de buena calidad.

Esa es mi diatriba. Gracias por escuchar :-)

Como un aparte, soy muy consciente de que esto no responde a tu pregunta específica (y voy a pedir disculpas por eso), pero hay un montón de precedentes en MODO donde las personas se han preguntado cómo hacer algo y la respuesta correcta es "No lo hagas".

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-05 08:32:40

Sí, utilice este código:

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

El problema es más difícil para valores arbitrarios. :-)

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-12 15:29:55

C # 7 introducido tuplas que permite 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.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-25 10:04:54
int a = 4, b = 6;
a ^= b ^= a ^= b;

Funciona para todos los tipos incluyendo cadenas y flotadores.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-12 15:30:08

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

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

El uso es el mismo que él mostró:

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<T>(this T x, ref T y)
    {
        T t = y;
        y = x;
        return t;
    }
}

Úsalo así:

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

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

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-05 19:33:15

Un intercambio XOR binario con un ejemplo detallado:

XOR truth table :

a b a^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;
 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-16 03:01:29

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

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

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-29 23:13:58

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

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

  • Utilice una variable temporal en un método genérico Swap. (El mejor rendimiento absoluto, junto a la variable temporal en línea)
  • Uso Interlocked.Exchange. (5.9 veces más lento en mi máquina, pero este es su único opción si varios hilos intercambiarán estas variables simultáneamente.)

Cosas que debes nunca hacer:

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

Debido a que todo el mundo ama los números duros, aquí hay un programa que compara sus opciones. Ejecútelo en modo release 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<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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-22 14:02:15

Puedes hacerlo en 3 líneas usando matemáticas básicas - en mi ejemplo usé multiplicación, pero la suma 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 indica 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. Por lo tanto, la solución +/- presentada alternativamente sería la mejor manera de hacerlo.


Para mantener mi código inmediatamente comprensible, sería más probable que hiciera algo como este. [Piensa siempre en el pobre tipo que va a tener que mantener tu código]:

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

Y entonces usted puede hacerlo en una línea de código:

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

O...

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

Hecho como la cena...ahora puede pasar cualquier tipo de objeto y cambiarlo...

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-20 13:01:53

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

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

Msdn: Entrelazado.Método de intercambio (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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-30 07:39:45

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 atómicos/referencias, ya que trata directamente con los bytes, pero puede requerir un contexto inseguro para trabajar con decimales o, si te sientes realmente retorcido, punteros. Y también puede ser más lento que una variable temporal en algunas circunstancias.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-05-01 09:42:37

¡Cuidado con tu entorno!

Por ejemplo, esto no parece funcionar en ECMAScript

y ^= x ^= y ^= x;

Pero esto hace

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

Mi consejo? Asume lo menos posible.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-16 03:00:12

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á pasando.

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-22 01:12:41

En 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-29 13:39:25

La forma sencilla de intercambiar 2 números en una sola línea:

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

Eg: a = 1, b=2

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

Paso 2: a = 3-1

=> a=2 y b = 1


La manera eficiente es usar:

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

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

Python: x, y = y, x

Nota: Error más común que la gente comete: / / Swap usando XOR bitwise (Solución incorrecta en C / C++)

x ^= y ^= x ^= y; 

Fuente: 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-16 02:58:21

Para los tipos binarios puedes usar este truco funky:

a %= b %= a %= b;

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

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-16 02:55:35

Espero que esto pueda ayudar...

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-14 09:30:18

Si desea 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);
 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-08 13:48:48

Aquí otro enfoque en una línea:

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-24 12:46:34

Podemos hacer eso haciendo un simple truco

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-30 17:37:17

Aquí hay un proceso diferente para intercambiar dos variables

//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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-11 19:16:40

Con tuplas

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-04 07:22:52
var a = 15;
var b = -214;
a = b | !(b = a);

Esto funciona muy bien.

 -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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-30 13:52:05

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();
}
 -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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-09 08:42:01

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

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-16 03:04:44