Поменяйте местами две переменные без использования временной переменной
Вопрос
Я бы хотел иметь возможность менять местами две переменные без использования временной переменной в 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);
В 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;