Ruby的隐藏特性
继续“隐藏的特性…”meme,让我们来分享Ruby编程语言鲜为人知但有用的特性 尽量用核心Ruby来限制这种讨论,不要使用RubyonRails之类的东西 另见:Ruby的隐藏特性,ruby,hidden-features,Ruby,Hidden Features,继续“隐藏的特性…”meme,让我们来分享Ruby编程语言鲜为人知但有用的特性 尽量用核心Ruby来限制这种讨论,不要使用RubyonRails之类的东西 另见: (请,每个答案只有一个隐藏功能。) 谢谢我发现使用define_method命令动态生成方法非常有趣,但并不广为人知。例如: ((0..9).each do |n| define_method "press_#{n}" do @number = @number.to_i * 10 + n en
谢谢我发现使用define_method命令动态生成方法非常有趣,但并不广为人知。例如:
((0..9).each do |n|
define_method "press_#{n}" do
@number = @number.to_i * 10 + n
end
end
上述代码使用“define_method”命令动态创建方法“press1”到“press9”。而不是键入所有10个基本上包含相同代码的方法,而是使用define method命令根据需要动态生成这些方法。send()方法是一种通用方法,可用于Ruby中的任何类或对象。如果未重写,send()将接受一个字符串并调用传递该字符串的方法的名称。例如,如果用户单击“Clr”按钮,“按下清除”字符串将发送到send()方法,并调用“按下清除”方法。send()方法允许以一种有趣的动态方式在Ruby中调用函数
%w(7 8 9 / 4 5 6 * 1 2 3 - 0 Clr = +).each do |btn|
button btn, :width => 46, :height => 46 do
method = case btn
when /[0-9]/: 'press_'+btn
when 'Clr': 'press_clear'
when '=': 'press_equals'
when '+': 'press_add'
when '-': 'press_sub'
when '*': 'press_times'
when '/': 'press_div'
end
number.send(method)
number_field.replace strong(number)
end
end
我在下载Ruby 1.9源代码并发布制作高尔夫游戏中详细介绍了此功能,然后您可以执行以下操作:
make golf
./goruby -e 'h'
# => Hello, world!
./goruby -e 'p St'
# => StandardError
./goruby -e 'p 1.tf'
# => 1.0
./goruby19 -e 'p Fil.exp(".")'
"/home/manveru/pkgbuilds/ruby-svn/src/trunk"
[*items].each do |item|
# ...
end
阅读高尔夫前奏曲.c了解隐藏起来的更简洁的东西。使用任何对
==(obj)
有反应的东西进行案例比较:
case foo
when /baz/
do_something_with_the_string_matching_baz
when 12..15
do_something_with_the_integer_between_12_and_15
when lambda { |x| x % 5 == 0 }
# only works in Ruby 1.9 or if you alias Proc#call as Proc#===
do_something_with_the_integer_that_is_a_multiple_of_5
when Bar
do_something_with_the_instance_of_Bar
when some_object
do_something_with_the_thing_that_matches_some_object
end
Module
(因此Class
)、Regexp
、Date
,以及许多其他类定义了一个实例方法:==(其他),并且都可以使用
感谢您提醒我们,Ruby 1.9中的Proc#call
别名为Proc#=
。Peter Cooper有很多Ruby技巧。也许我最喜欢的是允许列举单个项目和集合。(也就是说,将非集合对象视为仅包含该对象的集合。)它如下所示:
make golf
./goruby -e 'h'
# => Hello, world!
./goruby -e 'p St'
# => StandardError
./goruby -e 'p 1.tf'
# => 1.0
./goruby19 -e 'p Fil.exp(".")'
"/home/manveru/pkgbuilds/ruby-svn/src/trunk"
[*items].each do |item|
# ...
end
你在Rubyland中看到的很多魔法都与元编程有关,元编程就是简单地编写代码,为你编写代码。Ruby的
attr\u访问器
,attr\u阅读器
,以及attr\u编写器
都是简单的元编程,它们按照标准模式在一行中创建两个方法。Rails使用它们的关系管理方法进行了大量元编程,比如有一个和属于
但是,使用class\u eval
动态执行编写的代码,创建自己的元编程技巧非常简单
以下示例允许包装器对象将某些方法转发给内部对象:
class Wrapper
attr_accessor :internal
def self.forwards(*methods)
methods.each do |method|
define_method method do |*arguments, &block|
internal.send method, *arguments, &block
end
end
end
forwards :to_i, :length, :split
end
w = Wrapper.new
w.internal = "12 13 14"
w.to_i # => 12
w.length # => 8
w.split('1') # => ["", "2 ", "3 ", "4"]
方法Wrapper.forwards
获取方法名称的符号,并将它们存储在methods
数组中。然后,对于给定的每个方法,我们使用define_method
创建一个新方法,其任务是发送消息,包括所有参数和块
元编程问题的一个很好的资源是。来自Ruby 1.9的Proc#===是Proc#call的别名,这意味着Proc对象可以在如下case语句中使用:
def multiple_of(factor)
Proc.new{|product| product.modulo(factor).zero?}
end
case number
when multiple_of(3)
puts "Multiple of 3"
when multiple_of(7)
puts "Multiple of 7"
end
愚弄某个类或模块,告诉它需要一些它实际上不需要的东西:
$" << "something"
Rails提供的符号#to#proc函数非常酷
而不是
Employee.collect { |emp| emp.name }
你可以写:
Employee.collect(&:name)
警告:此物品被评为2008年最可怕的黑客行为,请小心使用。事实上,避免它就像瘟疫一样,但它肯定是隐藏的红宝石
超级玩家为Ruby添加了新的操作符
你是否想要一个超级秘密的握手操作符来完成你代码中的一些独特操作?喜欢打高尔夫吗?试试像这样的操作人员
-~+~-
或
1.9 Proc功能中另一个有趣的新增功能是Proc#curry,它允许您将接受n个参数的Proc转换为接受n-1的Proc。这里它与上面提到的Proc#==技巧结合在一起:
it_is_day_of_week = lambda{ |day_of_week, date| date.wday == day_of_week }
it_is_saturday = it_is_day_of_week.curry[6]
it_is_sunday = it_is_day_of_week.curry[0]
case Time.now
when it_is_saturday
puts "Saturday!"
when it_is_sunday
puts "Sunday!"
else
puts "Not the weekend"
end
我不知道这有多隐蔽,但我发现它在需要对一维数组进行散列时很有用:
fruit = ["apple","red","banana","yellow"]
=> ["apple", "red", "banana", "yellow"]
Hash[*fruit]
=> {"apple"=>"red", "banana"=>"yellow"}
我喜欢的一个技巧是在数组以外的对象上使用splat(*
)扩展器。下面是一个正则表达式匹配的示例:
match, text, number = *"Something 981".match(/([A-z]*) ([0-9]*)/)
其他例子包括:
a, b, c = *('A'..'Z')
Job = Struct.new(:name, :occupation)
tom = Job.new("Tom", "Developer")
name, occupation = *tom
另一个小功能-将Fixnum
转换为最多36个基数:
>> 1234567890.to_s(2)
=> "1001001100101100000001011010010"
>> 1234567890.to_s(8)
=> "11145401322"
>> 1234567890.to_s(16)
=> "499602d2"
>> 1234567890.to_s(24)
=> "6b1230i"
>> 1234567890.to_s(36)
=> "kf12oi"
正如Huw Walters所评论的,以另一种方式转换也同样简单:
>> "kf12oi".to_i(36)
=> 1234567890
最后一个-在ruby中,您可以使用任何想要分隔字符串的字符。以下面的代码为例:
message = "My message"
contrived_example = "<div id=\"contrived\">#{message}</div>"
模函数
声明为Module_函数的模块方法将在包含该模块的类中创建自身作为private实例方法的副本:
module M
def not!
'not!'
end
module_function :not!
end
class C
include M
def fun
not!
end
end
M.not! # => 'not!
C.new.fun # => 'not!'
C.new.not! # => NoMethodError: private method `not!' called for #<C:0x1261a00>
Class.new()
在运行时创建一个新类。参数可以是派生自的类,而块是类的主体。您可能还希望查看const\u set/const\u get/const\u defined?
以正确注册新类,以便inspect
打印出名称而不是数字
这不是你每天都需要的东西,但当你这样做的时候非常方便。ruby最酷的一点是,你可以在其他语言不喜欢的地方调用方法并运行代码,比如在方法或类定义中
例如,要创建在运行时之前具有未知超类(即随机)的类,可以执行以下操作:
class RandomSubclass < [Array, Hash, String, Fixnum, Float, TrueClass].sample
end
RandomSubclass.superclass # could output one of 6 different classes.
当然,第一个示例的问题是,它是在定义时而不是在调用时进行计算的。因此,一旦选择了一个超类,它将在程序的其余部分保留该超类
但是,在第二个示例中,每次调用do\u something\u at
,变量at
将是调用该方法的时间(非常接近它)Short inj
module M
module_function
def not!
'not!'
end
def yea!
'yea!'
end
end
class C
include M
def fun
not! + ' ' + yea!
end
end
M.not! # => 'not!'
M.yea! # => 'yea!'
C.new.fun # => 'not! yea!'
class RandomSubclass < [Array, Hash, String, Fixnum, Float, TrueClass].sample
end
RandomSubclass.superclass # could output one of 6 different classes.
def do_something_at(something, at = Time.now)
# ...
end
(1..10).inject(:+)
=> 55
parties = Hash.new {|hash, key| hash[key] = [] }
parties["Summer party"]
# => []
parties["Summer party"] << "Joe"
parties["Other party"] << "Jane"
x = [*0..5]
a = [:x, :y, :z]
b = [123, 456, 789]
Hash[a.zip(b)]
# => { :x => 123, :y => 456, :z => 789 }
a.zip(b) # => [[:x, 123], [:y, 456], [:z, 789]]
Hash[*a.zip(b).flatten] # unnecessary!
Inf = 1.0 / 0
(1..Inf).take(5) #=> [1, 2, 3, 4, 5]
string &&= string + "suffix"
if string
string = string + "suffix"
end
# Print each line with its number:
ruby -ne 'print($., ": ", $_)' < /etc/irbrc
# Print each line reversed:
ruby -lne 'puts $_.reverse' < /etc/irbrc
# Print the second column from an input CSV (dumb - no balanced quote support etc):
ruby -F, -ane 'puts $F[1]' < /etc/irbrc
# Print lines that contain "eat"
ruby -ne 'puts $_ if /eat/i' < /etc/irbrc
# Same as above:
ruby -pe 'next unless /eat/i' < /etc/irbrc
# Pass-through (like cat, but with possible line-end munging):
ruby -p -e '' < /etc/irbrc
# Uppercase all input:
ruby -p -e '$_.upcase!' < /etc/irbrc
# Same as above, but actually write to the input file, and make a backup first with extension .bak - Notice that inplace edit REQUIRES input files, not an input STDIN:
ruby -i.bak -p -e '$_.upcase!' /etc/irbrc
def cnh # silly name "create nested hash"
Hash.new {|h,k| h[k] = Hash.new(&h.default_proc)}
end
my_hash = cnh
my_hash[1][2][3] = 4
my_hash # => { 1 => { 2 => { 3 =>4 } } }
1.upto(100) do |i|
puts i if (i == 3)..(i == 15)
end
(a, b), c, d = [ [:a, :b ], :c, [:d1, :d2] ]
a #=> :a
b #=> :b
c #=> :c
d #=> [:d1, :d2]
def hello(*)
super
puts "hello!"
end
private unless Rails.env == 'test'
# e.g. a bundle of methods you want to test directly