class Computer
def mouse
component :mouse
end
def component(name)
puts "get the #{name} information in the computer"
end
end
cp = Computer.new
cp.mouse
代码运行成功,但是改动其中的代码:
def mouse
component :mouse
end
改为
def mouse
component(mouse)
end
出现了 stack level too deep (SystemStackError),百度了下是死循环,为什么会出现这个问题?
上面的问题已经解决,楼盖了这么高,主要是通过这个问题又引出了其他问题,引申的问题如下所示
经过楼下楼下伙伴的提醒,解释器对每个表达式都是从右向左求值,在上面的代码中,component :mouse 中参数是 symbol 对象,component 方法可以将这个已经定义的对象作为参数代入求值,而在 component mouse 这段代码中,解释器会寻找 mouse 这个变量,找到程序已经定义了这个变量作为一个方法名,因此在执行这段程序的时候,会引出如上的 SystemStackError 错误。按照解释器对每个表达式都是从右向左求值这个简单道理,如下的代码会出现 undefined variable or method 错误,代码如下所示:
def mouse
component(dog)
end
因为在解释器无法找到 dog 这个变量的定义。 总结来说,当方法的参数是已经定义的对象,比如 Symbol(:mouse)对象和 String("mouse") 对象,可以按照预期使用这些对象参数,但是如果方法的参数是变量的形式,比如 mouse 的形式,解释器会查找这个变量是否已经被定义,如果被定义成方法,则会当做方法进行调用。这些都是我自己的理解,但是如下的代码却和我理解有些矛盾,代码实现了send方法的动态调用,具体代码如下所示:
class MyClass
def my_method(my_arg)
puts my_arg*2
end
end
obj = MyClass.new
obj.send(:my_method, 3) #=>6
obj.send("my_method", 6) #=>6
obj.send(my_method, 6) #=> undefined variable or method error
在上面的代码中,:my_method 和“my_method”被当做是方法名传递给了对象 obj,而本身是 symbol 对象和 String 对象,而 my_method 变量在上面已经进行了定义,它是一个方法,在如上的代码中如果执行会报出 undefined variable or method error,而这刚好和上面最初的理解相矛盾。为什么会这样?还是说这是send方法特有的形式?
总结楼下的答案: 1、
obj.send(my_method, 6) #=> undefined variable or method error
报错的原因是 obj.send(my_method, 6) ,这个作用域是 main 对象的作用域,而 MyClass 类中的方法 my_method 不在 main 对象作用域内,因此 send 使用这个 my_method 这个参数的时候会报错,因为在 main 对象的作用域内的,my_method 方法并没有定义。 2、
obj.send(:my_method, 3) #=>6
obj.send("my_method", 6) #=>6
至于说 send 方法能将字符串对象和符号对象接受为方法,send文档本身就是这么定义,该方法只接受符号对象和字符串对象作为参数,可以认为这是 Object 的方法 send 约定的特有的形式。
经过楼下伙伴的提醒,send 从广义上说,接受的参数只要能返回字符串对象和符号对象,那么这个参数就是可行的,见如下的代码所示:
class MyClass
def my_method(my_arg)
puts my_arg*2
end
end
obj = MyClass.new
def simulate
:my_method
end
obj.send(simulate, 3) #=>6
component(mouse)
component(component(mouse))
component(component(component(mouse)))
...
def mouse
component(mouse)
end
其中的 mouse 改为任意一个变量 cc,即代码如下:
def mouse
component(cc)
end
出现了问题为:undefied local variable and method "cc",但是代码改为 component(:cc),则出现正常,那么我想问,在 componet(XX),中,如何判断 XX 是参数名还是方法名?
建议楼主看看这篇文章 http://www.ibm.com/developerworks/cn/opensource/os-cn-rubysbl/
加了冒号相当于创建了一个 symbol 对象,所以传递的参数就是这个 symbol 对象,解释器不会认为它是一个方法或者变量,就好像你使用 componet("string"),componet(1) 是一个概念。
由于 Symbol 比 String 在执行时更为高效,所以在一些仅仅只需要表示"名称"的场合,Rubyist 都会倾向于使用 symbol。例如这个 hash{"name" => "adam"},String 当然也可以用来作为 key,但几乎没有人这样用,因为 Ruby 在处理 Symbol 时更加快速,占用的资源更少。许多方法都以 Symbol 而不是 String 来作为名称参数,比如 define_method(symbol, method) 。在 Rails 里,绝大多数 DSL 后面的参数,跟的也是 Symbol。虽然 Rails 有自己的补救方法可以兼容 Symbol 和 String,但最好还是使用 Symbol 来表示名称。
不太明白为什么回答得如此复杂。
:mouse 是 symbol, 没有任何意义的一个变量,注意嘴型,这个是一个变量。 mouse 是方法名,是方法名。
def mouse compute mouse end 就是递归调用 mouse 这个方法。 没有退出条件,自然就是死循环。
#21 楼 @runup 呃,我的错,习惯了这种说法。那就来个学究点的。
A Ruby symbol is a thing that has both a number (integer) representation and a string representation.
from http://www.troubleshooters.com/codecorn/ruby/symbols.htm
只是一种表现,要怎么理解都可以。关键是,在这个场景里,把它传到方法里和把一个变量传到方法里没区别,或者说是旗标的意义会更符合实际的情况。
#16 楼 @rei @kikyous @kikyous 看书的时候遇到下面的代码,似乎和我对问题的理解以及您的解释有点相悖,代码如下:
class MyClass
def my_method(my_arg)
puts my_arg*2
end
end
obj = MyClass.new
obj.send(:my_method, 3) #=>6
obj.send("my_method", 6) #=>6
obj.send(my_method, 6) #=> undefined variable or method error
问题中将 mouse 当做是方法,把:mouse 当做是一个符号对象,而这里却刚好相反。 还是说send的这种方式是一种约定?
#30 楼 @runup Ruby 中所有的都是对象。因为 :my_method "my_method" my_method 这几个只有第三个没有具体的值 要不你再试试,你仔细看看 send 方法是做什么的
class MyClass
def my_method(my_arg)
puts my_arg*2
end
end
obj = MyClass.new
obj.send(:my_method, 3)
obj.send("my_method", 6)
my_method = "my_method"
obj.send(my_method, 6)
my_method = :my_method
obj.send(my_method, 6)
# 省略括号版本
def mouse
component mouse
end
等效于
# 带括号版本
def mouse()
component(mouse())
end
# 更明显版本(注意并不完全等价)
def mouse()
component(self.mouse())
end
里面的 mouse 既不是 String, 也不是方法名,而是一次方法调用
楼主有那么难理解吗,给你看看你这个代码的 backtrace:
(pry):24:in `mouse'
(pry):24:in `mouse'
(pry):24:in `mouse'
(pry):24:in `mouse'
(pry):24:in `mouse'
(pry):24:in `mouse'
(pry):24:in `mouse'
#...... 很多很多个 mouse 调用
(pry):24:in `mouse'
(pry):24:in `mouse'
(pry):24:in `mouse'
(pry):24:in `mouse'
(pry):24:in `mouse'
(pry):24:in `mouse'
(pry):24:in `mouse'
(pry):24:in `mouse'
(pry):24:in `mouse'
(pry):24:in `mouse'
(pry):24:in `mouse'
(pry):24:in `mouse'
(pry):24:in `mouse'
(pry):48:in `__pry__'
/Users/martin/.rvm/gems/ruby-2.2.2/gems/pry-0.10.3/lib/pry/pry_instance.rb:355:in `eval'
/Users/martin/.rvm/gems/ruby-2.2.2/gems/pry-0.10.3/lib/pry/pry_instance.rb:355:in `evaluate_ruby'
/Users/martin/.rvm/gems/ruby-2.2.2/gems/pry-0.10.3/lib/pry/pry_instance.rb:323:in `handle_line'
/Users/martin/.rvm/gems/ruby-2.2.2/gems/pry-0.10.3/lib/pry/pry_instance.rb:243:in `block (2 levels) in eval'
/Users/martin/.rvm/gems/ruby-2.2.2/gems/pry-0.10.3/lib/pry/pry_instance.rb:242:in `catch'
/Users/martin/.rvm/gems/ruby-2.2.2/gems/pry-0.10.3/lib/pry/pry_instance.rb:242:in `block in eval'
简单这么说吧,就是:
我是谁?我是我!但我是谁?我是我啊!我是谁?我是我啊!... (过了好久)溢出了~~~
object 的 send 本身的实现就只接受 symbol 和字符串。这个看看文档即可。追究为何如此,好像倒有点钻牛角尖了。当然你不妨看 send 的源码. 至于说为何是 undefined 错误,我觉得是上下文的关系,obj.send(my_method, 6) 中的 my_method, 是 main 中的 my_method, 而你并未定义.(你的 my_method 是 MyClass 中的,外部不可见). 你如果 obj.send( obj.my_method, 6) 自然就可以找到了,然而并没有什么用。send 只接受 symbol 和字符串。
至于说 send 方法能将字符串对象和符号对象接受为方法,send 文档本身就是这么定义,该方法只接受符号对象和字符串对象作为参数,可以认为这是 Object 的方法 send 约定的特有的形式。
只要你传入的参数能够返回 Symbol 对象或者 String 对象就行了,比如:
# 传入这两种类型的字面量
obj.send : my_method
obj.send "my_method"
# 方法返回 Symbol 类型的值
def my_method
: my_method
end
obj.send my_method
这些都是可以的。
关于前面的上下文的问题,建议你去阅读《Ruby 元编程》,里边有关于祖先链以及方法查找的详细例子跟解释。