Обмен двумя переменными без использования временной переменной

Я хотел бы иметь возможность обменивать две переменные без использования временной переменной в С#. Это можно сделать?

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

// Swap each:
//   startAngle becomes: 355.87
//   stopAngle becomes: 159.9

Ответ 1

Прежде всего, замена без временной переменной на языке С# a очень плохая идея.

Но ради ответа вы можете использовать этот код:

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

Однако проблемы могут возникать при округлении, если два числа сильно отличаются. Это связано с природой чисел с плавающей запятой.

Если вы хотите скрыть временную переменную, вы можете использовать служебный метод:

public static class Foo {

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

Ответ 2

Правильный способ замены двух переменных:

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

Другими словами, используйте временную переменную.

Там у вас есть. Никаких хитроумных трюков, никаких сопровождающих вашего кода, проклинающих вас на долгие десятилетия, никаких записей в The Daily WTF, и не тратить слишком много времени на попытки выясните, почему вы нуждались в ней в одной операции, так как на самом низком уровне даже самая сложная функция языка - это серия простых операций.

Простое, понятное, легко понятное решение t = a; a = b; b = t;.

По-моему, разработчики, которые пытаются использовать трюки, например, "менять переменные без использования временного" или "устройства Duff", просто пытаются показать, насколько они умны (и терпят неудачу).

Я уподобляю их тем, кто читает книги с большими книгами исключительно для того, чтобы казаться более интересными на вечеринках (в отличие от расширения ваших горизонтов).

Решения, в которые вы добавляете и вычитаете, или на основе XOR, менее читабельны и, скорее всего, медленнее, чем простое решение "temp variable" (арифметические/boolean-ops вместо простых перемещений на уровне сборки).

Сделай сам и другие услуги, написав хороший читаемый код.

Это моя напыщенная речь. Спасибо за прослушивание: -)

В стороне, я вполне понимаю, что это не отвечает на ваш конкретный вопрос (и я извинюсь за это), но там много прецедентов на SO, где люди спрашивали, как что-то делать, и правильный ответ - Не делай этого ".

Ответ 3

Да, используйте этот код:

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

Задача сложнее для любых значений.: -)

Ответ 4

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

Работает для всех типов, включая строки и поплавки.

Ответ 5

BenAlabaster показал практический способ выполнения переключателя переменной, но предложение try-catch не требуется. Этот код достаточно.

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

Использование такое же, как показано на рисунке:

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

Вы также можете использовать метод расширения:

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

Используйте его следующим образом:

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

Оба способа используют временную переменную в методе, но вам не нужна временная переменная, в которой вы выполняете обмен.

Ответ 6

Двоичный обмен XOR с подробным примером:

Таблица истинности XOR:

a b a^b
0 0  0
0 1  1
1 0  1
1 1  0

Ввод:

a = 4;
b = 6;

Шаг 1: a = a ^ b

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

Шаг 2: b = a ^ b

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

Шаг 3: a = a ^ b

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

Вывод:

a = 6;
b = 4;

Ответ 7

С# 7 вводит правильный Tuples, который позволяет обменивать две переменные без временной:

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

Это присваивает b до a и a до b.

Ответ 8

Не в С#. В собственном коде вы можете использовать трюк triple-XOR, но не на языке высокого уровня. (В любом случае, я слышал, что трюк XOR на самом деле заканчивается медленнее, чем использование временной переменной во многих обычных архитектурах процессора.)

Вы должны просто использовать временную переменную. Нет причин, по которым вы не можете их использовать; это не похоже на ограниченное предложение.

Ответ 9

Для будущих учеников и человечества я отправлю эту поправку на текущий выбранный ответ.

Если вы хотите избежать использования временных переменных, есть только два разумных параметра, которые учитывают первую производительность и затем читаемость.

  • Используйте временную переменную в общем методе Swap. (Абсолютная максимальная производительность, рядом с встроенной переменной temp)
  • Используйте Interlocked.Exchange. (В 5,9 раза медленнее на моей машине, но это ваш единственный вариант, если несколько потоков будут обменивать эти переменные одновременно.)

