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

StackOverflow https://stackoverflow.com/questions/804706

  •  03-07-2019
  •  | 
  •  

Вопрос

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

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

// Swap each:
//   startAngle becomes: 355.87
//   stopAngle becomes: 159.9
Это было полезно?

Решение

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

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

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

Другие советы

Тот Самый правильно способ поменять местами две переменные - это:

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

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

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

Просто очень простой, читаемый, понятный, t = a; a = b; b = t; решение.

На мой взгляд, разработчики, которые пытаются использовать хитрости, чтобы, например, "поменять местами переменные без использования temp" или "Устройство Даффа", просто пытаются показать, насколько они умны (и с треском проваливаются).

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

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

Окажите услугу себе и другим, написав качественный читаемый код.

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

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

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

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

Проблема сложнее для произвольных значений.:-)

Представлен C # 7 кортежи который позволяет менять местами две переменные без использования временной переменной:

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

Это присваивает b Для a и a Для b.

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

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

БенАлабастер показал практический способ выполнения переключения переменных, но предложение 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);

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

Бинарный обмен 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;

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

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

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

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

  • Используйте временную переменную в универсальном 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();
    }
}

<deprecated>

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

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

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

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

</deprecated>


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

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

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

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

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

Msdn:Заблокированный.Способ обмена (двойной, сдвоенный)

В C # 7:

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

Для полноты картины, вот бинарный обмен XOR:

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

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

Остерегайтесь своего окружения!

Например, похоже, что это не работает в ECMAScript

y ^= x ^= y ^= x;

Но это делает

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

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

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

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

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

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

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

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

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

Шаг 2:a=3-1

=> a=2 и b=1


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

Программирование на языке Си: (x ^= y), (y ^= x), (x ^= y);

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

Питон: x, y = y, x

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

x ^= y ^= x ^= y; 

Источник: GeeksforGeek - Гиксфоргик

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

َ

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

a %= b %= a %= b;

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

Я надеюсь, что это может помочь...

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

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

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

С кортежами

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

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

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

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

Вспомогательный метод соответственно:

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

Использование было бы тогда:

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

Вот еще один подход в одной строке:

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

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

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

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

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

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

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

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

a = a + b;
b = a - b;
a = a - b;
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top