1 symbols简介
在ruby中一个symbol就是一个Symbol类的实例,他的语法也就是简单的一个冒号后面跟着一个声明.
一个symbol就像一个字符串,可是它和字符串所不同的是每一个symbol都只有一个实例。我们举个例子:
[code]array = ["foo", "foo", "foo", :foo, :foo, :foo][/code]
在这个例子中字符串"foo"在内存里面被存储为3个不同的对象,而:foo则是被存储为一个对象,只不过是多个引用而已。
在ruby1.6之前的版本,一个symbol不是一个first-class 的对象,他是被传递给一个Fixnum ,然后再存储起来。在现在的版本中,内部仍然是这么实现的,一个symbol表示为一个数字然后被存储。这个数字可以用to_i来得到。
在这里一个symbol和他的名字之间有了一个一一对应的关系,我们操作他就像操作其他东西一样.
symbol经常使用到的地方就是表示一个变量或者一个方法,例如如果我们想要加一个读写方法给一个类中的变量我们能这么做:
[code]class SomeClass
attr_accessor :whatever
end[/code]
他和下面的代码是等价的:
[code]class SomeClass
def whatever
@whatever
end
def whatever=(val)
@whatever = val
end
end[/code]
这里你可能要问为什么要用symbol而不是用string。其实这里也就是一个习惯了。大部分使用symbol的地方都能用字符串来代替。
一个symbol像是一个字符串,这导致很多人说,symbol就是一个不可变得字符串。这个观点非常错误,因为Symbol并没有从String类继承什么东西。并且很多典型的操作符只能用用于字符串,而不能用于symbol。
另外一个误会是symbols能够直接联系到他的标识符,这导致很多人在讨论the symbol table。可是这是没有意义的,因为尽管symbols在内部是有一个table,可是那个表对于外部程序来说是不可见的,我们并不能访问它。
一个symbols并不需要看起来像是一个标识符:
[code]
sym1 = :"This is a symbol"
sym2 = :"This is, too!"
sym3 = :")(*&^%$" # and even this[/code]
你可能使用symbols来定义实例变量和方法,这时你就需要使用诸如send and instance_variable_get 这样的方法来操作他们。这种情况下,不建议你使用字符串。
2 作为枚举的symbol
象pascal和c这样的语言提供了枚举类型。ruby没有真正意义上的这个东西。我么能够使用symbols来模拟这个东西:
[code]North, South, East, West = :north, :south, :east, :west[/code]
虽然这边还可以使用string,可是一个symbol只有一个变量,使我们选择symbol更合理
3 symbol作为一个Metavalues
经常我们使用异常来代替使用返回码。我们经常需要一些值来作为错误时的返回值(比如nil),例如asc字符NUL被认为不是一个字符,c有空指针,pascal也有nil指针,ruby也有nil。
可是问题来了,在ruby中nil不是一个真正的 非对象,它能被存储和操作,比如我们有一个has[key]=nil.当我们调用这个返回时,我们不知道是真的返回nil,还是说出错没有找到key所以返回nil。
这个时候,我们能使用symbol:
[code]str = get_string
case str
when String
# Proceed normally
when :eof
# end of file, socket closed, whatever
when :error
# I/O or network error
when :timeout
# didn't get a reply
end
[/code]
这个代码一定就比使用异常更清晰和更聪明吗,不一定这个方法只是说能让你保持在你的脑子里就行了。
4 symbol,变量和方法
可能最广为人知的使用symbol就是在一个类里面定义一个属性:
[code]class MyClass
attr_reader :alpha, :beta
attr_writer :gamma, :delta
attr_accessor :epsilon
# ...
end[/code]
attr_accessor使用symbol来表示实例的名字和读写方法的名字。可是这并不意味着实例的名字和symbol之间有什么联系请看例子:
[code]sym1 = :@foo
sym2 = :foo
instance_variable_set(sym1,"str") # Works
instance_variable_set(sym2,"str") # 出错[/code]
简而言之,一个symbol只是作为一个参数给atrr之类的方法,这些方法,基于symbol的值来创建实例变量和方法(写方法结尾有一个=,实例变量前面加上@)。因此symbol之和它的引用标记之间有联系。
5 symbols的转换
字符串和symbols之间的转换使用to_s和to_sym方法:
[code]a = "foobar"
b = :foobar
puts a == b.to_s # true
puts b == a.to_sym # true [/code]
如果你正在使用元编程(metaprogramming),那么接下来的方法有时会非常有用:
[code]class Symbol
def +(other)
(self.to_s + other.to_s).to_sym
end
end[/code]
+方法使我们能够连接两个symbol。接下来就是使用他的例子。accessor方法接受一个symbol为参数,并测试它是否含有读写方法:
[code]class Object
def accessor?(sym)
return (self.respond_to?(sym) and self.respond_to?(sym+"="))
end
end[/code]
现在,我要提一个使用symbol得很聪明的例子:
当我们使用map的时候,一个很复杂的block总是跟在后面,就算我们只是简单的进行以下操作,比如:
[code]list = words.map {|x| x.capitalize }[/code]
在这里,我们似乎为了一个很简单的功能做了太多的东西。让我们打开Symbol类,定义一个to_proc的方法,这个方法就会在必要的时候将一个symbol转型为一个proc。但是我们应当返回什么proc呢?显然,有两个参数,一个是对象上下文中的symbol自身。也就是将symbol他自己作为一个消息传递给的那个对象:
[code]
def to_proc
proc {|obj, *args| obj.send(self, *args) }
end[/code]
这个代码,来自于Gavin Sinclair的Ruby Extensions工程。现在我们能够重写我们上面的代码了:
[code]
list = words.map(&:capitalize)[/code]
花时间理解这段代码是很有意义的。map方法接受一个block为参数。这里将一个不是proc的类传递给block,这时解释器会自动调用to_proc方法,来将symbol转换成一个block。然后返回proc,这时map会为他的每一个参数调用它。为什么self作为一个消息传递给数组是正确的呢?那是因为proc是一个闭包,它能够记忆他被创建时的上下文环境。当他被创建时self指向的就是to_proc被创建时的那个symbol。
在ruby中一个symbol就是一个Symbol类的实例,他的语法也就是简单的一个冒号后面跟着一个声明.
一个symbol就像一个字符串,可是它和字符串所不同的是每一个symbol都只有一个实例。我们举个例子:
[code]array = ["foo", "foo", "foo", :foo, :foo, :foo][/code]
在这个例子中字符串"foo"在内存里面被存储为3个不同的对象,而:foo则是被存储为一个对象,只不过是多个引用而已。
在ruby1.6之前的版本,一个symbol不是一个first-class 的对象,他是被传递给一个Fixnum ,然后再存储起来。在现在的版本中,内部仍然是这么实现的,一个symbol表示为一个数字然后被存储。这个数字可以用to_i来得到。
在这里一个symbol和他的名字之间有了一个一一对应的关系,我们操作他就像操作其他东西一样.
symbol经常使用到的地方就是表示一个变量或者一个方法,例如如果我们想要加一个读写方法给一个类中的变量我们能这么做:
[code]class SomeClass
attr_accessor :whatever
end[/code]
他和下面的代码是等价的:
[code]class SomeClass
def whatever
@whatever
end
def whatever=(val)
@whatever = val
end
end[/code]
这里你可能要问为什么要用symbol而不是用string。其实这里也就是一个习惯了。大部分使用symbol的地方都能用字符串来代替。
一个symbol像是一个字符串,这导致很多人说,symbol就是一个不可变得字符串。这个观点非常错误,因为Symbol并没有从String类继承什么东西。并且很多典型的操作符只能用用于字符串,而不能用于symbol。
另外一个误会是symbols能够直接联系到他的标识符,这导致很多人在讨论the symbol table。可是这是没有意义的,因为尽管symbols在内部是有一个table,可是那个表对于外部程序来说是不可见的,我们并不能访问它。
一个symbols并不需要看起来像是一个标识符:
[code]
sym1 = :"This is a symbol"
sym2 = :"This is, too!"
sym3 = :")(*&^%$" # and even this[/code]
你可能使用symbols来定义实例变量和方法,这时你就需要使用诸如send and instance_variable_get 这样的方法来操作他们。这种情况下,不建议你使用字符串。
2 作为枚举的symbol
象pascal和c这样的语言提供了枚举类型。ruby没有真正意义上的这个东西。我么能够使用symbols来模拟这个东西:
[code]North, South, East, West = :north, :south, :east, :west[/code]
虽然这边还可以使用string,可是一个symbol只有一个变量,使我们选择symbol更合理
3 symbol作为一个Metavalues
经常我们使用异常来代替使用返回码。我们经常需要一些值来作为错误时的返回值(比如nil),例如asc字符NUL被认为不是一个字符,c有空指针,pascal也有nil指针,ruby也有nil。
可是问题来了,在ruby中nil不是一个真正的 非对象,它能被存储和操作,比如我们有一个has[key]=nil.当我们调用这个返回时,我们不知道是真的返回nil,还是说出错没有找到key所以返回nil。
这个时候,我们能使用symbol:
[code]str = get_string
case str
when String
# Proceed normally
when :eof
# end of file, socket closed, whatever
when :error
# I/O or network error
when :timeout
# didn't get a reply
end
[/code]
这个代码一定就比使用异常更清晰和更聪明吗,不一定这个方法只是说能让你保持在你的脑子里就行了。
4 symbol,变量和方法
可能最广为人知的使用symbol就是在一个类里面定义一个属性:
[code]class MyClass
attr_reader :alpha, :beta
attr_writer :gamma, :delta
attr_accessor :epsilon
# ...
end[/code]
attr_accessor使用symbol来表示实例的名字和读写方法的名字。可是这并不意味着实例的名字和symbol之间有什么联系请看例子:
[code]sym1 = :@foo
sym2 = :foo
instance_variable_set(sym1,"str") # Works
instance_variable_set(sym2,"str") # 出错[/code]
简而言之,一个symbol只是作为一个参数给atrr之类的方法,这些方法,基于symbol的值来创建实例变量和方法(写方法结尾有一个=,实例变量前面加上@)。因此symbol之和它的引用标记之间有联系。
5 symbols的转换
字符串和symbols之间的转换使用to_s和to_sym方法:
[code]a = "foobar"
b = :foobar
puts a == b.to_s # true
puts b == a.to_sym # true [/code]
如果你正在使用元编程(metaprogramming),那么接下来的方法有时会非常有用:
[code]class Symbol
def +(other)
(self.to_s + other.to_s).to_sym
end
end[/code]
+方法使我们能够连接两个symbol。接下来就是使用他的例子。accessor方法接受一个symbol为参数,并测试它是否含有读写方法:
[code]class Object
def accessor?(sym)
return (self.respond_to?(sym) and self.respond_to?(sym+"="))
end
end[/code]
现在,我要提一个使用symbol得很聪明的例子:
当我们使用map的时候,一个很复杂的block总是跟在后面,就算我们只是简单的进行以下操作,比如:
[code]list = words.map {|x| x.capitalize }[/code]
在这里,我们似乎为了一个很简单的功能做了太多的东西。让我们打开Symbol类,定义一个to_proc的方法,这个方法就会在必要的时候将一个symbol转型为一个proc。但是我们应当返回什么proc呢?显然,有两个参数,一个是对象上下文中的symbol自身。也就是将symbol他自己作为一个消息传递给的那个对象:
[code]
def to_proc
proc {|obj, *args| obj.send(self, *args) }
end[/code]
这个代码,来自于Gavin Sinclair的Ruby Extensions工程。现在我们能够重写我们上面的代码了:
[code]
list = words.map(&:capitalize)[/code]
花时间理解这段代码是很有意义的。map方法接受一个block为参数。这里将一个不是proc的类传递给block,这时解释器会自动调用to_proc方法,来将symbol转换成一个block。然后返回proc,这时map会为他的每一个参数调用它。为什么self作为一个消息传递给数组是正确的呢?那是因为proc是一个闭包,它能够记忆他被创建时的上下文环境。当他被创建时self指向的就是to_proc被创建时的那个symbol。