我喜欢 ruby 的一件事是它主要是一种非常易读的语言(这对于自记录代码来说非常有用)
然而,受这个问题的启发:Ruby Code explained 和对 ||=
如何在 ruby 中工作的描述,我在思考我不使用的 ruby 习语,坦率地说,我并没有完全理解它们。
所以我的问题是,类似于引用问题中的示例,我需要了解哪些常见但不明显的 ruby 习语才能成为真正精通的 ruby 程序员?
顺便说一句,从引用的问题
a ||= b
相当于
if a == nil || a == false
a = b
end
(感谢 Ian Terrell 的更正)
编辑:事实证明,这一点并非完全没有争议。正确的展开其实是
(a || (a = (b)))
请参阅以下链接了解原因:
http://DABlog.RubyPAL.Com/2008/3/25/a-short-circuit-edge-case/
http://DABlog.RubyPAL.Com/2008/3/26/short-circuit-post-correction/
http://ProcNew.Com/ruby-short-circuit-edge-case-response.html
感谢 Jörg W Mittag 指出这一点。
foo ||= bar
等价于 foo || foo = bar
obj.foo ||= bar
如果它是 obj.foo = obj.foo || bar
则 obj.foo= 将被调用,即使 obj.foo 是错误的(并且 obj.foo= 可能有副作用)。但这不是它的工作方式
让同一个文件充当库或脚本的神奇 if 子句:
if __FILE__ == $0
# this library may be run as a standalone script
end
打包和解包数组:
# put the first two words in a and b and the rest in arr
a,b,*arr = *%w{a dog was following me, but then he decided to chase bob}
# this holds for method definitions to
def catall(first, *rest)
rest.map { |word| first + word }
end
catall( 'franken', 'stein', 'berry', 'sense' ) #=> [ 'frankenstein', 'frankenberry', 'frankensense' ]
哈希作为方法参数的语法糖
this(:is => :the, :same => :as)
this({:is => :the, :same => :as})
哈希初始化器:
# this
animals = Hash.new { [] }
animals[:dogs] << :Scooby
animals[:dogs] << :Scrappy
animals[:dogs] << :DynoMutt
animals[:squirrels] << :Rocket
animals[:squirrels] << :Secret
animals #=> {}
# is not the same as this
animals = Hash.new { |_animals, type| _animals[type] = [] }
animals[:dogs] << :Scooby
animals[:dogs] << :Scrappy
animals[:dogs] << :DynoMutt
animals[:squirrels] << :Rocket
animals[:squirrels] << :Secret
animals #=> {:squirrels=>[:Rocket, :Secret], :dogs=>[:Scooby, :Scrappy, :DynoMutt]}
元类语法
x = Array.new
y = Array.new
class << x
# this acts like a class definition, but only applies to x
def custom_method
:pow
end
end
x.custom_method #=> :pow
y.custom_method # raises NoMethodError
类实例变量
class Ticket
@remaining = 3
def self.new
if @remaining > 0
@remaining -= 1
super
else
"IOU"
end
end
end
Ticket.new #=> Ticket
Ticket.new #=> Ticket
Ticket.new #=> Ticket
Ticket.new #=> "IOU"
块、过程和 lambda。生活和呼吸它们。
# know how to pack them into an object
block = lambda { |e| puts e }
# unpack them for a method
%w{ and then what? }.each(&block)
# create them as needed
%w{ I saw a ghost! }.each { |w| puts w.upcase }
# and from the method side, how to call them
def ok
yield :ok
end
# or pack them into a block to give to someone else
def ok_dokey_ok(&block)
ok(&block)
block[:dokey] # same as block.call(:dokey)
ok(&block)
end
# know where the parentheses go when a method takes arguments and a block.
%w{ a bunch of words }.inject(0) { |size,w| size + 1 } #=> 4
pusher = lambda { |array, word| array.unshift(word) }
%w{ eat more fish }.inject([], &pusher) #=> ['fish', 'more', 'eat' ]
这个 slideshow 在主要的 Ruby 习语上相当完整,如下所示:
交换两个值:x, y = y, x
如果未指定,则采用某些默认值的参数 def somemethod(x, y=nil)
将无关参数批处理到数组中 def 替换(re, str, *rest)
等等...
还有一些成语:
使用 %w
、%r
和 %(
分隔符
%w{ An array of strings %}
%r{ ^http:// }
%{ I don't care if the string has 'single' or "double" strings }
case 语句中的类型比较
def something(x)
case x
when Array
# Do something with array
when String
# Do something with string
else
# You should really teach your objects how to 'quack', don't you?
end
end
...以及在案例陈述中对 ===
方法的整体滥用
case x
when 'something concrete' then ...
when SomeClass then ...
when /matches this/ then ...
when (10...20) then ...
when some_condition >= some_value then ...
else ...
end
对于 Ruby 主义者来说应该看起来很自然,但对于来自其他语言的人来说可能不是这样:使用 each
来支持 for .. in
some_iterable_object.each{|item| ... }
在 Ruby 1.9+、Rails 中,或者通过修补 Symbol#to_proc 方法,this 正在成为一个越来越流行的习惯用法:
strings.map(&:upcase)
条件方法/常量定义
SOME_CONSTANT = "value" unless defined?(SOME_CONSTANT)
查询方法和破坏性(bang)方法
def is_awesome?
# Return some state of the object, usually a boolean
end
def make_awesome!
# Modify the state of the object
end
隐式 splat 参数
[[1, 2], [3, 4], [5, 6]].each{ |first, second| puts "(#{first}, #{second})" }
我喜欢这个:
str = "Something evil this way comes!"
regexp = /(\w[aeiou])/
str[regexp, 1] # <- This
这(大致)相当于:
str_match = str.match(regexp)
str_match[1] unless str_match.nil?
或者至少那是我用来替换这些块的东西。
我建议通读你钦佩和尊重的人的流行和精心设计的插件或宝石的代码。
我遇到的一些例子:
if params[:controller] == 'discussions' or params[:controller] == 'account'
# do something here
end
对应于
if ['account', 'discussions'].include? params[:controller]
# do something here
end
稍后将被重构为
if ALLOWED_CONTROLLERS.include? params[:controller]
# do something here
end
顺便说一句,从引用的问题 a ||= b 等价于 if a == nil a = b end
这是不正确的,并且是新手 Ruby 应用程序中错误的来源。
由于(且仅)nil
和 false
都评估为布尔值 false,因此 a ||= b
实际上(几乎*)等效于:
if a == nil || a == false
a = b
end
或者,用另一个 Ruby 习语重写它:
a = b unless a
(*由于每个语句都有一个值,因此它们在技术上并不等同于 a ||= b
。但如果您不依赖该语句的值,您将看不到差异。)
这里有一些,从各种来源中挑选出来的:
使用“除非”和“直到”而不是“如果不是”和“而不是”。但是,当存在“其他”条件时,尽量不要使用“除非”。
请记住,您可以一次分配多个变量:
a,b,c = 1,2,3
甚至交换没有临时变量的变量:
a,b = b,a
在适当的地方使用尾随条件,例如
do_something_interesting unless want_to_be_bored?
请注意定义类方法的常用但不是立即显而易见(至少对我而言)的方式:
class Animal
class<<self
def class_method
puts "call me using Animal.class_method"
end
end
end
一些参考资料:
http://cbcg.net/talks/rubyidioms/index.html
http://www.therailsway.com/2006/12/8/idiomatic-ruby
http://www.therailsway.com/2007/1/21/more-idiomatic-ruby
http://www.caliban.org/ruby/rubyguide.shtml
我总是忘记这个速记 if else 语句的确切语法(以及运算符的名称。评论任何人?)我认为它在 ruby 之外被广泛使用,但如果其他人想要这里的语法,它是:
refactor < 3 ? puts("No need to refactor YET") : puts("You need to refactor this into a method")
扩展到
if refactor < 3
puts("No need to refactor YET")
else
puts("You need to refactor this into a method")
end
更新
称为三元运算符:
返回 myvar ?我的变量大小:0
您可以轻松地使用 Marshaling 对象进行深度复制。 - 取自 The Ruby Programming Language
def deepcopy(o)
Marshal.load(Marshal.dump(o))
end
请注意,文件和 I/O 流以及 Method 和 Binding 对象过于动态而无法编组;没有可靠的方法来恢复他们的状态。
a = (b && b.attribute) || "default"
大致是:
if ( ! b.nil? && ! b == false) && ( ! b.attribute.nil? && ! b.attribute.false) a = b
else a = "default"
当 b 是可能已找到或未找到的记录时,我使用它,并且我需要获取它的一个属性。
我喜欢 If-then-elses 或 case-when 可以如何缩短,因为它们返回一个值:
if test>0
result = "positive"
elsif test==0
result = "zero"
else
result = "negative"
end
可以重写
result = if test>0
"positive"
elsif test==0
"zero"
else
"negative"
end
这同样适用于 case-when:
result = case test
when test>0 ; "positive"
when test==0 ; "zero"
else "negative"
end
result = (test>0)? "positive" : (test==0 ? "zero" : "negative")
when test > 0 then "positive"
。 (@Salil 我发现嵌套的三元运算符真的很难阅读。)
好问题!
正如我认为更直观的 &代码越快,我们正在构建的软件就越好。我将向您展示我如何使用 Ruby 在小段代码中表达我的想法。 Read more here
地图
我们可以通过不同的方式使用 map 方法:
user_ids = users.map { |user| user.id }
或者:
user_ids = users.map(&:id)
样本
我们可以使用 rand 方法:
[1, 2, 3][rand(3)]
随机播放:
[1, 2, 3].shuffle.first
以及惯用、简单和最简单的方法……示例!
[1, 2, 3].sample
双管等于/记忆
正如您在描述中所说,我们可以使用记忆:
some_variable ||= 10
puts some_variable # => 10
some_variable ||= 99
puts some_variable # => 10
静态方法/类方法
我喜欢使用类方法,我觉得这是一种非常惯用的创建和使用类的方式:
GetSearchResult.call(params)
简单的。美丽的。直觉的。后台发生了什么?
class GetSearchResult
def self.call(params)
new(params).call
end
def initialize(params)
@params = params
end
def call
# ... your code here ...
end
end
有关编写惯用 Ruby 代码的更多信息,请阅读 here
Array.pack 和 String.unpack 用于处理二进制文件:
# extracts four binary sint32s to four Integers in an Array
data.unpack("iiii")
方法缺少魔法
class Dummy
def method_missing(m, *args, &block)
"You just called method with name #{m} and arguments- #{args}"
end
end
Dummy.new.anything(10, 20)
=> "You just called method with name anything and arguments- [10, 20]"
如果您调用 ruby 对象中不存在的方法,ruby 解释器将调用称为“method_missing”的方法(如果已定义),您可以将其用于一些技巧,例如编写 api 包装器或 dsl,您不知道所有方法和参数名字
不定期副业成功案例分享