@user.update_languages(params[:language][:language1], 
                       params[:language][:language2], 
                       params[:language][:language3])
lang_errors = @user.errors
logger.debug "--------------------LANG_ERRORS----------101-------------" 
                + lang_errors.full_messages.inspect

if params[:user]
  @user.state = params[:user][:state]
  success = success & @user.save
end
logger.debug "--------------------LANG_ERRORS-------------102----------" 
                + lang_errors.full_messages.inspect

if lang_errors.full_messages.empty?

@user对象将错误添加到update_languages方法中的lang_errors变量中。 当我在@user对象上执行保存时,我丢失了最初存储在lang_errors变量中的错误。

虽然我正在尝试做的更多的是一个黑客(似乎没有工作)。我想知道为什么变量值被洗掉了。我理解通过引用传递,所以我想知道值如何可以保存在那个变量中而不被洗掉。


当前回答

Ruby是解释性的。变量是对数据的引用,而不是数据本身。这便于对不同类型的数据使用相同的变量。

赋值lhs = rhs然后复制rhs上的引用,而不是数据。这与其他语言不同,比如C语言,赋值是从rhs复制数据到lhs。

对于函数调用,传递的变量,比如x,确实被复制到函数中的局部变量中,但x是一个引用。然后会有两个引用副本,它们都引用相同的数据。一个在调用者中,一个在函数中。

函数中的赋值会将一个新的引用复制到函数的x版本。在此之后,调用者的x版本保持不变。它仍然是对原始数据的引用。

相反,在x上使用.replace方法将导致ruby执行数据复制。如果在任何新赋值之前使用replace,那么调用者确实也会在其版本中看到数据更改。

类似地,只要传入变量的原始引用是恰当的,则调用者看到的实例变量将是相同的。在对象的框架内,实例变量总是具有最新的引用值,无论这些引用值是由调用者提供的,还是在类传入的函数中设置的。

“按值调用”或“按引用调用”在这里被混淆了,因为在编译语言中“=”是一个数据副本。在这种解释语言中,'='是一个参考副本。在这个例子中,你有一个传入的引用,后面跟着一个引用副本,尽管'='破坏了传入的原始引用,然后人们谈论它就好像'='是一个数据副本。

为了与定义保持一致,我们必须与'保持一致。替换',因为它是一个数据副本。从的角度来看。替换'我们看到这确实是通过引用传递的。此外,如果我们在调试器中遍历,我们会看到传入的引用,因为变量是引用。

However if we must keep '=' as a frame of reference, then indeed we do get to see the passed in data up until an assignment, and then we don't get to see it anymore after assignment while the caller's data remains unchanged. At a behavioral level this is pass by value as long as we don't consider the passed in value to be composite - as we won't be able to keep part of it while changing the other part in a single assignment (as that assignment changes the reference and the original goes out of scope). There will also be a wart, in that instance variables in objects will be references, as are all variables. Hence we will be forced to talk about passing 'references by value' and have to use related locutions.

其他回答

Ruby是解释性的。变量是对数据的引用,而不是数据本身。这便于对不同类型的数据使用相同的变量。

赋值lhs = rhs然后复制rhs上的引用,而不是数据。这与其他语言不同,比如C语言,赋值是从rhs复制数据到lhs。

对于函数调用,传递的变量,比如x,确实被复制到函数中的局部变量中,但x是一个引用。然后会有两个引用副本,它们都引用相同的数据。一个在调用者中,一个在函数中。

函数中的赋值会将一个新的引用复制到函数的x版本。在此之后,调用者的x版本保持不变。它仍然是对原始数据的引用。

相反,在x上使用.replace方法将导致ruby执行数据复制。如果在任何新赋值之前使用replace,那么调用者确实也会在其版本中看到数据更改。

类似地,只要传入变量的原始引用是恰当的,则调用者看到的实例变量将是相同的。在对象的框架内,实例变量总是具有最新的引用值,无论这些引用值是由调用者提供的,还是在类传入的函数中设置的。

“按值调用”或“按引用调用”在这里被混淆了,因为在编译语言中“=”是一个数据副本。在这种解释语言中,'='是一个参考副本。在这个例子中,你有一个传入的引用,后面跟着一个引用副本,尽管'='破坏了传入的原始引用,然后人们谈论它就好像'='是一个数据副本。

为了与定义保持一致,我们必须与'保持一致。替换',因为它是一个数据副本。从的角度来看。替换'我们看到这确实是通过引用传递的。此外,如果我们在调试器中遍历,我们会看到传入的引用,因为变量是引用。

