2007-08-07
每天一条Ruby小道之Symbol
关键字: Symbol
在Ruby中symbol是Symbol类的实例。symbol的语法为一个冒号后面接一个标识符。
symbol就像一个字符串,它表示了一个字符序列。它不像字符串,每个symbol只有一个实例(和Fixnum一样)。因此,有一个内存或
性能问题需要弄清楚。例如,在下面的代码中,"foo"字符串以三个单独的对象存储在内存中,但是symbol :foo作为一个单独的对
象存储(被引用了多次):
有些人被symbol前面的冒号弄糊涂了。其实没有必要糊涂;这只是一个简单的语法形式。字符串,数组和哈希都有开始和结束界定
符;而symbol只有一个开始界定符。把它当成一个单界定符而不是二元界定符即可。开始时你可能认为这个语法很奇怪,但事实上
没有什么神秘的。
值得注意的是在老版本的Ruby(1.6之前)中,symbol常量不是第一类对象而是转换成Fixnum来存储。现在在内核中这仍然是对的;
symbol对应于一个数字并作为一个immediate value来存储。这个值可以使用to_i来得到,但是没有必要。
根据Jim Weirich,symbol是"有名字的对象"。Austin Ziegler更喜欢说"an object that is a name"。无论怎样,在symbol和名字
间始终有一个一对一的对应。什么样的东西我们需要使用名字?例如变量,方法和任意的常量。
一个常见的symbol使用场景是表示变量或方法的名字。例如,我们知道如果我们想添加一个读/写属性到类中,我们可以这样做:
这等价于:
换句话说,:whatever这个symbol告诉attr_accessor方法"getter"和"setter(以及实例变量)都将根据该symbol来给予名字。
你可能想问为什么我们不能使用一个字符串来替代。我们可以。大部分希望使用symbol的核心方法都可以使用字符串来替代。
事实上,symbol就"像"一个字符串,它对应于一个字符序列。这导致有些人说"a symbol is just an immutalbe string"。
但是,Symbol类并不是继承于String,并且对于字符串典型的操作并不适合symbol。
另一个误解是认为symbol没必要直接对应于标识符。这导致有些人说"the symbol table"(as they would in referring to
an assembled object program)。但是这不是一个真正有用的概念;尽管在内部symbol存储在一种table里,Ruby并没有将
这个table作为一个实体暴露给我们访问,而我们作为编程者也不关心它的存在。
而且,symbol不需要看起来像标识符。虽然它们像标识符,但是它们也可以包含标点,如果它们被引号包围的话。如下也是
合法的Ruby symbol:
你甚至可以使用这些symbol来定义实例变量和方法,但是这样的话你需要send和instance_variable_get技术来引用它们。
一般来说不推荐使用字符串形式的symbol。
Symbols As Enumerations
Pascal以及后期版本的C等语言有一个enumerated type的概念。Ruby没有这样的东西;Ruby没有类型检查。但是symbol
非常有用;我可以用:north,:south,:east和:west来表示方向。
将这些symbol存储为常量可能更清晰。
如果它们是字符串而不是symbol,将它们定义为常量会节省内存,但无论如何每个symbol都只在对象空间里存在一次。
(像Fixnum一样Symbol存储为immediate value)
Symbols As Metavalues
我们经常使用异常来作为避免返回代码的方式。但是如果你宁愿使用返回代码的话也可以。RUby的方法不限于单返回类型,
这让传回"out of band"值成为可能。
我们经常需要这样的值。ASCII NUL字符被认为根本就不是一个字符。C有NULL指针,Pascal有nil指针,SQL有NULL等等。
Ruby当然也有nil。
这些metavalue的问题是它们成为合法的值。现在每个人都认为NUL是一个ASCII字符。在Ruby中,nil不是真的non-object;
它可以被存储和操作。这样我们有了如hash[key]返回nil这样的小烦恼;是因为key没有找到而返回nil,还是由于这个key
真的指向nil?
这里symbol可以用来作为好的metavalue。假设一个方法从忘了攫取一个字符串(可能通过http或其他类似的东西)。我们可以
返回非字符串值来表示出现异常。
这比使用异常更好或更清晰?没必要。但是记住,这是处理可能为"edge cases"但不一定是异常的条件时的一项技术。
Symbols, Variables, and Methods
可能使用symbol的最知名的地方是定义类的属性:
attr_accessor使用symbol名字来觉得实例变量和读写方法的名字。这不代表symbol和实例变量名之间有绝对的关系。
例如,如果你使用instance_variable_set,我们必须指定变量的名字,包括@符号:
简短来说,传递到attr系列方法的symbol只是一个参数,这些方法基于symbol的值创建需要的实例变量和方法。(writer方法
在结尾有一个=,而实例变量名字在前面有一个@。)换句话说,symbol必须与它引用的标识符相对应。
大部分情况下,希望使用symbol的方法也可以使用字符串。相反则不一定正确。
Converting to/from Symbols
字符串和symbol可以使用to_s和to_sym方法自由互换:
如果你做元编程,如下方法有时候可能很有用。
上面的方法允许我们连接symbol(或者添加字符串到一个symbol)。下面是一个使用它的例子;这段微小的代码接收一个symbol并
尝试告诉我们它是否是一个accessor(即,读写方法都存在):
我这里要提到一个使用symbol的聪明的方式。当我们做map操作时,有时候一个复杂的block可能附加在后面。但是许多情况下,
我们对每个元素简单的调用一个方法:
在这里,似乎我们为完成工作做了太多事情。让我们打开Symbol类并定义to_proc方法。这保证任何symbol都可以强制转换成
一个proc对象。但是我们应该返回什么proc?显然应该根据对象context里的symbol本身。换句话说,proc应该将symbol本身作
为一个消息发送给对象。
顺便提一下,这段代码来自Gavin Sinclair的"Ruby Extensions"对象。有了这个方法,我们可以重写上面的代码:
值得花费一分钟来理解它怎样工作。map方法使用一个block作为参数。&符号允许我们传递一个proc而不是一个显式附加的
block。由于我们在一个非proc对象上使用&符号,解释器会尝试调用该对象的to_proc方法。返回的proc替代了显式的block,
map将会对每个元素调用它。为什么self作为一个消息传递给array元素有意义呢?这是因为proc是一个闭包,所以它会记住
它被创建在哪个context里。当它被创建时,self表示调用to_proc的symbol。
symbol就像一个字符串,它表示了一个字符序列。它不像字符串,每个symbol只有一个实例(和Fixnum一样)。因此,有一个内存或
性能问题需要弄清楚。例如,在下面的代码中,"foo"字符串以三个单独的对象存储在内存中,但是symbol :foo作为一个单独的对
象存储(被引用了多次):
array = ["foo", "foo", "foo", :foo, :foo, :foo]
有些人被symbol前面的冒号弄糊涂了。其实没有必要糊涂;这只是一个简单的语法形式。字符串,数组和哈希都有开始和结束界定
符;而symbol只有一个开始界定符。把它当成一个单界定符而不是二元界定符即可。开始时你可能认为这个语法很奇怪,但事实上
没有什么神秘的。
值得注意的是在老版本的Ruby(1.6之前)中,symbol常量不是第一类对象而是转换成Fixnum来存储。现在在内核中这仍然是对的;
symbol对应于一个数字并作为一个immediate value来存储。这个值可以使用to_i来得到,但是没有必要。
根据Jim Weirich,symbol是"有名字的对象"。Austin Ziegler更喜欢说"an object that is a name"。无论怎样,在symbol和名字
间始终有一个一对一的对应。什么样的东西我们需要使用名字?例如变量,方法和任意的常量。
一个常见的symbol使用场景是表示变量或方法的名字。例如,我们知道如果我们想添加一个读/写属性到类中,我们可以这样做:
class SomeClass attr_accessor :whatever end
这等价于:
class SomeClass
def whatever
@whatever
end
def whatever=(val)
@whatever = val
end
end
换句话说,:whatever这个symbol告诉attr_accessor方法"getter"和"setter(以及实例变量)都将根据该symbol来给予名字。
你可能想问为什么我们不能使用一个字符串来替代。我们可以。大部分希望使用symbol的核心方法都可以使用字符串来替代。
attr_reader :alpha attr_reader "beta" # This is also legal
事实上,symbol就"像"一个字符串,它对应于一个字符序列。这导致有些人说"a symbol is just an immutalbe string"。
但是,Symbol类并不是继承于String,并且对于字符串典型的操作并不适合symbol。
另一个误解是认为symbol没必要直接对应于标识符。这导致有些人说"the symbol table"(as they would in referring to
an assembled object program)。但是这不是一个真正有用的概念;尽管在内部symbol存储在一种table里,Ruby并没有将
这个table作为一个实体暴露给我们访问,而我们作为编程者也不关心它的存在。
而且,symbol不需要看起来像标识符。虽然它们像标识符,但是它们也可以包含标点,如果它们被引号包围的话。如下也是
合法的Ruby symbol:
sym1 = :"This is a symbol" sym2 = :"This is, too1" sym3 = :")(*&^%$" # and even this
你甚至可以使用这些symbol来定义实例变量和方法,但是这样的话你需要send和instance_variable_get技术来引用它们。
一般来说不推荐使用字符串形式的symbol。
Symbols As Enumerations
Pascal以及后期版本的C等语言有一个enumerated type的概念。Ruby没有这样的东西;Ruby没有类型检查。但是symbol
非常有用;我可以用:north,:south,:east和:west来表示方向。
将这些symbol存储为常量可能更清晰。
North, South, East, West = :north, :south, :east, :west
如果它们是字符串而不是symbol,将它们定义为常量会节省内存,但无论如何每个symbol都只在对象空间里存在一次。
(像Fixnum一样Symbol存储为immediate value)
Symbols As Metavalues
我们经常使用异常来作为避免返回代码的方式。但是如果你宁愿使用返回代码的话也可以。RUby的方法不限于单返回类型,
这让传回"out of band"值成为可能。
我们经常需要这样的值。ASCII NUL字符被认为根本就不是一个字符。C有NULL指针,Pascal有nil指针,SQL有NULL等等。
Ruby当然也有nil。
这些metavalue的问题是它们成为合法的值。现在每个人都认为NUL是一个ASCII字符。在Ruby中,nil不是真的non-object;
它可以被存储和操作。这样我们有了如hash[key]返回nil这样的小烦恼;是因为key没有找到而返回nil,还是由于这个key
真的指向nil?
这里symbol可以用来作为好的metavalue。假设一个方法从忘了攫取一个字符串(可能通过http或其他类似的东西)。我们可以
返回非字符串值来表示出现异常。
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
这比使用异常更好或更清晰?没必要。但是记住,这是处理可能为"edge cases"但不一定是异常的条件时的一项技术。
Symbols, Variables, and Methods
可能使用symbol的最知名的地方是定义类的属性:
class MyClass attr_reader :alpha, :beta attr_writer :gamma, :delta attr_accessor :epsilon # ... end
attr_accessor使用symbol名字来觉得实例变量和读写方法的名字。这不代表symbol和实例变量名之间有绝对的关系。
例如,如果你使用instance_variable_set,我们必须指定变量的名字,包括@符号:
sym1 = :@foo sym2 = :foo instance_variable_set(sym1, "str") # Works instance_variable_set(sym2, "str") # errors
简短来说,传递到attr系列方法的symbol只是一个参数,这些方法基于symbol的值创建需要的实例变量和方法。(writer方法
在结尾有一个=,而实例变量名字在前面有一个@。)换句话说,symbol必须与它引用的标识符相对应。
大部分情况下,希望使用symbol的方法也可以使用字符串。相反则不一定正确。
Converting to/from Symbols
字符串和symbol可以使用to_s和to_sym方法自由互换:
a = "foobar" b = :foobar a == b.to_s # true b == a.to_sym # true
如果你做元编程,如下方法有时候可能很有用。
class Symbol
def +(other)
(self.to_s + other.to_s).to_sym
end
end
上面的方法允许我们连接symbol(或者添加字符串到一个symbol)。下面是一个使用它的例子;这段微小的代码接收一个symbol并
尝试告诉我们它是否是一个accessor(即,读写方法都存在):
class Object
def accessor?(sym)
return (self.respond_to?(sym) and self.respond_to?(sym+"="))
end
end
我这里要提到一个使用symbol的聪明的方式。当我们做map操作时,有时候一个复杂的block可能附加在后面。但是许多情况下,
我们对每个元素简单的调用一个方法:
list = words.map { |x| x.capitalize }
在这里,似乎我们为完成工作做了太多事情。让我们打开Symbol类并定义to_proc方法。这保证任何symbol都可以强制转换成
一个proc对象。但是我们应该返回什么proc?显然应该根据对象context里的symbol本身。换句话说,proc应该将symbol本身作
为一个消息发送给对象。
def to_proc
proc { |obj, *args| obj.send(self, *args) }
end
顺便提一下,这段代码来自Gavin Sinclair的"Ruby Extensions"对象。有了这个方法,我们可以重写上面的代码:
list = words.map(&:capitalize)
值得花费一分钟来理解它怎样工作。map方法使用一个block作为参数。&符号允许我们传递一个proc而不是一个显式附加的
block。由于我们在一个非proc对象上使用&符号,解释器会尝试调用该对象的to_proc方法。返回的proc替代了显式的block,
map将会对每个元素调用它。为什么self作为一个消息传递给array元素有意义呢?这是因为proc是一个闭包,所以它会记住
它被创建在哪个context里。当它被创建时,self表示调用to_proc的symbol。
- 12:35
- 浏览 (2857)
- 论坛浏览 (6007)
- 评论 (8)
- 分类: Ruby
- 相关推荐
评论
funjackyone
2008-05-24
回复
hideto 写道
在Ruby中symbol是Symbol类的实例。symbol的语法为一个冒号后面接一个标识符。
symbol就像一个字符串,它表示了一个字符序列。它不像字符串,每个symbol只有一个实例(和Fixnum一样)。因此,有一个内存或
性能问题需要弄清楚。例如,在下面的代码中,"foo"字符串以三个单独的对象存储在内存中,但是symbol :foo作为一个单独的对
象存储(被引用了多次):
有些人被symbol前面的冒号弄糊涂了。其实没有必要糊涂;这只是一个简单的语法形式。字符串,数组和哈希都有开始和结束界定
符;而symbol只有一个开始界定符。把它当成一个单界定符而不是二元界定符即可。开始时你可能认为这个语法很奇怪,但事实上
没有什么神秘的。
值得注意的是在老版本的Ruby(1.6之前)中,symbol常量不是第一类对象而是转换成Fixnum来存储。现在在内核中这仍然是对的;
symbol对应于一个数字并作为一个immediate value来存储。这个值可以使用to_i来得到,但是没有必要。
根据Jim Weirich,symbol是"有名字的对象"。Austin Ziegler更喜欢说"an object that is a name"。无论怎样,在symbol和名字
间始终有一个一对一的对应。什么样的东西我们需要使用名字?例如变量,方法和任意的常量。
一个常见的symbol使用场景是表示变量或方法的名字。例如,我们知道如果我们想添加一个读/写属性到类中,我们可以这样做:
这等价于:
换句话说,:whatever这个symbol告诉attr_accessor方法"getter"和"setter(以及实例变量)都将根据该symbol来给予名字。
你可能想问为什么我们不能使用一个字符串来替代。我们可以。大部分希望使用symbol的核心方法都可以使用字符串来替代。
事实上,symbol就"像"一个字符串,它对应于一个字符序列。这导致有些人说"a symbol is just an immutalbe string"。
但是,Symbol类并不是继承于String,并且对于字符串典型的操作并不适合symbol。
另一个误解是认为symbol没必要直接对应于标识符。这导致有些人说"the symbol table"(as they would in referring to
an assembled object program)。但是这不是一个真正有用的概念;尽管在内部symbol存储在一种table里,Ruby并没有将
这个table作为一个实体暴露给我们访问,而我们作为编程者也不关心它的存在。
而且,symbol不需要看起来像标识符。虽然它们像标识符,但是它们也可以包含标点,如果它们被引号包围的话。如下也是
合法的Ruby symbol:
你甚至可以使用这些symbol来定义实例变量和方法,但是这样的话你需要send和instance_variable_get技术来引用它们。
一般来说不推荐使用字符串形式的symbol。
Symbols As Enumerations
Pascal以及后期版本的C等语言有一个enumerated type的概念。Ruby没有这样的东西;Ruby没有类型检查。但是symbol
非常有用;我可以用:north,:south,:east和:west来表示方向。
将这些symbol存储为常量可能更清晰。
如果它们是字符串而不是symbol,将它们定义为常量会节省内存,但无论如何每个symbol都只在对象空间里存在一次。
(像Fixnum一样Symbol存储为immediate value)
Symbols As Metavalues
我们经常使用异常来作为避免返回代码的方式。但是如果你宁愿使用返回代码的话也可以。RUby的方法不限于单返回类型,
这让传回"out of band"值成为可能。
我们经常需要这样的值。ASCII NUL字符被认为根本就不是一个字符。C有NULL指针,Pascal有nil指针,SQL有NULL等等。
Ruby当然也有nil。
这些metavalue的问题是它们成为合法的值。现在每个人都认为NUL是一个ASCII字符。在Ruby中,nil不是真的non-object;
它可以被存储和操作。这样我们有了如hash[key]返回nil这样的小烦恼;是因为key没有找到而返回nil,还是由于这个key
真的指向nil?
这里symbol可以用来作为好的metavalue。假设一个方法从忘了攫取一个字符串(可能通过http或其他类似的东西)。我们可以
返回非字符串值来表示出现异常。
这比使用异常更好或更清晰?没必要。但是记住,这是处理可能为"edge cases"但不一定是异常的条件时的一项技术。
Symbols, Variables, and Methods
可能使用symbol的最知名的地方是定义类的属性:
attr_accessor使用symbol名字来觉得实例变量和读写方法的名字。这不代表symbol和实例变量名之间有绝对的关系。
例如,如果你使用instance_variable_set,我们必须指定变量的名字,包括@符号:
简短来说,传递到attr系列方法的symbol只是一个参数,这些方法基于symbol的值创建需要的实例变量和方法。(writer方法
在结尾有一个=,而实例变量名字在前面有一个@。)换句话说,symbol必须与它引用的标识符相对应。
大部分情况下,希望使用symbol的方法也可以使用字符串。相反则不一定正确。
Converting to/from Symbols
字符串和symbol可以使用to_s和to_sym方法自由互换:
如果你做元编程,如下方法有时候可能很有用。
上面的方法允许我们连接symbol(或者添加字符串到一个symbol)。下面是一个使用它的例子;这段微小的代码接收一个symbol并
尝试告诉我们它是否是一个accessor(即,读写方法都存在):
我这里要提到一个使用symbol的聪明的方式。当我们做map操作时,有时候一个复杂的block可能附加在后面。但是许多情况下,
我们对每个元素简单的调用一个方法:
在这里,似乎我们为完成工作做了太多事情。让我们打开Symbol类并定义to_proc方法。这保证任何symbol都可以强制转换成
一个proc对象。但是我们应该返回什么proc?显然应该根据对象context里的symbol本身。换句话说,proc应该将symbol本身作
为一个消息发送给对象。
顺便提一下,这段代码来自Gavin Sinclair的"Ruby Extensions"对象。有了这个方法,我们可以重写上面的代码:
值得花费一分钟来理解它怎样工作。map方法使用一个block作为参数。&符号允许我们传递一个proc而不是一个显式附加的
block。由于我们在一个非proc对象上使用&符号,解释器会尝试调用该对象的to_proc方法。返回的proc替代了显式的block,
map将会对每个元素调用它。为什么self作为一个消息传递给array元素有意义呢?这是因为proc是一个闭包,所以它会记住
它被创建在哪个context里。当它被创建时,self表示调用to_proc的symbol。
symbol就像一个字符串,它表示了一个字符序列。它不像字符串,每个symbol只有一个实例(和Fixnum一样)。因此,有一个内存或
性能问题需要弄清楚。例如,在下面的代码中,"foo"字符串以三个单独的对象存储在内存中,但是symbol :foo作为一个单独的对
象存储(被引用了多次):
array = ["foo", "foo", "foo", :foo, :foo, :foo]
有些人被symbol前面的冒号弄糊涂了。其实没有必要糊涂;这只是一个简单的语法形式。字符串,数组和哈希都有开始和结束界定
符;而symbol只有一个开始界定符。把它当成一个单界定符而不是二元界定符即可。开始时你可能认为这个语法很奇怪,但事实上
没有什么神秘的。
值得注意的是在老版本的Ruby(1.6之前)中,symbol常量不是第一类对象而是转换成Fixnum来存储。现在在内核中这仍然是对的;
symbol对应于一个数字并作为一个immediate value来存储。这个值可以使用to_i来得到,但是没有必要。
根据Jim Weirich,symbol是"有名字的对象"。Austin Ziegler更喜欢说"an object that is a name"。无论怎样,在symbol和名字
间始终有一个一对一的对应。什么样的东西我们需要使用名字?例如变量,方法和任意的常量。
一个常见的symbol使用场景是表示变量或方法的名字。例如,我们知道如果我们想添加一个读/写属性到类中,我们可以这样做:
class SomeClass attr_accessor :whatever end
这等价于:
class SomeClass
def whatever
@whatever
end
def whatever=(val)
@whatever = val
end
end
换句话说,:whatever这个symbol告诉attr_accessor方法"getter"和"setter(以及实例变量)都将根据该symbol来给予名字。
你可能想问为什么我们不能使用一个字符串来替代。我们可以。大部分希望使用symbol的核心方法都可以使用字符串来替代。
attr_reader :alpha attr_reader "beta" # This is also legal
事实上,symbol就"像"一个字符串,它对应于一个字符序列。这导致有些人说"a symbol is just an immutalbe string"。
但是,Symbol类并不是继承于String,并且对于字符串典型的操作并不适合symbol。
另一个误解是认为symbol没必要直接对应于标识符。这导致有些人说"the symbol table"(as they would in referring to
an assembled object program)。但是这不是一个真正有用的概念;尽管在内部symbol存储在一种table里,Ruby并没有将
这个table作为一个实体暴露给我们访问,而我们作为编程者也不关心它的存在。
而且,symbol不需要看起来像标识符。虽然它们像标识符,但是它们也可以包含标点,如果它们被引号包围的话。如下也是
合法的Ruby symbol:
sym1 = :"This is a symbol" sym2 = :"This is, too1" sym3 = :")(*&^%$" # and even this
你甚至可以使用这些symbol来定义实例变量和方法,但是这样的话你需要send和instance_variable_get技术来引用它们。
一般来说不推荐使用字符串形式的symbol。
Symbols As Enumerations
Pascal以及后期版本的C等语言有一个enumerated type的概念。Ruby没有这样的东西;Ruby没有类型检查。但是symbol
非常有用;我可以用:north,:south,:east和:west来表示方向。
将这些symbol存储为常量可能更清晰。
North, South, East, West = :north, :south, :east, :west
如果它们是字符串而不是symbol,将它们定义为常量会节省内存,但无论如何每个symbol都只在对象空间里存在一次。
(像Fixnum一样Symbol存储为immediate value)
Symbols As Metavalues
我们经常使用异常来作为避免返回代码的方式。但是如果你宁愿使用返回代码的话也可以。RUby的方法不限于单返回类型,
这让传回"out of band"值成为可能。
我们经常需要这样的值。ASCII NUL字符被认为根本就不是一个字符。C有NULL指针,Pascal有nil指针,SQL有NULL等等。
Ruby当然也有nil。
这些metavalue的问题是它们成为合法的值。现在每个人都认为NUL是一个ASCII字符。在Ruby中,nil不是真的non-object;
它可以被存储和操作。这样我们有了如hash[key]返回nil这样的小烦恼;是因为key没有找到而返回nil,还是由于这个key
真的指向nil?
这里symbol可以用来作为好的metavalue。假设一个方法从忘了攫取一个字符串(可能通过http或其他类似的东西)。我们可以
返回非字符串值来表示出现异常。
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
这比使用异常更好或更清晰?没必要。但是记住,这是处理可能为"edge cases"但不一定是异常的条件时的一项技术。
Symbols, Variables, and Methods
可能使用symbol的最知名的地方是定义类的属性:
class MyClass attr_reader :alpha, :beta attr_writer :gamma, :delta attr_accessor :epsilon # ... end
attr_accessor使用symbol名字来觉得实例变量和读写方法的名字。这不代表symbol和实例变量名之间有绝对的关系。
例如,如果你使用instance_variable_set,我们必须指定变量的名字,包括@符号:
sym1 = :@foo sym2 = :foo instance_variable_set(sym1, "str") # Works instance_variable_set(sym2, "str") # errors
简短来说,传递到attr系列方法的symbol只是一个参数,这些方法基于symbol的值创建需要的实例变量和方法。(writer方法
在结尾有一个=,而实例变量名字在前面有一个@。)换句话说,symbol必须与它引用的标识符相对应。
大部分情况下,希望使用symbol的方法也可以使用字符串。相反则不一定正确。
Converting to/from Symbols
字符串和symbol可以使用to_s和to_sym方法自由互换:
a = "foobar" b = :foobar a == b.to_s # true b == a.to_sym # true
如果你做元编程,如下方法有时候可能很有用。
class Symbol
def +(other)
(self.to_s + other.to_s).to_sym
end
end
上面的方法允许我们连接symbol(或者添加字符串到一个symbol)。下面是一个使用它的例子;这段微小的代码接收一个symbol并
尝试告诉我们它是否是一个accessor(即,读写方法都存在):
class Object
def accessor?(sym)
return (self.respond_to?(sym) and self.respond_to?(sym+"="))
end
end
我这里要提到一个使用symbol的聪明的方式。当我们做map操作时,有时候一个复杂的block可能附加在后面。但是许多情况下,
我们对每个元素简单的调用一个方法:
list = words.map { |x| x.capitalize }
在这里,似乎我们为完成工作做了太多事情。让我们打开Symbol类并定义to_proc方法。这保证任何symbol都可以强制转换成
一个proc对象。但是我们应该返回什么proc?显然应该根据对象context里的symbol本身。换句话说,proc应该将symbol本身作
为一个消息发送给对象。
def to_proc
proc { |obj, *args| obj.send(self, *args) }
end
顺便提一下,这段代码来自Gavin Sinclair的"Ruby Extensions"对象。有了这个方法,我们可以重写上面的代码:
list = words.map(&:capitalize)
值得花费一分钟来理解它怎样工作。map方法使用一个block作为参数。&符号允许我们传递一个proc而不是一个显式附加的
block。由于我们在一个非proc对象上使用&符号,解释器会尝试调用该对象的to_proc方法。返回的proc替代了显式的block,
map将会对每个元素调用它。为什么self作为一个消息传递给array元素有意义呢?这是因为proc是一个闭包,所以它会记住
它被创建在哪个context里。当它被创建时,self表示调用to_proc的symbol。
liusong1111
2008-05-09
回复
居然还没锁贴,再补充一下。
Rails给Hash类做了扩展,加了一个实例方法with_indifferent_access,所做的就是我前面贴的期望自己封装的Hash#to_indiff要做的。源码是:
这样用:
另外,为什么HashWithIndifferentAccess在内部将输入的String或Symbol的key统一当成String存储呢?为什么不统一用Symbol?后者性能不更好吗?
原因在于,ruby1.8中Symbol不会被垃圾收集(1.9解决了),而这个类经常用于封装用户输入数据,比如params返回的就是它的一个实例。恶意用户通过发送大量key值每次各不相同query string,就可能使服务器端内存占用居高不下甚至耗尽。
Rails给Hash类做了扩展,加了一个实例方法with_indifferent_access,所做的就是我前面贴的期望自己封装的Hash#to_indiff要做的。源码是:
def with_indifferent_access
hash = HashWithIndifferentAccess.new(self)
hash.default = self.default
hash
end
这样用:
{:a => 1,'b' => 2}.with_indifferent_access #注意用它的返回值,其本身没有修改
另外,为什么HashWithIndifferentAccess在内部将输入的String或Symbol的key统一当成String存储呢?为什么不统一用Symbol?后者性能不更好吗?
原因在于,ruby1.8中Symbol不会被垃圾收集(1.9解决了),而这个类经常用于封装用户输入数据,比如params返回的就是它的一个实例。恶意用户通过发送大量key值每次各不相同query string,就可能使服务器端内存占用居高不下甚至耗尽。
liusong1111
2007-11-04
回复
从实用角度看,在一些场景下Symbol好过String只有两点:节省内存,表达简洁。
而把两者强制拆开,既有额外好处也带来不便。
好处是充分利用两者的不同进行差异处理,如上面举的异常处理/when的用法。
但不便之处也不少,因为很多情况下需要无区别化对待两者,rails中就大量采用了这种风格,使用者无须担心
attr_accessor :abc 和 attr_accessor 'abc'有什么区别,layout 'main'和layout :main有什么区别(当然,跟layout nil和layout false还是有区别的,一点机智和诡异)。
当用户传过的东东作为Hash的key时,两者的不同让人烦死。
比如,我在每个model里声明一些字段的默认值:
在controller里
请注意controller里的merge,假定页面没有传入params[:person][:age],则新建的person的age取默认值30.
那么,当页面传了age参数为40呢?按理merge后age应该为40,事实却不总正确。因为params里的key都是以String存储的,而我的DEFAULTS的key是Symbol,则merge后的结果是
最终save!以哪个为准,天知道~
rails实现了名为HashWithIndifferentAccess的类(继承自Hash),就是为了解决这一问题。无论开发者传过来的是String还是Symbol(作为key),它都会把预先统一转化成String进行存取,以实现无差别化对待。细心的人可能已察觉,既然params里的内容都是以String作为Key,为啥还可以params[:person]这样访问,奥秘就是用了这个类。它的源码在gems/activesupport-x.x.x/lib/active_support/core_ext/hash/indifferent_access.rb
所以,我上面的model要改成:
即使有这个类,也让人呕吐不止。曾想对它简单封装,提供Hash#to_indiff,仍不满意,不过,注意这个差别并有意避免仍是当前能做到的。
ruby1.9已经把Symbol作为String的子类实现了,并去除了Symbol#to_int,虽然没有百分之百确定出现在ruby2.0里,还好离解放不远,这样从语言角度两者的边界就更清晰了,"Goodbye, HashWithIndifferentAccess."
:symbol.is_a? String
http://redhanded.hobix.com/inspect/SymbolIs_aString.html
让人绝倒的理由之一:
Smalltalk’s symbols are a subclass of string. (He adds that this is his most motivating reason to do it.)
而把两者强制拆开,既有额外好处也带来不便。
好处是充分利用两者的不同进行差异处理,如上面举的异常处理/when的用法。
但不便之处也不少,因为很多情况下需要无区别化对待两者,rails中就大量采用了这种风格,使用者无须担心
attr_accessor :abc 和 attr_accessor 'abc'有什么区别,layout 'main'和layout :main有什么区别(当然,跟layout nil和layout false还是有区别的,一点机智和诡异)。
当用户传过的东东作为Hash的key时,两者的不同让人烦死。
比如,我在每个model里声明一些字段的默认值:
class Person < ActiveRecord::Base
DEFAULTS = {
:age => 30
}
end
在controller里
class PersonController < ActiveController::Base
def create
person = Person.new(Person::DEFAULTS.merge(params[:person]))
person.save!
end
end
请注意controller里的merge,假定页面没有传入params[:person][:age],则新建的person的age取默认值30.
那么,当页面传了age参数为40呢?按理merge后age应该为40,事实却不总正确。因为params里的key都是以String存储的,而我的DEFAULTS的key是Symbol,则merge后的结果是
{
:age => 30,
"age" => 40
}
最终save!以哪个为准,天知道~
rails实现了名为HashWithIndifferentAccess的类(继承自Hash),就是为了解决这一问题。无论开发者传过来的是String还是Symbol(作为key),它都会把预先统一转化成String进行存取,以实现无差别化对待。细心的人可能已察觉,既然params里的内容都是以String作为Key,为啥还可以params[:person]这样访问,奥秘就是用了这个类。它的源码在gems/activesupport-x.x.x/lib/active_support/core_ext/hash/indifferent_access.rb
所以,我上面的model要改成:
class Person < ActiveRecord::Base
DEFAULTS = HashWithIndifferentAccess.new({
:age => 30
})
end
即使有这个类,也让人呕吐不止。曾想对它简单封装,提供Hash#to_indiff,仍不满意,不过,注意这个差别并有意避免仍是当前能做到的。
ruby1.9已经把Symbol作为String的子类实现了,并去除了Symbol#to_int,虽然没有百分之百确定出现在ruby2.0里,还好离解放不远,这样从语言角度两者的边界就更清晰了,"Goodbye, HashWithIndifferentAccess."
:symbol.is_a? String
http://redhanded.hobix.com/inspect/SymbolIs_aString.html
让人绝倒的理由之一:
引用
Smalltalk’s symbols are a subclass of string. (He adds that this is his most motivating reason to do it.)
字符串和symbol可以使用to_str和to_sym方法自由互换
我用irb的时候出显的错误:
我用irb的时候出显的错误:
irb(main):007:0> a == b.to_str NoMethodError: underfind method 'to_str' for :foobar:Symbol form(irb):7
发表评论
该博客是同时发布到论坛的,无法评论在论坛已被锁定的帖子
- 浏览: 721822 次
- 性别:

- 来自: BJ

- 详细资料
搜索本博客
我的相册
screenshot
共 1 张
共 1 张
最近加入圈子
最新评论
-
PHP、CakePHP哪凉快哪呆 ...
这孩子被java毒害太深。。。跳出java,你会发现外面的世界真的很大。
-- by woodless -
学习svn命令
只会用 apt-get 不是好孩子。
-- by smartly -
Rails宝典之第二十八式: ...
当显示条目不为4的倍数时,会发生错误,分组里有nil,nil调用.name显示会 ...
-- by zllicho -
翻译www.djangobook.com之 ...
一个问题,我用的python 2.5.2,Django 1.0正式版 按照这些步 ...
-- by jemmywang -
Erlang内存管理和运行模式 ...
你是德瑞克?
-- by stoneyzjw






评论排行榜