简体   繁体   English

当块比函数(ruby)更有用时?

[英]When blocks are more useful than functions (ruby)?

I have two examples that give the same result. 我有两个例子给出相同的结果。

With block: 带块:

def self.do_something(object_id)
  self.with_params(object_id) do |params|
    some_stuff(params)
  end
end

def self.with_params(object_id, &block)
  find_object_by_id
  calculate_params_hash
  block.call(params_hash)
end

and with method: 并与方法:

def self.do_something(object_id)
  some_stuff(self.get_params(object_id))
end

def self.get_params(object_id)
  find_object_by_id
  calculate_params_hash
  params_hash
end

The second solution seems more straightforward, but I found some usages of the first one in our application code. 第二个解决方案似乎更直接,但我在应用程序代码中找到了第一个解决方案的一些用法。 My question is: in which situation the first one is recommended? 我的问题是:在哪种情况下推荐第一个? What are the pros and cons of each one? 每个人的利弊是什么?

Normally people use blocks when they want to run a piece of code inside of another piece of code. 通常人们在想要在另一段代码中运行一段代码时使用块。 Examples: 例子:

DB.with_shard_for_user(user_id) do |db|
  # perform operations on a user's shard

end # shard is reverted back to original value

File.new(filename) do |file|
  # work with file
end # file is closed automatically

User.transaction do
  # run some operations as a single transaction
end

These blocks are closed on their lexical context (they capture variables from where the block is declared, and carry them over to the place when blocks are called). 这些块在它们的词汇上下文中被关闭(它们从块的声明位置捕获变量,并在调用块时将它们带到该位置)。

Schematic structure of a method that accepts a block. 接受块的方法的示意结构。

def transaction
  open_transaction # pre- part

  yield if block_given? # run provided code

  commit_transaction # post- part
rescue
  rollback_transaction # handle problems
end

In your first example, use of a block is probably unjustified (IMHO). 在您的第一个示例中,使用块可能是不合理的(恕我直言)。 Too complex for no apparent reason. 太复杂了,没有明显的理由。

The main difference between a block and function as per your example is that the block runs within the context of the calling function . 根据您的示例,块和函数之间的主要区别在于块在调用函数的上下文中运行

So if your example was as: 所以如果你的例子如下:

def self.do_something(object_id)
  x = "boogy on"
  self.with_params(object_id) do |params|
    some_stuff(params)
    puts x
  end
end

The code within the block can access the variable x that was defined outside the block. 块中的代码可以访问在块外部定义的变量x。 This is called a closure. 这称为闭包。 You couldn't do this if you were just calling a function as per your second example. 如果您只是按照第二个示例调用函数,则无法执行此操作。

Another interesting thing about blocks is they can affect the control flow of the outer function. 关于块的另一个有趣的事情是它们可以影响外部函数的控制流。 So it is possible to do : 所以有可能做到:

def self.do_something(object_id)
  self.with_params(object_id) do |params|
    if some_stuff(params)
        return
    end
  end

  # This wont get printed if some_stuff returns true.
  puts "porkleworkle"
end

If the some_stuff call within the block returns a true value, the block will return. 如果块中的some_stuff调用返回true值,则块将返回。 This will return out of the block and out of the dosomething method . 这将退出块并退出dosomething方法 porkleworkle would not get output. porkleworkle不会得到输出。

In your examples you don't rely on either of these, so using function calls is probably much cleaner. 在您的示例中,您不依赖于其中任何一个,因此使用函数调用可能更清晰。

However, there are many situations where using blocks to allow you to take advantage of these things is invaluable. 但是,在许多情况下,使用块来让您利用这些东西是非常宝贵的。

When you call with_params(), you are not only sending in data, you are also providing some code to run. 当您调用with_params()时,您不仅要发送数据,还要提供一些代码来运行。 See if different blocks are sent into the with_params() call: 查看是否将不同的块发送到with_params()调用:

...
self.with_params(object_id) do |params|
  some_other_stuff()
  some_stuff(params)
end
...

and somewhere else: 和其他地方:

...
self.with_params(object_id) do |params|
  even_more_stuff(params)
end
...

If the blocks are all the same or with_params() is just called from one place then you might consider eliminating the blocks. 如果块都是相同的,或者只是从一个地方调用with_params(),那么您可以考虑消除块。

To sum up: use blocks if you want to pass into the method different bits of code(blocks) as well as data: hey with_params, take this data(object_id) and, by the way, run this code(block) while you're at it. 总结一下:如果你想将不同的代码(块)和数据传递给方法,请使用块:hey with_params,获取此数据(object_id),顺便说一句,运行此代码(块)重新开始吧。

BTW you are doing different things in the two examples: with_params() returns 顺便说一句,你在两个例子中做了不同的事情:with_params()返回

some_stuff(params_hash)

after evaluating the block. 在评估块之后。 And get_params() just returns 而get_params()只是返回

params_hash

A block totally rely on your code, but a function has its own code. 块完全依赖于您的代码,但函数有自己的代码。

So, if your code varies situation by situation, use block. 因此,如果您的代码因情况而异,请使用block。 If not, build a function and use it as a block box. 如果没有,请构建一个函数并将其用作块框。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM