常见的 Ruby 习语

发布于 2024-07-14 04:10:10 字数 1152 浏览 7 评论 0原文

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

但是,受到这个问题的启发:Ruby 代码解释 以及 ||= 如何在 ruby​​ 中工作的描述,我在思考我不使用的 ruby​​ 习惯用法,坦率地说,我并没有完全理解它们。

所以我的问题是,与引用问题中的示例类似,要成为一名真正熟练的 ruby​​ 程序员,我需要了解哪些常见但不明显的 ruby​​ 习惯用法?

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

a ||= b 

相当于

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

(感谢 Ian Terrell 的更正)

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

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

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

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

One thing I love about ruby is that mostly it is a very readable language (which is great for self-documenting code)

However, inspired by this question: Ruby Code explained
and the description of how ||= works in ruby, I was thinking about the ruby idioms I don't use, as frankly, I don't fully grok them.

So my question is, similar to the example from the referenced question, what common, but not obvious, ruby idioms do I need to be aware of to be a truly proficient ruby programmer?

By the way, from the referenced question

a ||= b 

is equivalent to

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

(Thanks to Ian Terrell for the correction)

Edit: It turns out this point is not totally uncontroversial. The correct expansion is in fact

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

See these links for why:

Thanks to Jörg W Mittag for pointing this out.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

ㄟ。诗瑗 2024-07-21 04:10:10

让同一个文件充当库或脚本的神奇 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' ]

The magic if clause that lets the same file serve as a library or a script:

if __FILE__ == $0
  # this library may be run as a standalone script
end

Packing and unpacking arrays:

# 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' ]

The syntatical sugar for hashes as method arguments

this(:is => :the, :same => :as)
this({:is => :the, :same => :as})

Hash initializers:

# 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]}

metaclass syntax

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 instance variables

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"

Blocks, procs, and lambdas. Live and breathe them.

 # 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' ]
我早已燃尽 2024-07-21 04:10:10

这个幻灯片对于主要的 Ruby 习惯用法非常完整,如:

  • 交换两个值:

    x, y = y, x

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

    def somemethod(x, y=nil)

  • 将无关参数批量放入数组中

    def Replace(re, str, *rest)

如此在...

This slideshow is quite complete on the main Ruby idioms, as in:

  • Swap two values:

    x, y = y, x

  • Parameters that, if not specified, take on some default value

    def somemethod(x, y=nil)

  • Batches up extraneous parameters into an array

    def substitute(re, str, *rest)

And so on...

一场信仰旅途 2024-07-21 04:10:10

更多习语:

使用 %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 语句中的 === 方法

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 方法,这个正在成为越来越流行的习惯用法:

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})" }

Some more idioms:

