Tôi tìm thấy mã này để trao đổi hai số mà không sử dụng biến thứ ba, sử dụng XOR ^
nhà điều hành.
Mã số:
int i = 25;
int j = 36;
j ^= i;
i ^= j;
j ^= i;
Console.WriteLine("i:" + i + " j:" + j);
//numbers Swapped correctly
//Output: i:36 j:25
Bây giờ tôi đã thay đổi mã trên thành mã tương đương này.
Ma cua toi:
int i = 25;
int j = 36;
j ^= i ^= j ^= i; // I have changed to this equivalent (???).
Console.WriteLine("i:" + i + " j:" + j);
//Not Swapped correctly
//Output: i:36 j:0
Bây giờ, tôi muốn biết, Tại sao mã của tôi cho đầu ra không chính xác?
EDIT: Được rồi, đã nhận nó.
Điểm đầu tiên cần làm là rõ ràng bạn không nên sử dụng mã này. Tuy nhiên, khi bạn mở rộng nó, nó sẽ trở thành tương đương với:
j = j ^ (i = i ^ (j = j ^ i));
(Nếu chúng ta sử dụng một biểu thức phức tạp hơn như foo.bar++ ^= i
, điều quan trọng là ++
chỉ được đánh giá một lần, nhưng ở đây tôi tin rằng nó đơn giản hơn.)
Bây giờ, thứ tự đánh giá của các toán hạng luôn luôn là trái sang phải, do đó, để bắt đầu với chúng ta nhận được:
j = 36 ^ (i = i ^ (j = j ^ i));
Điều này (ở trên) là bước quan trọng nhất. Chúng tôi đã kết thúc với 36 là LHS cho các hoạt động XOR được thực hiện cuối cùng. LHS không phải là "giá trị của j
sau khi đánh giá RHS ".
Việc đánh giá RHS của ^ liên quan đến biểu thức "một cấp lồng nhau", vì vậy nó sẽ trở thành:
j = 36 ^ (i = 25 ^ (j = j ^ i));
Sau đó, nhìn vào mức độ sâu nhất của làm tổ, chúng ta có thể thay thế cả hai i
và j
:
j = 36 ^ (i = 25 ^ (j = 25 ^ 36));
... trở thành
j = 36 ^ (i = 25 ^ (j = 61));
Bài tập cho j
trong RHS xảy ra đầu tiên, nhưng kết quả sau đó được ghi đè vào cuối anyway, vì vậy chúng tôi có thể bỏ qua điều đó - không có đánh giá thêm về j
trước bài tập cuối cùng:
j = 36 ^ (i = 25 ^ 61);
Điều này bây giờ tương đương với:
i = 25 ^ 61;
j = 36 ^ (i = 25 ^ 61);
Hoặc là:
i = 36;
j = 36 ^ 36;
Trở thành:
i = 36;
j = 0;
tôi suy nghĩ đó là tất cả chính xác, và nó được câu trả lời đúng ... xin lỗi Eric Lippert nếu một số chi tiết về thứ tự đánh giá là hơi off: (
Kiểm tra IL tạo ra và nó cho ra kết quả khác nhau;
Việc hoán đổi chính xác tạo ra một cách đơn giản:
IL_0001: ldc.i4.s 25
IL_0003: stloc.0 //create a integer variable 25 at position 0
IL_0004: ldc.i4.s 36
IL_0006: stloc.1 //create a integer variable 36 at position 1
IL_0007: ldloc.1 //push variable at position 1 [36]
IL_0008: ldloc.0 //push variable at position 0 [25]
IL_0009: xor
IL_000a: stloc.1 //store result in location 1 [61]
IL_000b: ldloc.0 //push 25
IL_000c: ldloc.1 //push 61
IL_000d: xor
IL_000e: stloc.0 //store result in location 0 [36]
IL_000f: ldloc.1 //push 61
IL_0010: ldloc.0 //push 36
IL_0011: xor
IL_0012: stloc.1 //store result in location 1 [25]
Việc hoán đổi không chính xác tạo ra mã này:
IL_0001: ldc.i4.s 25
IL_0003: stloc.0 //create a integer variable 25 at position 0
IL_0004: ldc.i4.s 36
IL_0006: stloc.1 //create a integer variable 36 at position 1
IL_0007: ldloc.1 //push 36 on stack (stack is 36)
IL_0008: ldloc.0 //push 25 on stack (stack is 36-25)
IL_0009: ldloc.1 //push 36 on stack (stack is 36-25-36)
IL_000a: ldloc.0 //push 25 on stack (stack is 36-25-36-25)
IL_000b: xor //stack is 36-25-61
IL_000c: dup //stack is 36-25-61-61
IL_000d: stloc.1 //store 61 into position 1, stack is 36-25-61
IL_000e: xor //stack is 36-36
IL_000f: dup //stack is 36-36-36
IL_0010: stloc.0 //store 36 into positon 0, stack is 36-36
IL_0011: xor //stack is 0, as the original 36 (instead of the new 61) is xor-ed)
IL_0012: stloc.1 //store 0 into position 1
Hiển nhiên rằng mã được tạo trong phương thức thứ hai được kết hợp, vì giá trị cũ của j được sử dụng trong một phép tính, nơi giá trị mới được yêu cầu.
C # tải j
, i
, j
, i
trên ngăn xếp và lưu trữ từng XOR
kết quả mà không cập nhật ngăn xếp, vì vậy ngoài cùng bên trái XOR
sử dụng giá trị ban đầu cho j
.
Viết lại:
j ^= i;
i ^= j;
j ^= i;
Mở rộng ^=
:
j = j ^ i;
i = j ^ i;
j = j ^ i;
Thay thế:
j = j ^ i;
j = j ^ (i = j ^ i);
Thay thế điều này chỉ hoạt động nếu / vì phía bên tay trái của toán tử ^ được đánh giá đầu tiên:
j = (j = j ^ i) ^ (i = i ^ j);
Sự sụp đổ ^
:
j = (j ^= i) ^ (i ^= j);
Đối xứng:
i = (i ^= j) ^ (j ^= i);