如果一切都是对象,为啥我不能交换两个变量? [复制]
Posted
技术标签:
【中文标题】如果一切都是对象,为啥我不能交换两个变量? [复制]【英文标题】:If everything is an object, why can't I swap two variables? [duplicate]如果一切都是对象,为什么我不能交换两个变量? [复制] 【发布时间】:2017-06-18 13:14:11 【问题描述】:我有一个简单的测试:
def swap(a, b)
c = a
a = b
b = c
end
a = 3
b = 4
swap(a, b)
print a # 3. not change value
我知道此示例不适用于 Java 或 C# 等某些语言,因为这些变量是原语。但是在 Ruby 中,如果一切都是对象,我认为上面的示例应该可以工作。请给我解释一下。
我认为问题在于 FixNum 是不可变的。我有使用 Java 的概念验证代码来制作一个不可变的整数类:
static class CustomInteger
Integer value;
public CustomInteger(int i)
this.value = i;
public void changeValue(int i)
this.value = i;
public int getValue()
return value;
public CustomInteger setValueImmutable(int i)
return new CustomInteger(i);
public static void swap(CustomInteger a, CustomInteger b)
int c = a.getValue();
a.changeValue(b.getValue());
b.changeValue(c);
public static void main(String[] args)
CustomInteger i1 = new CustomInteger(3);
CustomInteger i2 = new CustomInteger(4);
swap(i1, i2);
System.out.println(i1.getValue());
public static void notWorkSwap(Integer a, Integer b)
int temp_a = a;
int temp_b = b;
a = temp_b;
b = temp_a;
【问题讨论】:
Fixnum
对象是不可变的。见this answer。
@TrầnKimDự,我 1000% 确定这个确切的代码不会在 c/c++/c#/java/whatever 中交换它们。如果您传递指针并更改这些指针的地址 - 是的。但不是这样的常规任务。
@TrầnKimDự,这不是原因。注意你是如何使用a.changeValue(b.getValue())
而不是a = b
或a = b.getValue()
。赋值 (=
) 只是改变了局部变量所指的内容。前一个值或新值是否可变并不重要。
@TrầnKimDự,装箱/拆箱是对应类的基元和对象之间的神奇转换。这与它无关。调用changeValue
不是“为对象赋值”,它调用了一个为实例变量赋值的方法。它仍在调用方法,而不是赋值。
@TrầnKimDự,它不起作用,因为您假设取消装箱会更改值是不正确的。它不会改变任何东西,它只是为您提供底层的原始值。装箱也不会改变任何东西,它会创建一个包装该值的新对象。在右侧完成任何包装/展开之后,它仍然像正常分配一样简单地分配。
【参考方案1】:
您的 swap
方法仅交换该方法的本地变量。
在方法内部,a
和 b
被交换。不过,这不会影响方法之外的任何内容。例如,c
没有在 swap
之外定义。
请注意,要交换 a
和 b
,您不需要任何其他变量:a, b = b, a
是有效的 Ruby 语法。
最后,变量aren't objects。它们只是引用对象。
这是实现您想做的事情的一种方法。 更新:它基本上相当于你的 Java 代码:
class MyObject
attr_accessor :x
def initialize(x)
@x = x
end
end
def swap(f,g)
f.x, g.x = g.x, f.x
end
a = MyObject.new(1)
b = MyObject.new(2)
swap(a, b)
puts a.x
#=> 2
puts b.x
#=> 1
请注意,对象 a
和 b
尚未交换。不过,它们的 x
值已被交换。
另一种可能性是使用实例变量:
def swap
@a, @b = @b, @a
end
@a = 1
@b = 2
swap
p @a
#=> 2
p @b
#=> 1
【讨论】:
【参考方案2】:这不起作用的原因是方法参数是按值传递的,而不是按引用传递的。默认情况下,只有少数语言使用传递引用语义。 Java 和 Ruby 中的行为是相同的。
但是,很多人滥用该术语就够棘手了。例如,请参阅此问题。 Is Java "pass-by-reference" or "pass-by-value"?
在许多语言中,例如 C#,可以使用特殊关键字(在 C# 中为 ref,或在 C++ 中类型后的 & 标记)进行引用传递。您可以通过传递间接指针来模拟 C 等语言中的行为(尽管由于不那么严格的类型检查,这会给您更多的权力)。
我已经有几年没有写过 C# 代码了,但是你想要达到的目标大概是:
static void SwapStrings(ref string s1, ref string s2)
string temp = s1;
s1 = s2;
s2 = temp;
它将被调用SwapStrings(ref str1, ref str2);
此代码是此处内容的缩写版本:https://msdn.microsoft.com/en-us/library/s6938f28.aspx
【讨论】:
它们是本地的,因为它们是该函数范围内的值。如果你有一个 byref a,byref b 它们现在是引用,技术上仍然是局部变量,但由于调用者有引用,所以调用函数可以使用。 在这个意义上你是对的;这些天我的 C 有点生疏了,IIRC 你必须使用双重间接来支持 C 中的交换。 我认为这里的问题是因为 Ruby 中的 FixNum 是不可变的。如果它们是可变的,它会起作用。请看看我更新的问题。我有用于概念验证的邮政编码 Java。 @ndn:是的,C 仍然按值传递指针。 C 通过值传递所有内容,就像 Ruby 一样。 (事实上,Ruby 基本上就像总是按值传递指针,并且由于传递的所有内容始终是指针,因此没有像 C 中那样获取和取消引用指针的特殊语法。)但是在 C++ 和 C 等语言中♯ ,确实具有传递引用语义,您可以影响调用者范围内的引用;事实上,这几乎就是传递引用的定义。 @TrầnKimDự:您发布的 Java 代码和 Ruby 代码完全不同。在 Ruby 代码中您正在修改引用,在 Java 代码中您正在修改对象。这些是完全不同的事情。上面写着“Trần Kim Dự”的便利贴和你不一样。仅供参考。以上是关于如果一切都是对象,为啥我不能交换两个变量? [复制]的主要内容,如果未能解决你的问题,请参考以下文章
为啥使用类变量的这段代码不能像在 Java 中那样工作? [复制]