Nice programing

임시 변수를 사용하지 않고 두 변수 교체

nicepro 2020. 11. 25. 21:08
반응형

임시 변수를 사용하지 않고 두 변수 교체


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;

즉, 임시 변수를 사용하십시오.

거기에 있습니다. 영리한 속임수도없고, 앞으로 수십 년 동안 당신을 저주하는 코드 관리자도없고, The Daily WTF에 대한 항목 도 없으며, 최하위 수준에서 심지어는 한 번의 작업에서 왜 필요한지 알아 내려고 너무 많은 시간을 소비하지 않습니다. 가장 복잡한 언어 기능은 일련의 간단한 작업입니다.

매우 간단하고 읽기 쉽고 이해하기 쉬운 t = a; a = b; b = t;솔루션입니다.

제 생각에는 "임시를 사용하지 않고 변수를 교체"하거나 "더프의 장치"와 같이 트릭을 사용하려는 개발자는 단지 그들이 얼마나 영리하고 비참하게 실패하는지 보여 주려고합니다.

나는 그들을 (당신의 시야를 넓히는 것과는 반대로) 파티에서 더 흥미로워 보일 목적으로 만 하이 브로우 책을 읽는 사람들에 비유합니다.

더하고 빼는 솔루션 또는 XOR 기반 솔루션은 읽기가 어렵고 단순한 "임시 변수"솔루션 (어셈블리 수준에서 일반 이동 대신 산술 / 부울 연산)보다 느립니다.

좋은 품질의 읽기 가능한 코드를 작성하여 자신과 다른 사람들에게 서비스를 제공하십시오.

그게 내 폭언입니다. 듣기 주셔서 감사합니다 :-)

제쳐두고, 나는 이것이 당신의 특정 질문에 대답하지 않는다는 것을 잘 알고 있습니다 (그리고 그것에 대해 사과 할 것입니다) 사람들이 무언가를하는 방법을 묻고 정답은 "하지 마십시오 해".


C # 7에서는 임시 변수없이 두 개의 변수를 바꿀 수 있는 튜플도입했습니다 .

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

이것은 baa할당 됩니다 b.


예, 다음 코드를 사용하십시오.

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

임의 값의 경우 문제가 더 어렵습니다. :-)


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 스왑 :

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;

미래의 학습자와 인류를 위해 현재 선택된 답변에이 수정 사항을 제출합니다.

임시 변수를 사용하지 않으 려면 먼저 성능을 고려한 다음 가독성을 고려하는 두 가지 합리적인 옵션 만 있습니다 .

  • 일반 Swap메서드 에서 임시 변수를 사용합니다 . (절대 최고 성능, 인라인 임시 변수 옆)
  • 사용 Interlocked.Exchange. (내 컴퓨터에서는 5.9 배 느리지 만 여러 스레드가 이러한 변수를 동시에 스왑하는 경우 유일한 옵션입니다.)

절대 하지 말아야 할 것 :

  • 부동 소수점 산술을 사용하지 마십시오. (느림, 반올림 및 오버플로 오류, 이해하기 어려움)
  • 원시가 아닌 산술을 사용하지 마십시오. (느림, 오버플로 오류, 이해하기 어려운) DecimalCPU 기본 요소가 아니며 사용자가 생각하는 것보다 훨씬 많은 코드가 생성됩니다.
  • 산술 기간을 사용하지 마십시오. 또는 약간의 해킹. (느리고 이해하기 어렵습니다) 그게 컴파일러의 일입니다. 다양한 플랫폼에 최적화 할 수 있습니다.

모두가 어려운 숫자를 좋아하기 때문에 옵션을 비교하는 프로그램이 있습니다. 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();
    }
}

C #이 아닙니다. 네이티브 코드에서는 triple-XOR 스왑 트릭을 사용할 수 있지만 높은 수준의 형식 안전 언어에서는 사용할 수 없습니다. (어쨌든 나는 XOR 트릭이 실제로 많은 일반적인 CPU 아키텍처에서 임시 변수를 사용하는 것보다 느리다고 들었습니다.)

임시 변수를 사용해야합니다. 사용할 수없는 이유는 없습니다. 제한된 공급이있는 것과는 다릅니다.


<사용되지 않음>

기본 수학을 사용하여 3 줄로 할 수 있습니다. 제 예에서는 곱셈을 사용했지만 간단한 덧셈도 작동합니다.

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

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

편집 : 주석에서 언급했듯이 y = 0이면 고려하지 않은 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변경할 수 있으면 수업을 double사용할 수 있습니다 Interlocked. 아마도 이것은 변수 성능을 현명하게 바꾸는 좋은 방법이 될 것입니다. 또한 XOR보다 약간 더 읽기 쉽습니다.

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

Msdn : Interlocked.Exchange 메서드 (Double, Double)


C # 7 :

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

완전성을 위해 다음은 바이너리 XOR 스왑입니다.

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

이것은 바이트를 직접 처리하기 때문에 모든 원자 객체 / 참조에 대해 작동하지만 소수점 또는 실제로 뒤틀린 느낌이 든다면 포인터에서 작동하려면 안전하지 않은 컨텍스트가 필요할 수 있습니다. 또한 어떤 상황에서는 임시 변수보다 느릴 수도 있습니다.


C # 7에서는 튜플 분해를 사용하여 한 줄로 원하는 스왑을 달성 할 수 있으며 무슨 일이 일어나고 있는지 분명합니다.

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

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

환경을 조심하십시오!

예를 들어, 이것은 ECMAscript에서 작동하지 않는 것 같습니다.

y ^= x ^= y ^= x;

그러나 이것은

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

내 조언? 가능한 한 적게 가정하십시오.


한 줄로 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);

자바: 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);

튜플 사용

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

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

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;

다음은 두 개의 변수를 교환하는 몇 가지 다른 프로세스입니다.

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

참고URL : https://stackoverflow.com/questions/804706/swap-two-variables-without-using-a-temporary-variable

반응형