简体   繁体   English

从Ruby到Python的移植:如何处理“ yield”

[英]Porting from Ruby to Python: What to do with 'yield'

I currently try to port a piece of code from Ruby to Python to do a bit algorithm research. 我目前尝试将一段代码从Ruby移植到Python,以进行一些算法研究。 I have no experience in Ruby and don't know how to handle the 'yield' keyword. 我没有Ruby的经验,也不知道如何处理'yield'关键字。

The code is for a Myers diff algorithm and fully described in this blog 该代码适用于Myers差异算法,并在此博客中进行了详细描述

This is the code snippet I don't understand: 这是我不明白的代码片段:

  while x > prev_x and y > prev_y
    yield x - 1, y - 1, x, y
    x, y = x - 1, y - 1
  end

Is there a way to approximate this in Python? 有没有办法在Python中对此进行近似?

Almost identically. 几乎完全相同。 Though the semantics of Python's and Ruby's yield is somewhat different, in this case they coincide almost exactly. 尽管Python和Ruby的yield的语义有些不同,但在这种情况下,它们几乎完全重合。

Ruby's yield invokes a block that is passed into the function, giving it its parameters. Ruby的yield调用一个传递给函数的块,为其提供参数。

Python's yield makes the function a generator, and generates one output from it. Python的yield使函数成为生成器,并从中生成一个输出。


Both of them only make sense in context of a function, so just your while loop is too short a context to use it. 两者都仅在函数上下文中有意义,因此,仅while循环对于使用该函数而言上下文太短。 But let's take something like it as a simplified example, in Ruby: 但是,在Ruby中,让我们将其作为一个简化示例:

def numbers_and_doubles(n)
  i = 0
  while i < n
    yield i, 2 * i
    i += 1
  end
end

This function accepts a block with one parameter, then generates numbers up to that number along with their double and executes that block with those parameters: 此函数接受带有一个参数的块,然后生成最大为该数字的数字及其双精度数,并使用这些参数执行该块:

numbers_and_doubles(5) do |num, double|
  puts "#{num} * 2 = #{double}"
end

As blocks are basically the same thing as callback functions, it is equivalent to this Python: 由于块与回调函数基本相同,因此等效于以下Python:

def numbers_and_doubles(n, callback):
    i = 0
    while i < n:
        callback(i, i*2)
        i += 1

def output(num, double):
    print(f"{num} * 2 = {double}")

numbers_and_doubles(5, output)

On the other hand, Python's yield creates a generator - a function that returns a function that can produce values on demand: 另一方面,Python的yield会创建一个生成器-一个函数,该函数返回可以按需产生值的函数:

def numbers_and_doubles(n):
    i = 0
    while i < n:
        yield i, 2 * i
        i += 1

The most natural way to consume a generator is via a for loop: 消耗生成器的最自然的方法是通过for循环:

for num, double in numbers_and_doubles(5):
    print(f"{num} * 2 = {double}")

In Ruby, the closest literal translation is Enumerator : 在Ruby中,最接近的文字翻译是Enumerator

def numbers_and_doubles(n)
  Enumerator.new do |yielder|
    i = 0
    while i < n
      yielder.yield(i, i*2)
      i += 1
    end
  end
end

and the most natural way to consume an Enumerator is using each (which is what Rubyists prefer over for ): 使用Enumerator的最自然的方法是使用each (这是Rubyists首选的for ):

numbers_and_doubles(5).each do |num, double|
  puts "#{num} * 2 = #{double}"
end

But, as I said, even though they do something slightly different, the original Ruby above (with yield ) is surprisingly similar to the original Python above (with yield ). 但是,正如我所说,即使它们做了一些稍有不同,上面的原始Ruby(带有yield )还是惊人地类似于上面的原始Python(带有yield )。 The way they are consumed is slightly different, but appropriate to each language's idiom. 它们的使用方式略有不同,但是适合每种语言的习惯用法。

In your case, if you leave yield exactly as it is in your Python, the line that consumes it changes from Ruby's 在您的情况下,如果您保留的yield与Python中的yield完全相同,则消耗yield的行将与Ruby的变化

backtrack do |prev_x, prev_y, x, y|

to Python's 到Python的

for prev_x, prev_y, x, y in backtrack():

You can read more at Python yield vs Ruby yield . 您可以阅读更多关于Python yield vs Ruby yield的信息


Note that the most natural way to write this loop is not while in either language (I'd use range in Python and times in Ruby), but I wanted to have code that looks similar for both languages, for comparison. 请注意,编写此循环的最自然的方法不是while使用两种语言(我将在Python中使用range ,而在Ruby中使用times ),但是为了比较,我希望使两种语言看起来都相似的代码。

Lets look at the code from the blog: 让我们看一下博客中的代码:

def diff
  diff = []

  backtrack do |prev_x, prev_y, x, y|
    a_line, b_line = @a[prev_x], @b[prev_y]

    if x == prev_x
      diff.unshift(Diff::Edit.new(:ins, nil, b_line))
    elsif y == prev_y
      diff.unshift(Diff::Edit.new(:del, a_line, nil))
    else
      diff.unshift(Diff::Edit.new(:eql, a_line, b_line))
    end
  end

  diff
end

As we can see the block is passed to the backtrack method with four arguments, so theoretically in your implementation of this method you have to pass a callback function to it. 如我们所见,该块将通过四个参数传递给backtrack方法,因此从理论上讲,在实现此方法时,您必须向其传递回调函数。

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

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