Use of the %w, %r and %( delimiters

%w{ An array of strings %}
%r{ ^http:// }
%{ I don't care if the string has 'single' or "double" strings }

Type comparison in case statements

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

... and overall abuse of the === method in case statements

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

Something that should look natural to Rubyists, but maybe not so to people coming from other languages: the use of each in favor of for .. in

some_iterable_object.each{|item| ... }

In Ruby 1.9+, Rails, or by patching the Symbol#to_proc method, this is becoming an increasingly popular idiom:

strings.map(&:upcase)

Conditional method/constant definition

SOME_CONSTANT = "value" unless defined?(SOME_CONSTANT)

Query methods and destructive (bang) methods

def is_awesome?
  # Return some state of the object, usually a boolean
end

def make_awesome!
  # Modify the state of the object
end

Implicit splat parameters

[[1, 2], [3, 4], [5, 6]].each{ |first, second| puts "(#{first}, #{second})" }
揽清风入怀 2024-07-21 04:10:10

我喜欢这样:

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?

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

I like this:

str = "Something evil this way comes!"
regexp = /(\w[aeiou])/

str[regexp, 1] # <- This

Which is (roughly) equivalent to:

str_match = str.match(regexp)
str_match[1] unless str_match.nil?

Or at least that's what I've used to replace such blocks.

维持三分热 2024-07-21 04:10:10

我建议阅读流行且设计良好的插件的代码或来自您钦佩和尊重的人的宝石。

我遇到的一些例子:

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

I would suggest reading through the code of popular and well designed plugins or gems from people you admire and respect.

Some examples I've run into:

if params[:controller] == 'discussions' or params[:controller] == 'account'
  # do something here
end

corresponding to

if ['account', 'discussions'].include? params[:controller]
  # do something here
end

which later would be refactored to

if ALLOWED_CONTROLLERS.include? params[:controller]
  # do something here
end
北凤男飞 2024-07-21 04:10:10

顺便说一句,从参考文献来看
问题

<前><代码>a ||= b

相当于

如果 a == nil    
    a = b  
  结尾 
  

That's 微妙地不正确,并且是新手 Ruby 应用程序中错误的来源。

由于(且仅)nilfalse 的计算结果均为布尔值 false,因此 a ||= b 实际上(几乎*)相当于:

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

或者,用另一个 Ruby 惯用法重写它:

a = b unless a

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

By the way, from the referenced
question

a ||= b 

is equivalent to

if a == nil   
  a = b 
end

That's subtly incorrect, and is a source of bugs in newcomers' Ruby applications.

Since both (and only) nil and false evaluate to a boolean false, a ||= b is actually (almost*) equivalent to:

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

Or, to rewrite it with another Ruby idiom:

a = b unless a

(*Since every statement has a value, these are not technically equivalent to a ||= b. But if you're not relying on the value of the statement, you won't see a difference.)

白云悠悠 2024-07-21 04:10:10

以下是从各种来源中精选的一些内容:

使用“unless”和“until”而不是“if not”和“while not”。 不过,当存在“else”条件时,尽量不要使用“unless”。

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

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

一些参考文献:

Here's a few, culled from various sources:

use "unless" and "until" instead of "if not" and "while not". Try not to use "unless" when an "else" condition exists, though.

Remember you can assign multiple variables at once:

a,b,c = 1,2,3

and even swap variable without a temp:

a,b = b,a

Use trailing conditionals where appropriate, e.g.

do_something_interesting unless want_to_be_bored?

Be aware of a commonly-used but not instantly obvious (to me at least) way of defining class methods:

class Animal
  class<<self
    def class_method
      puts "call me using Animal.class_method"
    end
  end
end

Some references:

最好是你 2024-07-21 04:10:10

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

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

I maintain a wiki page that covers some Ruby idioms and formatting:

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

情独悲 2024-07-21 04:10:10

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

update

调用三元运算符:

return myvar ? myvar.大小:0

I always forget the exact syntax of this shorthand if else statement (and the name of the operator. comments anyone?) I think it's widely used outside of ruby, but in case someone else wants the syntax here it is:

refactor < 3 ? puts("No need to refactor YET") : puts("You need to refactor this into a  method")

expands to

if refactor < 3
  puts("No need to refactor YET")
else
  puts("You need to refactor this into a  method")
end

update

called the ternary operator:

return myvar ? myvar.size : 0

笔落惊风雨 2024-07-21 04:10:10

好问题!

正如我认为更直观& 代码越快,我们正在构建的软件就越好。 我将向您展示如何使用 Ruby 在小代码片段中表达我的想法。 在此处了解更多信息

地图

我们可以以不同的方式使用map方法:

user_ids = users.map { |user| user.id }

或者:

user_ids = users.map(&:id)

示例

我们可以使用rand方法:

[1, 2, 3][rand(3)]

Shuffle:

[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 代码的更多信息,请阅读此处

Nice question!

As I think the more intuitive & faster the code is, a better software we’re building. I will show you how I express my thoughts using Ruby in little snippets of code. Read more here

Map

We can use map method in different ways:

user_ids = users.map { |user| user.id }

Or:

user_ids = users.map(&:id)

Sample

We can use rand method:

[1, 2, 3][rand(3)]

Shuffle:

[1, 2, 3].shuffle.first

And the idiomatic, simple and easiest way... sample!

[1, 2, 3].sample

Double Pipe Equals / Memoization

As you said in the description, we can use memoization:

some_variable ||= 10
puts some_variable # => 10

some_variable ||= 99
puts some_variable # => 10

Static Method / Class Method

I like to use class methods, I feel it is a really idiomatic way to create & use classes:

GetSearchResult.call(params)

Simple. Beautiful. Intuitive. What happens in the background?

class GetSearchResult
  def self.call(params)
    new(params).call
  end

  def initialize(params)
    @params = params
  end

  def call
    # ... your code here ...
  end
end

For more info to write idiomatic Ruby code, read here

没有伤那来痛 2024-07-21 04:10:10

您可以轻松地使用封送对象进行深度复制。 - 摘自《Ruby 编程语言》

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

请注意文件和 I/O 流,如
以及方法和绑定对象,
过于动态而难以整理; 那里
没有可靠的恢复方法
他们的状态。

You can deepcopy with Marshaling object easily. - taken from The Ruby Programming Language

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

Note that files and I/O streams, as
well as Method and Binding objects,
are too dynamic to be marshaled; there
would be no reliable way to restore
their state.

难如初 2024-07-21 04:10:10
a = (b && b.attribute) || "default"

大致是:

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

当 b 是一条可能已找到或未找到的记录,并且我需要获取其属性之一时,我使用此选项。

a = (b && b.attribute) || "default"

is roughly:

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

I use this when b is a record which may or may not have been found, and I need to get one of its attributes.

夜未央樱花落 2024-07-21 04:10:10

我喜欢如何缩短 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

I like how If-then-elses or case-when could be shortened because they return a value:

if test>0
  result = "positive"
elsif test==0
  result = "zero"
else
  result = "negative"
end

could be rewriten

result = if test>0
  "positive"
elsif test==0
  "zero"
else
  "negative"
end

The same could be applied to case-when:

result = case test
when test>0 ; "positive"
when test==0 ; "zero"
else "negative"
end
天赋异禀 2024-07-21 04:10:10

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

# extracts four binary sint32s to four Integers in an Array
data.unpack("iiii") 

Array.pack and String.unpack for working with binary files:

# extracts four binary sint32s to four Integers in an Array
data.unpack("iiii") 
鹤仙姿 2024-07-21 04:10:10

方法缺失 magick

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,在你不知道的情况下方法和参数名称

method missing magick

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]"

if you call methods that not exists in ruby objects, ruby interpreter will call method called 'method_missing' if its defined, you could user this for some tricks, like writing api wrappers, or dsl, where you don;t know all methods and parameters names

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文