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



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



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

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

28 ответов:

прежде всего, замена без временной переменной в языке, поскольку 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;
    }
}

The право способ замены двух переменных:

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

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

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

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

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

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

Это моя декламация. Спасибо, что выслушали :-)

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

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

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

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

C# 7 введен ОК что позволяет заменить две переменные без временной:

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

в результате b to a и a до b.

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

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

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

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

двоичное исключающее или поменять с подробным пример:

таблица правды 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:Б = А ^ Б

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#. В машинном коде вы можете использовать трюк с заменой triple-XOR, но не на высокоуровневом типобезопасном языке. (Во всяком случае, я слышал, что трюк XOR на самом деле оказывается медленнее, чем использование временной переменной во многих распространенных архитектурах ЦП.)

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

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

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

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

вы должны никогда do:

  • никогда не используйте арифметику с плавающей запятой. (медленные ошибки округления и переполнения, трудно понять)
  • никогда не используйте непримитивную арифметику. (медленно, ошибки переполнения, трудно понять) 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();
    }
}

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

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

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

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


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

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 to double можно использовать Interlocked класса. Предположительно, это будет хороший способ обмена переменными с точки зрения производительности. Также немного более читаемый, чем XOR.

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

Msdn: Блокируется.Метод Обмена (Double, Double)

для полноты, вот двоичный 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);

В C# 7:

(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


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

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

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

Python: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);
    }
}
startAngle = (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;

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

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

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

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

с кортежами

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

Это прекрасно работает.

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

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;

Comments

    Ничего не найдено.