Domanda

Mi piacerebbe essere in grado di scambiare due variabili senza l'uso di una variabile temporanea in C #. Questo 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
È stato utile?

Soluzione

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

Ma per motivi di risposta, puoi usare questo codice:

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

Possono tuttavia verificarsi problemi con l'arrotondamento se i due numeri differiscono ampiamente. Ciò è dovuto alla natura dei numeri in virgola mobile.

Se vuoi nascondere la variabile temporanea, puoi usare un metodo di utilità:

public static class Foo {

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

Altri suggerimenti

Il modo giusto di scambiare due variabili è:

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

In altre parole, usa una variabile temporanea.

Eccolo qui. Nessun trucco intelligente, nessun manutentore del codice che ti maledice per i decenni a venire, nessuna voce su The Daily WTF , e nessuna spesa troppo tempo cercando di capire perché ne avevi bisogno in una sola operazione poiché, al livello più basso, anche la funzione linguistica più complicata è una serie di semplici operazioni.

Solo una soluzione molto semplice, leggibile, facile da capire, t = a; a = b; b = t;.

A mio avviso, gli sviluppatori che cercano di usare i trucchi per, ad esempio, " scambiano variabili senza usare un temp " oppure " dispositivo di Duff " stanno solo cercando di mostrare quanto siano intelligenti (e fallendo miseramente).

Li ho paragonati a coloro che leggono libri di alto livello esclusivamente allo scopo di sembrare più interessanti alle feste (invece di espandere i tuoi orizzonti).

Le soluzioni in cui aggiungi e sottrai, o quelle basate su XOR, sono meno leggibili e molto probabilmente più lente di una semplice " temp variabile " soluzione (operazioni aritmetiche / booleane invece di semplici movimenti a livello di assieme).

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

Questo è il mio rant. Grazie per l'ascolto :-)

A parte questo, sono abbastanza consapevole che questo non risponde alla tua domanda specifica (e mi scuserò per quello) ma ci sono molti precedenti su SO in cui le persone hanno chiesto come fare qualcosa e la risposta corretta è < !> quot; Non farlo " ;.

Sì, utilizzare questo codice:

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

Il problema è più difficile per valori arbitrari. : -)

C # 7 introdotto tuple che consente di scambiare due variabili senza una temporanea:

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

Questo assegna b a a e <=> a <=>.

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

Funziona con tutti i tipi inclusi stringhe e float.

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

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

L'uso è lo stesso di quello mostrato:

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

Puoi anche usare un metodo di estensione:

static class SwapExtension
{
    public static T Swap<T>(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);

In entrambi i modi viene utilizzata una variabile temporanea nel metodo, ma non è necessaria la variabile temporanea in cui si esegue lo scambio.

Uno scambio XOR binario con un esempio dettagliato:

Tabella di verità XOR :

a b a^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

Output:

a = 6;
b = 4;

Non in C #. Nel codice nativo potresti essere in grado di utilizzare il trucco di scambio triple-XOR, ma non in un linguaggio sicuro di alto livello. (Comunque, ho sentito che il trucco XOR finisce per essere più lento rispetto all'uso di una variabile temporanea in molte architetture CPU comuni.)

Dovresti semplicemente usare una variabile temporanea. Non c'è motivo per cui non puoi usarne uno; non è che ci sia un'offerta limitata.

Per il bene dei futuri discenti e dell'umanità, sottopongo questa correzione alla risposta attualmente selezionata.

Se vuoi evitare di usare le variabili temporanee, ci sono solo due opzioni sensibili che prendono in considerazione la prima prestazione e poi la leggibilità.

  • Utilizza una variabile temp in un metodo Swap generico. (Migliore prestazione assoluta, accanto alla variabile temp inline)
  • Utilizza Interlocked.Exchange . (5.9 volte più lento sulla mia macchina, ma questa è l'unica opzione se più thread scambieranno queste variabili contemporaneamente.)

Cose che non dovresti mai fare:

  • Non usare mai l'aritmetica in virgola mobile. (errori lenti, di arrotondamento e di overflow, difficili da capire)
  • Non usare mai l'aritmetica non primitiva. (errori di overflow lento, difficile da capire) Decimal non è una primitiva della CPU e risultati in molto più codice di quanto pensiate.
  • Non usare mai periodi aritmetici. O bit hack. (lento, difficile da capire) Questo è il lavoro del compilatore. Può ottimizzare per molte piattaforme diverse.

Poiché tutti amano i numeri difficili, ecco un programma che confronta le tue opzioni. Eseguilo in modalità di rilascio dall'esterno di Visual Studio in modo che <=> sia inline. 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<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();
    }
}

lt &; Deprecated gt &;

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

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 in quanto genererebbe un errore di divisione per zero che non avevo considerato. Quindi la soluzione +/- presentata in alternativa sarebbe il modo migliore per procedere.

lt &; / Deprecated gt &;


Per rendere immediatamente comprensibile il mio codice, avrei maggiori probabilità di fare qualcosa del genere. [Pensa sempre al povero ragazzo che dovrà mantenere il tuo codice]:

static bool Swap<T>(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<float>(ref startAngle, ref stopAngle);

Oppure ...

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

Fatto come la cena ... ora puoi passare qualsiasi tipo di oggetto e cambiarlo ...

Se puoi passare dall'uso decimal a double puoi usare la classe Interlocked. Presumibilmente questo sarà un buon modo per scambiare le variabili in termini di prestazioni. Anche leggermente più leggibile di XOR.

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

Msdn: Interlocked.Exchange Method (Double, Double)

In C # 7:

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

Per completezza, ecco lo scambio XOR binario:

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

Funziona con tutti gli oggetti / riferimenti atomici, poiché si occupa direttamente dei byte, ma potrebbe richiedere un contesto non sicuro per lavorare su decimali o, se ti senti davvero contorto, puntatori. E in alcune circostanze potrebbe essere più lento di una variabile temp.

Attenzione al tuo ambiente!

Ad esempio, questo non & # 8217; t sembra funzionare in ECMAscript

y ^= x ^= y ^= x;

Ma questo

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

Il mio consiglio? Assumi il meno possibile.

Con C # 7, puoi usare 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);

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

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

es: a = 1, b = 2

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

Passaggio 2: a = 3-1

&

= gt; a = 2 eb = 1


Il modo più efficiente è usare:

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

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

Python: x, y = y, x

Nota: l'errore più comune che le persone commettono: // Scambia usando XOR bit a bit (soluzione errata in C / C ++)

x ^= y ^= x ^= y; 

Fonte: GeeksforGeek

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

# 1614;

Per i tipi binari puoi usare questo trucco funky:

a %= b %= a %= b;

Fintanto che aeb non sono esattamente la stessa variabile (ad 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);
    }
}

possiamo farlo facendo un semplice trucco

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

Con le tuple

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

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

Ecco un processo diverso per scambiare due variabili

//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);
var a = 15;
var b = -214;
a = b | !(b = a);

Funziona benissimo.

Codice molto semplice per scambiare 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 più migliore dell'altro codice.

a = a + b;
b = a - b;
a = a - b;
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top