Troque duas variables ​​sem usar uma variável temporária

Eu gostaria de ser capaz de trocar duas variables ​​sem o uso de uma variável temporária em c #. Isso pode ser feito?

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

    Primeiro de tudo, trocar sem uma variável temporária em uma linguagem como C # é uma idéia muito ruim .

    Mas, por uma questão de resposta, você pode usar este código:

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

    No entanto, os problemas podem ocorrer com o arredondamento se os dois números diferirem amplamente. Isso se deve à natureza dos números de ponto flutuante.

    Se você quiser ocultar a variável temporária, use um método de utilitário:

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

    O caminho certo para trocar duas variables ​​é:

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

    Em outras palavras, use uma variável temporária.

    Lá você tem isso. Nenhum truque inteligente, nenhum mantenedor do seu código amaldiçoando você por décadas, nenhuma input para o The Daily WTF , e sem gastar muito tempo tentando descobrir por que você precisava disso em uma operação de qualquer maneira, já que, no nível mais baixo, até mesmo o característica de linguagem mais complicada é uma série de operações simples.

    Apenas um muito simples, legível, fácil de entender, t = a; a = b; b = t; t = a; a = b; b = t; solução.

    Na minha opinião, os desenvolvedores que tentam usar truques para, por exemplo, “trocar variables ​​sem usar uma temperatura” ou “dispositivo de Duff”, estão apenas tentando mostrar o quão inteligentes eles são (e falhar miseravelmente).

    Eu os comparo àqueles que lêem livros sofisticados apenas com o propósito de parecerem mais interessantes em festas (em vez de expandir seus horizontes).

    As soluções em que você adiciona e subtrai, ou as baseadas em XOR, são menos legíveis e provavelmente mais lentas que uma solução simples de “variável temporária” (operações aritméticas / booleanas em vez de simples em um nível de assembly).

    Faça você mesmo, e outros, um serviço escrevendo um código legível de boa qualidade.

    Esse é meu discurso Obrigado por ouvir 🙂

    Como um aparte, estou ciente de que isso não responde à sua pergunta específica (e peço desculpas por isso), mas há muitos precedentes sobre o SO, onde as pessoas perguntaram como fazer algo ea resposta correta é: “Não faça isso”. faça”.

    Sim, use este código:

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

    O problema é mais difícil para valores arbitrários. 🙂

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

    Funciona para todos os tipos, incluindo strings e floats.

    C # 7 introduziu tuplas que permitem trocar duas variables ​​sem uma temporária:

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

    Isso atribui b a e a a b .

    BenAlabaster mostrou uma maneira prática de fazer um switch variável, mas a cláusula try-catch não é necessária. Este código é suficiente.

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

    O uso é o mesmo que ele mostrou:

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

    Você também pode usar um método de extensão:

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

    Use assim:

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

    Ambas as formas usam uma variável temporária no método, mas você não precisa da variável temporária onde faz a troca.

    Uma troca binária XOR com um exemplo detalhado:

    Tabela de verdade XOR :

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

    Entrada:

     a = 4; b = 6; 

    Etapa 1 : a = a ^ b

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

    Etapa 2 : b = a ^ b

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

    Etapa 3 : a = a ^ b

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

    Saída:

     a = 6; b = 4; 

    Não em c #. No código nativo, você pode usar o truque de troca triple-XOR, mas não em uma linguagem de alto nível para segurança de tipos. (De qualquer forma, ouvi dizer que o truque XOR acaba sendo mais lento do que usar uma variável temporária em muitas arquiteturas de CPU comuns.)

    Você deve apenas usar uma variável temporária. Não há motivo para você não poder usar um; Não é como se houvesse um suprimento limitado.

    Para o bem dos futuros alunos e da humanidade, eu envio esta correção para a resposta atualmente selecionada.

    Se você quiser evitar o uso de variables ​​temporárias, existem apenas duas opções sensatas que levam em consideração o primeiro desempenho e, em seguida, a legibilidade.

    • Use uma variável temporária em um método de Swap genérico. (Melhor desempenho absoluto, próximo à variável temp inline)
    • Use Interlocked.Exchange . (5,9 vezes mais lento na minha máquina, mas esta é sua única opção se vários segmentos estiverem trocando essas variables ​​simultaneamente).

    Coisas que você nunca deve fazer:

    • Nunca use aritmética de ponto flutuante. (lento, arredondamento e excesso de erros, difícil de entender)
    • Nunca use aritmética não-primitiva. (lento, erros de estouro, difícil de entender) Decimal não é uma CPU primitiva e resulta em muito mais código do que você imagina.
    • Nunca use período aritmético. Ou bit hacks. (lento, difícil de entender) Esse é o trabalho do compilador. Pode otimizar para muitas plataformas diferentes.

    Porque todo mundo ama números difíceis, aqui está um programa que compara suas opções. Execute-o no modo de liberação de fora do Visual Studio para que o Swap esteja embutido. Resultados na minha 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(); } } 

    Você pode fazer isso em 3 linhas usando matemática básica – no meu exemplo eu usei multiplicação, mas a simples adição também funcionaria.

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

    Edit: Como observado nos comentários, isso não funcionaria se y = 0, pois geraria uma divisão por erro zero que eu não tinha considerado. Assim, a solução +/- apresentada alternativamente seria o melhor caminho a percorrer.


    Para manter meu código imediatamente compreensível, seria mais provável que fizesse algo assim. [Sempre pense no cara pobre que terá que manter seu código]:

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

    E então você pode fazer isso em uma linha de código:

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

    Ou…

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

    Feito como o jantar … agora você pode passar em qualquer tipo de object e trocá-los …

    Se você pode mudar de usar decimal para double você pode usar a class Interlocked . Presumivelmente, esta será uma boa maneira de trocar as variables ​​de desempenho. Também é ligeiramente mais legível que o XOR.

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

    Msdn: Interlocked.Exchange Method (Double, Double)

    Para completar, aqui está a troca binária do XOR:

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

    Isso funciona para todos os objects / referências atômicas, pois lida diretamente com os bytes, mas pode exigir um contexto inseguro para trabalhar em decimais ou, se você estiver se sentindo realmente distorcido, em pointers. E pode ser mais lento que uma variável temporária em algumas circunstâncias também.

    Cuidado com o seu ambiente!

    Por exemplo, isso não parece funcionar no ECMAscript

     y ^= x ^= y ^= x; 

    Mas isso faz

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

    Meu conselho? Assuma o mínimo possível.

    Com o C # 7, você pode usar a desconstrução de tupla para obter o swap desejado em uma linha, e está claro o que está acontecendo.

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

    Em C # 7:

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

    A maneira simples de trocar 2 números em apenas uma linha:

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

    por exemplo: a = 1, b = 2

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

    Etapa 2: a = 3-1

    => a = 2 eb = 1


    Maneira eficiente é usar:

    Programação C: (x ^= y), (y ^= x), (x ^= y);

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

    Python: x, y = y, x

    Nota: O erro mais comum que as pessoas cometem: // Troca usando XOR bitwise (Solução Errada em C / C ++)

     x ^= y ^= x ^= y; 

    Fonte: GeeksforGeek

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

    َ

    Para tipos binários você pode usar este truque funky:

     a %= b %= a %= b; 

    Contanto que a e b não sejam exatamente a mesma variável (por exemplo, aliases para a mesma memory), ela funciona.

    Espero que isso possa ajudar …

     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 você quiser trocar duas variables ​​de string:

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

    Um método auxiliar de acordo:

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

    O uso seria então:

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

    Aqui outra abordagem em uma linha:

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

    nós podemos fazer isso fazendo um truque simples

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

    Aqui está algum processo diferente para trocar duas 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); 

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

    Isso funciona muito bem.

    Código muito simples para trocar duas 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(); } 

    Você pode tentar o seguinte código. É muito mais melhor que o outro código.

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