Ruby的隐藏功能
继续"……的隐藏功能"模因,让我们分享Ruby编程语言鲜为人知但有用的功能。
尝试将讨论限制在核心Ruby之上,而不要使用任何Ruby on Rails的东西。
也可以看看:
- C#的隐藏功能
- Java的隐藏功能
- JavaScript的隐藏功能
- Ruby on Rails的隐藏功能
- Python的隐藏功能
(请每个答案只有一个隐藏功能。)
谢谢
解决方案
回答
我发现使用define_method命令动态生成方法非常有趣,而且并不为人所知。例如:
((0..9).each do |n| define_method "press_#{n}" do @number = @number.to_i * 10 + n end end
上面的代码使用" define_method"命令动态创建方法" press1"到" press9"。而不是键入essentailly包含相同代码的所有10个方法,而是使用define method命令根据需要即时生成这些方法。
回答
send()方法是一种通用方法,可以在Ruby中的任何类或者对象上使用。如果未重写,则send()接受一个字符串并调用传递其字符串的方法的名称。例如,如果用户单击Clr按钮,则将press_clear字符串发送到send()方法,并调用press_clear方法。 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
我在Blogging Shoes:Simple-Calc应用程序中讨论了更多有关此功能的信息
回答
下载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
,以获取更多隐藏的整洁东西。
回答
使用任何可以响应===(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
模块(以及类),正则表达式,日期和许多其他类定义了一个实例方法:===(其他),并且都可以使用。
感谢Farrel提醒我们在Ruby 1.9中将Proc#call别名为Proc#===。
回答
彼得·库珀(Peter Cooper)列出了很多不错的Ruby技巧。也许我最喜欢他的是允许枚举单个项目和集合。 (也就是说,将非集合对象视为仅包含该对象的集合。)如下所示:
[*items].each do |item| # ... end
回答
如何基于ARGV [0]打开文件?
readfile.rb:
$<.each_line{|l| puts l} ruby readfile.rb testfile.txt
这是编写一次性脚本的绝佳捷径。大多数人不知道的是一堆乱七八糟的预定义变量。明智地使用它们(请阅读:不要乱扔我们打算使用它们维护的代码库,否则可能会造成混乱)。
回答
我们在Rubyland中看到的许多魔术都与元编程有关,元编程只是编写可为我们编写代码的代码。 Ruby的attr_accessor,attr_reader和attr_writer都是简单的元编程,它们按照标准模式在一行中创建两个方法。 Rails使用诸如has_one和belongs_to之类的关系管理方法来进行大量元编程。
但是使用class_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"使用符号作为方法名称,并将其存储在"方法"数组中。然后,对于每个给定的参数,我们使用define_method
创建一个新方法,该方法的任务是一起发送消息,包括所有参数和块。
关于元编程问题的重要资源是为什么Lucky Stiff的"清晰地看到元编程"。
回答
从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"
例如,这在需要A而又需要B但我们的代码中不需要B的情况下很有用(并且A也不会通过我们的代码使用它):
例如,Backgroundrb的bdrb_test_helper需要
'test / spec'`,但是我们根本不使用它,因此在代码中:
$" << "test/spec" require File.join(File.dirname(__FILE__) + "/../bdrb_test_helper")
回答
Rails提供的Symbol#to_proc函数确实很棒。
代替
Employee.collect { |emp| emp.name }
你可以写:
Employee.collect(&:name)
回答
警告:该产品被评为2008年"最恐怖的黑客工具"第一名,请谨慎使用。实际上,请像瘟疫一样避免使用它,但是最确定的是隐藏的Ruby。
超级运算符将新运算符添加到Ruby
是否曾经想要一个超级秘密的握手运算符来在代码中进行某些独特的操作?喜欢打代码高尔夫吗?尝试像
-〜+〜
或者
<-
在示例中,最后一个用于反转项目的顺序。
除了欣赏它之外,我与Superators项目无关。
回答
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
回答
红宝石的最后一个,我们可以使用要分隔字符串的任何字符。采取以下代码:
message = "My message" contrived_example = "<div id=\"contrived\">#{message}</div>"
如果不想转义字符串中的双引号,则可以简单地使用其他定界符:
contrived_example = %{<div id="contrived-example">#{message}</div>} contrived_example = %[<div id="contrived-example">#{message}</div>]
除了避免转义定界符外,我们还可以将这些定界符用于更好的多行字符串:
sql = %{ SELECT strings FROM complicated_table WHERE complicated_condition = '1' }
回答
声明为module_function的模块方法将在包含Module的类中创建自己的副本作为私有实例方法:
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!'
回答
Class.new()
在运行时创建一个新类。参数可以是要派生的类,而块是类主体。我们可能还希望查看const_set / const_get / const_defined?
来正确注册新类,以便inspect
输出名称而不是数字。
并不是我们每天都需要的东西,但是当我们这样做时非常方便。
回答
Ruby有一个call / cc机制,允许人们自由地在堆栈中上下移动。
下面是一个简单的示例。当然,这不是将红宝石上的序列相乘的方式,但是它演示了如何使用call / cc到达堆栈以使算法短路。在这种情况下,我们递归地将数字列表相乘,直到看到每个数字或者看到零(这两种情况我们都知道答案)。在零情况下,我们可以在列表中任意深入并终止。
#!/usr/bin/env ruby def rprod(k, rv, current, *nums) puts "#{rv} * #{current}" k.call(0) if current == 0 || rv == 0 nums.empty? ? (rv * current) : rprod(k, rv * current, *nums) end def prod(first, *rest) callcc { |k| rprod(k, first, *rest) } end puts "Seq 1: #{prod(1, 2, 3, 4, 5, 6)}" puts "" puts "Seq 2: #{prod(1, 2, 0, 3, 4, 5, 6)}"
我们可以在此处查看输出:
http://codepad.org/Oh8ddh9e
有关具有沿堆栈的另一个方向连续移动的连续性的更复杂示例,请阅读Generator的源代码。
回答
关于ruby的一件很酷的事情是,我们可以调用方法并在其他语言不会使用的地方运行代码,例如在方法或者类定义中。
例如,要创建一个在运行时之前具有未知超类的类,即是随机的,我们可以执行以下操作:
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"),该示例非常人为,但我们可以在此处看到强大的功能。
另一个很酷的例子是能够放置非固定的默认参数值(就像其他语言经常需要的那样):
def do_something_at(something, at = Time.now) # ... end
当然,第一个示例的问题在于它是在定义时而不是调用时进行评估的。因此,一旦选择了超类,它将在该程序的其余部分中保留该超类。
但是,在第二个示例中,每次调用do_something_at
时,at
变量将是方法被调用的时间(嗯,非常接近它)
回答
class A private def my_private_method puts 'private method called' end end a = A.new a.my_private_method # Raises exception saying private method was called a.send :my_private_method # Calls my_private_method and prints private method called'
回答
短注入,例如:
范围总和:
(1..10).inject(:+) => 55
回答
具有默认值的哈希!在这种情况下为数组。
parties = Hash.new {|hash, key| hash[key] = [] } parties["Summer party"] # => [] parties["Summer party"] << "Joe" parties["Other party"] << "Jane"
在元编程中非常有用。
回答
我发现这在某些脚本中很有用。这样就可以直接使用环境变量,例如在shell脚本和Makefile中。环境变量用作未定义Ruby常量的备用。
>> class <<Object >> alias :old_const_missing :const_missing >> def const_missing(sym) >> ENV[sym.to_s] || old_const_missing(sym) >> end >> end => nil >> puts SHELL /bin/zsh => nil >> TERM == 'xterm' => true
回答
创建一个连续数字数组:
x = [*0..5]
将x设置为[0、1、2、3、4、5]
回答
我参加聚会迟到,但是:
我们可以轻松地获取两个等长数组,并将它们变成一个哈希,其中一个数组提供键,另一个提供键值:
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 []可以采用这样的数组。我也看到人们也这样做:
Hash[*a.zip(b).flatten] # unnecessary!
产生相同的结果,但是splat和flatten完全不必要-也许它们不是过去了吗?)
回答
将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/
回答
非布尔值的布尔运算符。
&&
和||
两者都返回最后一个求值表达式的值。
这就是为什么如果变量未定义,|| =
将使用右侧返回的表达式更新变量的原因。这没有明确记录,但是是常识。
但是,&& =
并不是很广为人知。
string &&= string + "suffix"
相当于
if string string = string + "suffix" end
对于破坏性操作非常方便,如果变量未定义,则破坏性操作不应继续进行。
回答
James A. Rosen的技巧很酷([* items] .each),但是我发现它破坏了哈希值:
irb(main):001:0> h = {:name => "Bob"} => {:name=>"Bob"} irb(main):002:0> [*h] => [[:name, "Bob"]]
当我接受要处理的事情列表但宽容并允许调用者提供以下内容时,我更喜欢这种处理情况的方式:
irb(main):003:0> h = {:name => "Bob"} => {:name=>"Bob"} irb(main):004:0> [h].flatten => [{:name=>"Bob"}]
可以很好地将其与方法签名结合使用:
def process(*entries) [entries].flatten.each do |e| # do something with e end end
回答
"红宝石"二进制文件(至少是MRI的)支持许多开关,这些开关使perl一线式很受欢迎。
重要的是:
- -n仅用" gets"设置一个外部循环-可以神奇地使用给定的文件名或者STDIN,在$ _中设置每个读取行
- -p与-n相似,但是在每次循环迭代结束时自动放入
- -a在每个输入行上自动调用.split,存储在$ F中
- -i就地编辑输入文件
- -l在输入时自动调用.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
随意使用Google的" ruby one-liners"和" perl one-liners",以获取更多有用和实用的示例。从本质上讲,它允许我们将ruby用作awk和sed的强大替代品。
回答
在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 } } }
这简直太方便了。
回答
在某些情况下,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"
回答
我只是喜欢这样的内联关键字抢救:
编辑示例:
@user #=> nil (but I did't know) @user.name rescue "Unknown" link_to( d.user.name, url_user( d.user.id, d.user.name)) rescue 'Account removed'
这样可以避免破坏我的应用程序,并且比Rails .try()发布的功能更好。
回答
@user #=> nil (but I did't know) @user.name rescue "Unknown"
回答
我是以下人群的粉丝:
%w{An Array of strings} #=> ["An", "Array", "of", "Strings"]
有用的频率有多有趣。
回答
哇,没人提到过触发器运算符:
1.upto(100) do |i| puts i if (i == 3)..(i == 15) end
回答
每个可枚举对象(数组,哈希等)的each_with_index方法?
myarray = ["la", "li", "lu"] myarray.each_with_index{|v,idx| puts "#{idx} -> #{v}"} #result: #0 -> la #1 -> li #2 -> lu
也许它比其他答案更广为人知,但并不是所有红宝石程序员都那么了解:)
回答
解构数组
(a, b), c, d = [ [:a, :b ], :c, [:d1, :d2] ]
在哪里:
a #=> :a b #=> :b c #=> :c d #=> [:d1, :d2]
使用这种技术,我们可以使用简单的赋值从任何深度的嵌套数组中获取所需的精确值。
回答
定义一个可以接受任意数量参数的方法,只是将其全部丢弃
def hello(*) super puts "hello!" end
上面的hello方法只需要在屏幕上放入" hello"并调用super,但是由于超类hello定义了参数,因此它也必须使用,因为参数本身,不必给它们起一个名字。
回答
调用在继承链中任何位置定义的方法,即使已被覆盖
ActiveSupport的对象有时会伪装成内置对象。
require 'active_support' days = 5.days days.class #=> Fixnum days.is_a?(Fixnum) #=> true Fixnum === days #=> false (huh? what are you really?) Object.instance_method(:class).bind(days).call #=> ActiveSupport::Duration (aha!) ActiveSupport::Duration === days #=> true
当然,以上内容是基于active_support并没有重新定义Object#instance_method的事实,在这种情况下,我们确实要小溪了。再说一次,我们总是可以在加载任何第三方库之前保存Object.instance_method(:class)的返回值。
Object.instance_method(...)返回一个UnboundMethod,我们可以将其绑定到该类的实例。在这种情况下,我们可以将其绑定到Object的任何实例(包括子类)。
如果对象的类包含模块,则还可以使用这些模块中的UnboundMethod。
module Mod def var_add(more); @var+more; end end class Cla include Mod def initialize(var); @var=var; end # override def var_add(more); @var+more+more; end end cla = Cla.new('abcdef') cla.var_add('ghi') #=> "abcdefghighi" Mod.instance_method(:var_add).bind(cla).call('ghi') #=> "abcdefghi"
这甚至适用于覆盖该对象所属类的实例方法的单例方法。
class Foo def mymethod; 'original'; end end foo = Foo.new foo.mymethod #=> 'original' def foo.mymethod; 'singleton'; end foo.mymethod #=> 'singleton' Foo.instance_method(:mymethod).bind(foo).call #=> 'original' # You can also call #instance method on singleton classes: class << foo; self; end.instance_method(:mymethod).bind(foo).call #=> 'singleton'
回答
多个返回值
def getCostAndMpg cost = 30000 # some fancy db calls go here mpg = 30 return cost,mpg end AltimaCost, AltimaMpg = getCostAndMpg puts "AltimaCost = #{AltimaCost}, AltimaMpg = #{AltimaMpg}"
并行分配
i = 0 j = 1 puts "i = #{i}, j=#{j}" i,j = j,i puts "i = #{i}, j=#{j}"
虚拟属性
class Employee < Person def initialize(fname, lname, position) super(fname,lname) @position = position end def to_s super + ", #@position" end attr_writer :position def etype if @position == "CEO" || @position == "CFO" "executive" else "staff" end end end employee = Employee.new("Augustus","Bondi","CFO") employee.position = "CEO" puts employee.etype => executive employee.position = "Engineer" puts employee.etype => staff
method_missing-一个好主意
(在大多数语言中,当找不到方法并且抛出错误并且程序停止时。在ruby中,我们实际上可以捕获到这些错误,并且可以对情况进行一些明智的操作)
class MathWiz def add(a,b) return a+b end def method_missing(name, *args) puts "I don't know the method #{name}" end end mathwiz = MathWiz.new puts mathwiz.add(1,4) puts mathwiz.subtract(4,2)
5 I don't know the method subtract nil