However if we must keep '=' as a frame of reference, then indeed we do get to see the passed in data up until an assignment, and then we don't get to see it anymore after assignment while the caller's data remains unchanged. At a behavioral level this is pass by value as long as we don't consider the passed in value to be composite - as we won't be able to keep part of it while changing the other part in a single assignment (as that assignment changes the reference and the original goes out of scope). There will also be a wart, in that instance variables in objects will be references, as are all variables. Hence we will be forced to talk about passing 'references by value' and have to use related locutions.

参数是原始引用的副本。因此,您可以更改值,但不能更改原始引用。

Two references refer to same object as long as there is no reassignment. 

同一对象中的任何更新都不会引用到新的内存,因为它仍然在相同的内存中。 以下是一些例子:

    a = "first string"
    b = a



    b.upcase! 
    => FIRST STRING
    a
    => FIRST STRING

    b = "second string"


a
    => FIRST STRING
    hash = {first_sub_hash: {first_key: "first_value"}}
first_sub_hash = hash[:first_sub_hash]
first_sub_hash[:second_key] = "second_value"

    hash
    => {first_sub_hash: {first_key: "first_value", second_key: "second_value"}}

    def change(first_sub_hash)
    first_sub_hash[:third_key] = "third_value"
    end

    change(first_sub_hash)

    hash
    =>  {first_sub_hash: {first_key: "first_value", second_key: "second_value", third_key: "third_value"}}

在传统术语中,Ruby是严格的值传递。但这不是你真正想要的。

Ruby doesn't have any concept of a pure, non-reference value, so you certainly can't pass one to a method. Variables are always references to objects. In order to get an object that won't change out from under you, you need to dup or clone the object you're passed, thus giving an object that nobody else has a reference to. (Even this isn't bulletproof, though — both of the standard cloning methods do a shallow copy, so the instance variables of the clone still point to the same objects that the originals did. If the objects referenced by the ivars mutate, that will still show up in the copy, since it's referencing the same objects.)

Ruby使用“对象引用传递”

(使用Python术语。)

说Ruby使用“按值传递”或“按引用传递”并不是真正的描述性足够有帮助。我想现在大多数人都知道,这个术语(“值”vs“引用”)来自c++。

在c++中,“传递值”意味着函数获得变量的副本,对副本的任何更改都不会改变原始变量。对象也是如此。如果你通过值传递一个对象变量,那么整个对象(包括它的所有成员)都会被复制,对成员的任何更改都不会改变原始对象上的那些成员。(如果你通过值传递一个指针,这是不同的,但Ruby没有指针,AFAIK。)

class A {
  public:
    int x;
};

void inc(A arg) {
  arg.x++;
  printf("in inc: %d\n", arg.x); // => 6
}

void inc(A* arg) {
  arg->x++;
  printf("in inc: %d\n", arg->x); // => 1
}

int main() {
  A a;
  a.x = 5;
  inc(a);
  printf("in main: %d\n", a.x); // => 5

  A* b = new A;
  b->x = 0;
  inc(b);
  printf("in main: %d\n", b->x); // => 1

  return 0;
}

输出:

in inc: 6
in main: 5
in inc: 1
in main: 1

在c++中,“引用传递”意味着函数可以访问原始变量。它可以分配一个全新的字面值整数,然后原始变量也会有这个值。

void replace(A &arg) {
  A newA;
  newA.x = 10;
  arg = newA;
  printf("in replace: %d\n", arg.x);
}

int main() {
  A a;
  a.x = 5;
  replace(a);
  printf("in main: %d\n", a.x);

  return 0;
}

输出:

in replace: 10
in main: 10

如果参数不是一个对象,Ruby使用按值传递(在c++的意义上)。但是在Ruby中,所有的东西都是一个对象,所以在Ruby中没有c++意义上的值传递。

在Ruby中,“通过对象引用传递”(使用Python的术语)被使用:

在函数内部,对象的任何成员都可以有新的值赋给他们,这些变化将在函数返回.*后持续存在 在函数内部,将一个全新的对象赋值给变量会导致变量停止引用旧对象。但是在函数返回后,原始变量仍然会引用旧对象。

因此Ruby不使用c++意义上的“引用传递”。如果存在,那么将一个新对象分配给函数内的变量将导致在函数返回后忘记旧对象。

class A
  attr_accessor :x
end

def inc(arg)
  arg.x += 1
  puts arg.x
end

def replace(arg)
  arg = A.new
  arg.x = 3
  puts arg.x
end

a = A.new
a.x = 1
puts a.x  # 1

inc a     # 2
puts a.x  # 2

replace a # 3
puts a.x  # 2

puts ''

def inc_var(arg)
  arg += 1
  puts arg
end

b = 1     # Even integers are objects in Ruby
puts b    # 1
inc_var b # 2
puts b    # 1

输出:

1
2
2
3
2

1
2
1

*这就是为什么在Ruby中,如果你想在函数中修改一个对象,但在函数返回时忘记这些更改,那么你必须在对副本进行临时更改之前显式地创建一个对象的副本。