68

我喜欢 ruby​​ 的一件事是它主要是一种非常易读的语言(这对于自记录代码来说非常有用)

然而,受这个问题的启发:Ruby Code explain and the description of how ||=works in ruby​​,我在想我不使用的 ruby​​ 成语,坦率地说,我并没有完全理解它们。

所以我的问题是,类似于引用问题中的示例,我需要了解哪些常见但不明显的 ruby​​ 习语才能成为真正精通的 ruby​​ 程序员?

顺便说一句,从引用的问题

a ||= b 

相当于

if a == nil || a == false
  a = b
end

(感谢 Ian Terrell 的更正)

编辑:事实证明,这一点并非完全没有争议。正确的展开其实是

(a || (a = (b))) 

请参阅以下链接了解原因:

感谢 Jörg W Mittag 指出这一点。

4

15 回答 15

52

让同一个文件充当库或脚本的神奇 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' ]
于 2009-03-05T09:27:09.010 回答
11

这张幻灯片非常完整地介绍了主要的 Ruby 习语,如下所示:

  • 交换两个值:

    x, y = y, x

  • 如果未指定,则采用某些默认值的参数

    def somemethod(x, y=nil)

  • 将无关参数批处理到数组中

    def substitute(re, str, *rest)

等等...

于 2009-03-05T09:22:29.347 回答
8

还有一些成语:

和分隔符%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

each对 Ruby主义者来说应该看起来很自然的东西,但对于来自其他语言的人来说可能不是这样:使用for .. in

some_iterable_object.each{|item| ... }

在 Ruby 1.9+、Rails 中,或者通过修补 Symbol#to_proc 方法,正变得越来越流行:

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})" }
于 2010-07-04T21:47:35.723 回答
7

我喜欢这个:

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?

或者至少那是我用来替换这些块的东西。

于 2009-03-05T09:08:46.247 回答
7

我建议通读你钦佩和尊重的人的流行和精心设计的插件或宝石的代码。

我遇到的一些例子:

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
于 2009-03-05T09:27:51.770 回答
5

这里有一些,从各种来源中挑选出来的:

使用“除非”和“直到”而不是“如果不是”和“而不是”。但是,当存在“其他”条件时,尽量不要使用“除非”。

请记住,您可以一次分配多个变量:

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

一些参考资料:

于 2009-03-05T09:47:55.433 回答
5

顺便说一句,从引用的问题

a ||= b 

相当于

if a == nil   
  a = b 
end

这是不正确的,并且是新手 Ruby 应用程序中错误的来源。

由于两者(并且唯一)nilfalse评估为布尔假,a ||= b实际上(几乎*)等同于:

if a == nil || a == false
  a = b
end

或者,用另一个 Ruby 习语重写它:

a = b unless a

(*由于每个语句都有一个值,因此这些在技术上并不等同于a ||= b。但如果您不依赖该语句的值,您将看不到差异。)

于 2009-03-05T15:23:49.120 回答
4

我维护了一个 wiki 页面,其中涵盖了一些 Ruby 习语和格式:

https://github.com/tokland/tokland/wiki/RubyIdioms

于 2011-09-27T13:42:44.363 回答
2

我总是忘记这个简写 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

于 2011-10-25T17:53:18.167 回答
1

您可以轻松地使用 Marshaling 对象进行深度复制。- 取自 The Ruby Programming Language

def deepcopy(o)
  Marshal.load(Marshal.dump(o))
end

请注意,文件和 I/O 流以及 Method 和 Binding 对象过于动态而无法编组;没有可靠的方法来恢复他们的状态。

于 2010-07-04T21:13:23.980 回答
1
a = (b && b.attribute) || "default"

大致是:

if ( ! b.nil? && ! b == false) && ( ! b.attribute.nil? && ! b.attribute.false) a = b
else a = "default"

当 b 是可能已找到或未找到的记录时,我使用它,并且我需要获取它的一个属性。

于 2011-03-09T20:13:42.867 回答
1

我喜欢 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
于 2012-02-09T00:51:51.967 回答
1

好问题!

我认为代码越直观、越快,我们正在构建的软件就越好。我将向您展示我如何使用 Ruby 在小段代码中表达我的想法。在这里阅读更多

地图

我们可以通过不同的方式使用 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 代码的更多信息,请阅读此处

于 2017-04-22T00:30:08.500 回答
0

Array.pack 和 String.unpack 用于处理二进制文件:

# extracts four binary sint32s to four Integers in an Array
data.unpack("iiii") 
于 2010-03-28T09:49:34.713 回答
0

方法缺少魔法

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,您不知道所有方法和参数名字

于 2014-06-20T10:13:42.753 回答