Вещи, которые вы должны никогда делать:

  • Никогда не используйте арифметику с плавающей запятой. (медленные, округлые и переполненные ошибки, трудно понять).
  • Никогда не используйте не примитивную арифметику. (медленные, ошибки переполнения, трудно понять) Decimal не является примитивом процессора и дает гораздо больше кода, чем вы понимаете.
  • Никогда не используйте арифметический период. Или бит хаки. (медленно, трудно понять) Это компилятор. Он может оптимизироваться для разных платформ.

Потому что все любят жесткие цифры, вот программа, которая сравнивает ваши варианты. Запустите его в режиме выпуска извне Visual Studio, чтобы Swap был встроен. Результаты на моей машине (Windows 7 64-бит i5-3470):

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

код:

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

Ответ 10

< & устаревший GT;

Вы можете сделать это в 3 строках с использованием базовой математики - в моем примере я использовал умножение, но простое дополнение также сработает.

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

startAngle = startAngle * stopAngle;
stopAngle = startAngle / stopAngle;
startAngle = startAngle / stopAngle;

Изменить: как отмечено в комментариях, это не сработает, если y = 0, поскольку оно создаст деление на нулевую ошибку, которую я не рассматривал. Таким образом, альтернативное представление +/- будет лучшим способом.

</& устаревший GT;


Чтобы мой код был сразу понятен, я бы скорее сделал что-то подобное. [Всегда думайте о бедном парне, который должен поддерживать ваш код]:

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

И тогда вы можете сделать это в одной строке кода:

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

Или...

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

Готово, как ужин... теперь вы можете проходить в любом типе объекта и переключаться между ними...

Ответ 11

Если вы можете отказаться от использования decimal до double, вы можете использовать класс Interlocked. Предположительно это будет хорошим способом обмена переменными производительности. Также немного читаем, чем XOR.

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

Msdn: Interlocked.Exchange Method (Double, Double)

Ответ 12

Для полноты здесь находится двоичный свод XOR:

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

Это работает для всех атомных объектов/ссылок, поскольку это касается непосредственно байтов, но может потребовать, чтобы небезопасный контекст работал на десятичных знаках или, если вы чувствуете себя действительно искаженными, указатели. И в некоторых случаях это может быть медленнее, чем временная переменная.

Ответ 13

Остерегайтесь своей среды!

Например, это не работает в ECMAscript

y ^= x ^= y ^= x;

Но это делает

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

Мой совет? Предположите как можно меньше.

Ответ 14

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

Ответ 15

Простой способ поменять 2 числа в одной строке:

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

например: a = 1, b = 2

Шаг 1: a = (1 + 2) - (b = 1)

Шаг 2: a = 3-1

= > a = 2 и b = 1


Эффективный способ использования:

C Программирование: (x ^= y), (y ^= x), (x ^= y);

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

Python: x, y = y, x

Примечание: Наиболее распространенная ошибка: //Swap с использованием побитового XOR (неправильное решение в C/С++)

x ^= y ^= x ^= y; 

Источник: GeeksforGeek

Ответ 16

Для двоичных типов вы можете использовать этот фанковый трюк:

a %= b %= a %= b;

Пока a и b не являются одной и той же переменной (например, псевдонимы для одной и той же памяти), она работает.

Ответ 17

Надеюсь, это поможет...

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

Ответ 18

startAngle = (startAngle + stopAngle) - (stopAngle = startAngle);

Ответ 19

В С# 7:

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

Ответ 20

Здесь другой подход в одной строке:

decimal a = 159.9m;
decimal b = 355.87m;

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

Ответ 21

мы можем сделать это, выполнив простой трюк

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

Ответ 22

С С# 7 вы можете использовать деконструкцию кортежа для достижения желаемого свопа в одной строке, и это ясно, что происходит.

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

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

Ответ 23

Вот несколько разных способов обмена двумя переменными

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

Ответ 24

Если вы хотите поменять две строковые переменные:

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

Ответ 25

var a = 15;
var b = -214;
a = b | !(b = a);

Это отлично работает.

Ответ 26

Очень простой код для замены двух переменных:

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

Ответ 27

Вы можете попробовать следующий код. Это намного лучше, чем другой код.

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