Ruby的隐藏function
继续“隐藏的…特性”,让我们分享一下Ruby编程语言鲜为人知的但有用的特性。
尝试限制与核心Ruby的讨论,没有任何Ruby on Rails的东西。
也可以看看:
- C#隐藏的function
- Java的隐藏function
- JavaScript的隐藏function
- Ruby on Rails的隐藏function
- Python的隐藏function
(请每个答案只有一个隐藏的function。)
谢谢
从Ruby 1.9 Proc#===是Proc#调用的别名,这意味着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
Peter Cooper有一个很好的Ruby技巧列表 。 也许我最喜欢的是允许枚举单个项目和集合。 (也就是说,将一个非集合对象视为一个包含该对象的集合。)看起来像这样:
[*items].each do |item| # ... end
不知道这是如何隐藏的,但是当我需要从一维数组中创build一个Hash的时候,我发现它非常有用:
fruit = ["apple","red","banana","yellow"] => ["apple", "red", "banana", "yellow"] Hash[*fruit] => {"apple"=>"red", "banana"=>"yellow"}
我喜欢的一个技巧是在数组以外的对象上使用splat( *
)扩展器。 下面是一个正则expression式匹配的例子:
match, text, number = *"Something 981".match(/([Az]*) ([0-9]*)/)
其他例子包括:
a, b, c = *('A'..'Z') Job = Struct.new(:name, :occupation) tom = Job.new("Tom", "Developer") name, occupation = *tom
哇,没有人提到触发器操作符:
1.upto(100) do |i| puts i if (i == 3)..(i == 15) end
关于ruby的一个很酷的事情是,你可以调用方法和运行代码在其他语言会皱眉的地方,如在方法或类定义。
例如,要创build一个具有未知超类的类,直到运行时,即随机,您可以执行以下操作:
class RandomSubclass < [Array, Hash, String, Fixnum, Float, TrueClass].sample end RandomSubclass.superclass # could output one of 6 different classes.
这使用了1.9 Array#sample
方法(仅在1.8.7中,请参阅Array#choice
),这个例子很有意思,但是您可以在这里看到这个function。
另一个很酷的例子是能够将不固定的默认参数值(像其他语言经常需要的那样):
def do_something_at(something, at = Time.now) # ... end
第一个例子的问题当然是在定义时间评估,而不是调用时间。 所以,一旦select了一个超类,它仍然是该程序其余部分的超类。
但是,在第二个例子中,每次调用do_something_at
, at
variables就是调用方法的时间(非常非常接近)
另一个小function – 将一个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
哈希与默认值! 在这种情况下的数组。
parties = Hash.new {|hash, key| hash[key] = [] } parties["Summer party"] # => [] parties["Summer party"] << "Joe" parties["Other party"] << "Jane"
在元编程中非常有用。
下载Ruby 1.9的源码,并发出make golf
,那么你可以做这样的事情:
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"
阅读golf_prelude.c
以获得更多隐藏的东西。
1.9 Procfunction的另一个有趣的添加是Proc#curry,它允许您将一个Proc接受n个参数为一个接受n-1。 在这里它与上面提到的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
布尔运算符对非布尔值。
&&
和||
两者都返回最后一个expression式的值。
这就是为什么如果variables是未定义的, ||=
将用右侧的返回expression式的值更新variables。 这没有明确logging,但常识。
然而&&=
并不是那么广为人知。
string &&= string + "suffix"
相当于
if string string = string + "suffix" end
如果variables未定义,那么破坏性操作非常方便,不应该继续。
Rails提供的Symbol#to_proc函数非常酷。
代替
Employee.collect { |emp| emp.name }
你可以写:
Employee.collect(&:name)
最后一个 – 在ruby中,你可以使用任何你想要分隔string的字符。 采取以下代码:
message = "My message" contrived_example = "<div id=\"contrived\">#{message}</div>"
如果你不想逃避string中的双引号,你可以简单地使用不同的分隔符:
contrived_example = %{<div id="contrived-example">#{message}</div>} contrived_example = %[<div id="contrived-example">#{message}</div>]
除了避免使用分隔符外,还可以使用这些分隔符来实现更好的多行string:
sql = %{ SELECT strings FROM complicated_table WHERE complicated_condition = '1' }
我发现使用define_method命令来dynamic生成方法是相当有趣的,而不是众所周知的。 例如:
((0..9).each do |n| define_method "press_#{n}" do @number = @number.to_i * 10 + n end end
上面的代码使用'define_method'命令通过“press9”dynamic地创build方法“press1”。 而不是input所有10个本质上包含相同代码的方法,define方法命令用于根据需要即时生成这些方法。
使用Range对象作为一个无限的懒惰列表:
Inf = 1.0 / 0 (1..Inf).take(5) #=> [1, 2, 3, 4, 5]
更多信息: http : //banisterfiend.wordpress.com/2009/10/02/wtf-infinite-ranges-in-ruby/
module_function
声明为module_function的模块方法将在包含Module的类中创build自己的副本作为私有实例方法:
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>
如果使用不带任何参数的module_function ,那么在module_function语句之后的任何模块方法都会自动变成module_functions。
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!'
短的注射,像这样:
范围总和:
(1..10).inject(:+) => 55
警告:这个项目被评为2008年度最可怕的黑客 ,所以请小心使用。 实际上,避免它像瘟疫一样,但它绝对是隐藏的ruby。
Superators添加新操作符到Ruby
你想要一个超级秘密的握手操作符在你的代码中进行一些独特的操作吗? 喜欢打码高尔夫? 尝试运算符,如〜〜〜〜或<—在示例中使用最后一个用于反转项目的顺序。
我和Superators Project没有任何关系。
我迟到了,但是:
你可以很容易地把两个等长的数组变成一个散列,其中一个数组提供键和另一个数组:
a = [:x, :y, :z] b = [123, 456, 789] Hash[a.zip(b)] # => { :x => 123, :y => 456, :z => 789 }
(这样做是因为Array#zip从两个数组中拉伸了数值:
a.zip(b) # => [[:x, 123], [:y, 456], [:z, 789]]
而哈希[]可以采取这样一个数组。 我也看到有人也这样做:
Hash[*a.zip(b).flatten] # unnecessary!
哪一个产生相同的结果,但是图示和展平是完全不必要的 – 也许它们不是过去的?)
Ruby中的自动生成哈希
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 } } }
这可以是该死的方便。
解构一个数组
(a, b), c, d = [ [:a, :b ], :c, [:d1, :d2] ]
哪里:
a #=> :a b #=> :b c #=> :c d #=> [:d1, :d2]
使用这种技术,我们可以使用简单的赋值来从任意深度的嵌套数组中获得我们想要的确切值。
Class.new()
在运行时创build一个新类。 参数可以是派生的类,块是类体。 你可能也想看看const_set/const_get/const_defined?
让你的新class级正确注册,以便inspect
打印一个名字,而不是一个数字。
不是你每天都需要的东西,但是当你做的时候非常方便。
创build一个连续数字的数组:
x = [*0..5]
将x设置为[0,1,2,3,4,5]
你在Rubyland看到的许多魔法都与元编程有关,它只是编写代码来为你编写代码。 Ruby的attr_accessor
, attr_reader
和attr_writer
都是简单的元编程,因为它们按照标准模式在一行中创build两个方法。 Rails使用像has_one
和belongs_to
这样的关系pipe理方法进行了大量的元编程。
但是使用class_eval
执行dynamic编写的代码来创build自己的元编程技巧非常简单。
以下示例允许包装器对象将某些方法转发给内部对象:
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
来创build一个新的方法,其工作是发送消息,包括所有的参数和块。
元编程问题的一个很好的资源是为什么幸运僵硬的“看清晰元程序” 。
使用任何对===(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
和许多其他类定义了一个实例方法:===(other),并且都可以使用。
感谢Farrel提醒Proc#===
在Ruby 1.9中的别名为Proc#===
。
“ruby”二进制文件(至less是MRI)支持许多使perl单行程序相当受欢迎的开关。
重要的:
- -n用“gets”设置一个外部循环 – 这对于给定的文件名或STDIN来说是神奇的,将每个读取行设置为$ _
- -p类似于-n,但在每次循环迭代结束时都会自动
put
- -a自动调用每个input行上的.split,存储在$ F中
- -i就地编辑input文件
- -linput时自动调用.chomp
- -e执行一段代码
- -c检查源代码
- -w带有警告
一些例子:
# 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
随意谷歌“ruby单线程”和“Perl单线程”为吨更多的可用和实际的例子。 它基本上允许你使用ruby作为awk和sed的一个相当强大的替代品。
send()方法是一个通用的方法,可以在Ruby中的任何Class或Object上使用。 如果不重写,send()接受一个string,并调用传递string的方法的名字。 例如,如果用户点击“Clr”button,“press_clear”string将被发送到send()方法,并且将调用“press_clear”方法。 send()方法允许以一种有趣且dynamic的方式在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
在Blogging Shoes:Simple-Calc应用程序中我会详细讨论这个function
愚弄一些类或模块告诉它需要的东西,它确实没有要求:
$" << "something"
例如,当需要A的时候,需要B,但是在我们的代码中不需要B(而A不会通过我们的代码使用它):
例如,Backgroundrb的bdrb_test_helper requires
'test/spec'
,但是你完全不使用它,所以在你的代码中:
$" << "test/spec" require File.join(File.dirname(__FILE__) + "/../bdrb_test_helper")
Fixnum#to_s(base)
在某些情况下可能非常有用。 一种这样的情况是通过将随机数转换为使用36的基数来产生随机(伪)唯一令牌。
令牌长度8:
rand(36**8).to_s(36) => "fmhpjfao" rand(36**8).to_s(36) => "gcer9ecu" rand(36**8).to_s(36) => "krpm0h9r"
令牌长度6:
rand(36**6).to_s(36) => "bvhl8d" rand(36**6).to_s(36) => "lb7tis" rand(36**6).to_s(36) => "ibwgeh"
定义一个方法,接受任意数量的参数,并放弃所有的参数
def hello(*) super puts "hello!" end
上面的hello
方法只需要在屏幕上puts
"hello"
并调用super
,但是由于超类hello
定义了参数,所以它不需要使用参数本身,给他们一个名字