Sre*_*har 63 c# algorithm swap
我希望能够在不使用C#中的临时变量的情况下交换两个变量.可以这样做吗?
decimal startAngle = Convert.ToDecimal(159.9);
decimal stopAngle = Convert.ToDecimal(355.87);
// Swap each:
// startAngle becomes: 355.87
// stopAngle becomes: 159.9
Run Code Online (Sandbox Code Playgroud)
pax*_*blo 217
交换两个变量的正确方法是:
decimal tempDecimal = startAngle;
startAngle = stopAngle;
stopAngle = tempDecimal;
Run Code Online (Sandbox Code Playgroud)
换句话说,使用临时变量.
你有它.不聪明的技巧,没有你的代码的维护者骂你几十年来,没有任何条目,以每日WTF,并没有花太多时间试图找出为什么你需要它在一个操作无论如何,因为,在最低水平,即使是最复杂的语言功能是一系列简单的操作.
只是一个非常简单,易读,易于理解的t = a; a = b; b = t;
解决方案.
在我看来,尝试使用技巧的开发人员,例如,"不使用临时交换变量"或"Duff的设备"只是试图表明他们有多聪明(并且悲惨地失败).
我将它们比作那些阅读高雅书籍的人,仅仅是为了在派对上看起来更有趣(而不是扩大你的视野).
您添加和减去的解决方案,或基于XOR的解决方案,其可读性较差,并且很可能比简单的"临时变量"解决方案(算术/布尔运算而不是汇编级别的普通移动)慢.
通过编写高质量的可读代码,为自己和他人提供服务.
那是我的咆哮.谢谢收听 :-)
顺便说一句,我很清楚这不会回答你的具体问题(我会为此道歉)但是在SO上有很多先例,人们已经问过如何做某事而且正确答案是"不要做它".
Wil*_*sem 111
首先,在没有C#语言的临时变量的情况下进行交换是一个非常糟糕的主意.
但为了回答,您可以使用以下代码:
startAngle = startAngle + stopAngle;
stopAngle = startAngle - stopAngle;
startAngle = startAngle - stopAngle;
Run Code Online (Sandbox Code Playgroud)
然而,如果这两个数字差别很大,则可能会出现问题.这是由于浮点数的性质.
如果要隐藏临时变量,可以使用实用程序方法:
public static class Foo {
public static void Swap<T> (ref T lhs, ref T rhs) {
T temp = lhs;
lhs = rhs;
rhs = temp;
}
}
Run Code Online (Sandbox Code Playgroud)
Tim*_*hyP 86
C#7引入了元组,它可以在没有临时变量的情况下交换两个变量:
int a = 10;
int b = 2;
(a, b) = (b, a);
Run Code Online (Sandbox Code Playgroud)
这种分配b
给a
和a
给b
.
Pau*_*ier 72
是的,使用此代码:
stopAngle = Convert.ToDecimal(159.9);
startAngle = Convert.ToDecimal(355.87);
Run Code Online (Sandbox Code Playgroud)
任意值都难以解决问题.:-)
thi*_*eek 43
int a = 4, b = 6;
a ^= b ^= a ^= b;
Run Code Online (Sandbox Code Playgroud)
适用于所有类型,包括字符串和浮动.
Mar*_*cus 20
BenAlabaster展示了一种实现变量切换的实用方法,但不需要try-catch子句.这段代码就足够了.
static void Swap<T>(ref T x, ref T y)
{
T t = y;
y = x;
x = t;
}
Run Code Online (Sandbox Code Playgroud)
用法与他所示的相同:
float startAngle = 159.9F
float stopAngle = 355.87F
Swap(ref startAngle, ref stopAngle);
Run Code Online (Sandbox Code Playgroud)
您还可以使用扩展方法:
static class SwapExtension
{
public static T Swap<T>(this T x, ref T y)
{
T t = y;
y = x;
return t;
}
}
Run Code Online (Sandbox Code Playgroud)
像这样使用它:
float startAngle = 159.9F;
float stopAngle = 355.87F;
startAngle = startAngle.Swap(ref stopAngle);
Run Code Online (Sandbox Code Playgroud)
两种方法都在方法中使用临时变量,但在交换时不需要临时变量.
Ste*_*uhr 15
带有详细示例的二进制XOR交换:
XOR真值表:
a b a^b
0 0 0
0 1 1
1 0 1
1 1 0
Run Code Online (Sandbox Code Playgroud)
输入:
a = 4;
b = 6;
Run Code Online (Sandbox Code Playgroud)
步骤1:a = a ^ b
a : 0100
b : 0110
a^b: 0010 = 2 = a
Run Code Online (Sandbox Code Playgroud)
步骤2:b = a ^ b
a : 0010
b : 0110
a^b: 0100 = 4 = b
Run Code Online (Sandbox Code Playgroud)
步骤3:a = a ^ b
a : 0010
b : 0100
a^b: 0110 = 6 = a
Run Code Online (Sandbox Code Playgroud)
输出:
a = 6;
b = 4;
Run Code Online (Sandbox Code Playgroud)
jnm*_*nm2 12
为了未来的学习者和人性,我将此修正提交给当前选择的答案.
如果您想避免使用临时变量,那么只有两个合理的选项可以考虑首次性能和可读性.
Swap
方法中使用temp变量.(绝对最佳性能,内联临时变量旁边)Interlocked.Exchange
.(在我的机器上慢了5.9倍,但如果多个线程同时交换这些变量,这是你唯一的选择.)你永远不应该做的事情:
Decimal
不是CPU原语,导致代码远远超出您的意识.因为每个人都喜欢硬数字,所以这是一个比较你的选择的程序.从Visual Studio外部以发布模式运行它,以便Swap
内联.在我的机器上的结果(Windows 7 64位i5-3470):
Inline: 00:00:00.7351931
Call: 00:00:00.7483503
Interlocked: 00:00:04.4076651
Run Code Online (Sandbox Code Playgroud)
码:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
Jen*_*fke 11
不在C#中.在本机代码中,您可以使用三重XOR交换技巧,但不能使用高级类型安全语言.(无论如何,我听说XOR技巧实际上比在许多常见CPU架构中使用临时变量要慢.)
你应该只使用一个临时变量.没有理由你不能使用它; 它不像供应有限.
<弃用>
您可以使用基本数学在3行中完成 - 在我的示例中,我使用了乘法,但简单的加法也可以.
float startAngle = 159.9F;
float stopAngle = 355.87F;
startAngle = startAngle * stopAngle;
stopAngle = startAngle / stopAngle;
startAngle = startAngle / stopAngle;
Run Code Online (Sandbox Code Playgroud)
编辑:如评论中所述,如果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;
}
}
Run Code Online (Sandbox Code Playgroud)
然后你可以在一行代码中完成它:
float startAngle = 159.9F
float stopAngle = 355.87F
Swap<float>(ref startAngle, ref stopAngle);
Run Code Online (Sandbox Code Playgroud)
要么...
MyObject obj1 = new MyObject("object1");
MyObject obj2 = new MyObject("object2");
Swap<MyObject>(ref obj1, ref obj2);
Run Code Online (Sandbox Code Playgroud)
像晚餐一样......你现在可以传递任何类型的物体并将它们切换到......
为了完整性,这里是二进制XOR交换:
int x = 42;
int y = 51236;
x ^= y;
y ^= x;
x ^= y;
Run Code Online (Sandbox Code Playgroud)
这适用于所有原子对象/引用,因为它直接处理字节,但可能需要一个不安全的上下文来处理小数,或者,如果你感觉真的扭曲,指针.在某些情况下,它可能比临时变量慢.
如果您可以从使用更改decimal
为double
您可以使用Interlocked
该类.据推测,这将是明智地交换变量性能的好方法.也比XOR稍微可读.
var startAngle = 159.9d;
var stopAngle = 355.87d;
stopAngle = Interlocked.Exchange(ref startAngle, stopAngle);
Run Code Online (Sandbox Code Playgroud)
Msdn:Interlocked.Exchange方法(双,双)
使用C#7,您可以使用元组解构来在一行中实现所需的交换,并且可以清楚地知道发生了什么.
decimal startAngle = Convert.ToDecimal(159.9);
decimal stopAngle = Convert.ToDecimal(355.87);
(startAngle, stopAngle) = (stopAngle, startAngle);
Run Code Online (Sandbox Code Playgroud)