質問
C#で一時変数を使用せずに2つの変数を交換できるようにしたいと思います。これはできますか?
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;
ただし、2つの数値が大きく異なる場合、四捨五入で問題が発生する可能性があります。これは、浮動小数点数の性質によるものです。
一時変数を非表示にする場合は、ユーティリティメソッドを使用できます。
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 へのエントリー、および支出なしとにかく、1つの操作でそれが必要な理由を理解しようとする時間が長すぎます。なぜなら、最も低いレベルでは、最も複雑な言語機能でさえ、一連の単純な操作だからです。
非常にシンプルで読みやすく、理解しやすいt = a; a = b; b = t;
ソリューションです。
たとえば、<!> quot; temp <!> quotを使用せずに変数を交換するためにトリックを使用しようとする開発者は、私の意見です。または<!> quot; Duffのデバイス<!> quot;どれだけ賢いかを見せようとしているだけです(そして惨めに失敗しています)。
(視野を広げるのではなく)パーティーでもっと面白そうに見えることだけを目的として、ハイブロウの本を読んでいる人にそれらを例えます。
加算と減算のソリューション、またはXORベースのソリューションは、単純な<!> quot; temp変数<!> quot;よりも読みにくく、おそらく遅いです。解決策(アセンブリレベルでの単純な移動ではなく、算術/ブール演算)。
高品質で読み取り可能なコードを記述して、自分自身や他の人にサービスを提供します。
それは私の暴言です。ご清聴ありがとうございました:-)
余談ですが、これがあなたの特定の質問に答えないことはよくわかっています(そして、それについて謝罪します)が、SOには多くの先例があり、人々は何かをする方法を尋ねており、正しい答えは< !> quot;しないでください<!> quot;。
はい、このコードを使用します:
stopAngle = Convert.ToDecimal(159.9);
startAngle = Convert.ToDecimal(355.87);
任意の値の場合、問題はより困難です。 :-)
C#7で tuples :一時的な変数を使用せずに2つの変数を交換できます:
int a = 10;
int b = 2;
(a, b) = (b, a);
これは、b
をa
に、<=>を<=>に割り当てます。
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;
C#にはありません。ネイティブコードでは、トリプルXORスワップトリックを使用できる場合がありますが、高レベルのタイプセーフ言語では使用できません。 (とにかく、多くの一般的なCPUアーキテクチャで一時変数を使用するよりも実際にXORトリックが遅くなると聞きました。)
一時変数を使用するだけです。使用できない理由はありません。供給が限られているわけではありません。
将来の学習者と人類のために、この修正を現在選択されている回答に提出します。
一時変数の使用を避けたい場合は、最初のパフォーマンス、次に読みやすさを考慮に入れる 2つの賢明なオプションのみがあります。
- 一般的な
Swap
メソッドで一時変数を使用します。 (絶対的な最高のパフォーマンス、インライン一時変数の横) -
Interlocked.Exchange
を使用します。 (私のマシンでは5.9倍遅くなりますが、複数のスレッドがこれらの変数を同時にスワップする場合、これが唯一のオプションです。)
絶対にしないでください:
- 浮動小数点演算を使用しないでください。 (遅い、丸め、オーバーフローエラー、わかりにくい)
- 非プリミティブ算術を使用しないでください。 (遅い、オーバーフローエラー、わかりにくい)
Decimal
はCPUプリミティブではなく、結果ですあなたが理解するよりもはるかに多くのコードで。 - 算術ピリオドを使用しないでください。 または少しハックします。(遅く、理解しにくい)それがコンパイラの仕事です。多くの異なるプラットフォーム向けに最適化できます。
誰もがハードナンバーを好むので、ここにあなたのオプションを比較するプログラムがあります。 <=>がインライン化されるように、Visual Studioの外部からリリースモードで実行します。私のマシンでの結果(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();
}
}
<!> lt; deprecated <!> gt;
基本的な数学を使用して3行で実行できます。この例では乗算を使用しましたが、単純な加算でも機能します。
float startAngle = 159.9F;
float stopAngle = 355.87F;
startAngle = startAngle * stopAngle;
stopAngle = startAngle / stopAngle;
startAngle = startAngle / stopAngle;
編集:コメントで述べたように、y = 0の場合、これは機能しません。なぜなら、ゼロ除算エラーが生成されたため、私は考慮していなかったからです。したがって、代わりに提示される+/-ソリューションが最善の方法です。
<!> lt; / deprecated <!> gt;
コードをすぐにわかりやすくするために、このようなことをする可能性が高くなります。 [コードを保守しなければならない貧しい人について常に考えてください]:
static bool Swap<T>(ref T x, ref T y)
{
try
{
T t = y;
y = x;
x = t;
return true;
}
catch
{
return false;
}
}
そして、次の1行のコードでできます:
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;
これは、バイトを直接処理するため、すべてのアトミックオブジェクト/参照に対して機能しますが、小数や、実際にねじれていると感じる場合はポインターで動作するために安全でないコンテキストが必要になる場合があります。また、状況によっては一時変数よりも遅い場合があります。
環境に注意してください!
たとえば、これは<!>#8217; tはECMAscriptで動作しないようです
y ^= x ^= y ^= x;
しかし、これは
x ^= y ^= x; y ^= x;
私のアドバイス?できるだけ少ないと仮定します。
C#7を使用すると、タプルの分解を使用して1行で目的のスワップを実現できます。何が起こっているかは明らかです。
decimal startAngle = Convert.ToDecimal(159.9);
decimal stopAngle = Convert.ToDecimal(355.87);
(startAngle, stopAngle) = (stopAngle, startAngle);
1行で2つの数字を入れ替える簡単な方法:
a=(a+b)-(b=a);
eg:a = 1、b = 2
ステップ1:a =(1 + 2)-(b = 1)
ステップ2:a = 3-1
= <!> gt; a = 2およびb = 1
効率的な使用方法:
Cプログラミング:(x ^= y), (y ^= x), (x ^= y);
Java:x = x ^ y ^ (y = x);
Python:x, y = y, x
注:よくある間違い: ビット単位のXORを使用して // Swap(C / C ++の間違ったソリューション)
x ^= y ^= x ^= y;
出典: GeeksforGeek
a = a + b
b = a - b
a = a - b
<!>#1614;
バイナリ型の場合、このファンキーなトリックを使用できます:
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);
ここで別のアプローチを1行で:
decimal a = 159.9m;
decimal b = 355.87m;
a = b + (b = a) - b;
2つの変数を交換するいくつかの異なるプロセスがあります
//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);
これはうまく機能します。
2つの変数を交換するための非常に単純なコード:
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;