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

时间:2019-03-29 15:19:03

标签: python ruby language-comparisons

我目前尝试将一段代码从Ruby移植到Python,以进行一些算法研究。我没有Ruby的经验,也不知道如何处理'yield'关键字。

该代码用于 Myers差异算法,并在此blog

中进行了详细说明

这是我不明白的代码段:

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

有没有办法在Python中对此进行近似?

2 个答案:

答案 0 :(得分:3)

几乎相同。尽管Python和Ruby yield的语义有些不同,但在这种情况下它们几乎完全重合。

Ruby的yield调用一个传递给函数的块,为其提供参数。

Python的yield使函数成为生成器,并从中生成一个输出。


它们两者仅在函数的上下文中才有意义,因此仅while循环的上下文太短而无法使用它。但是,在Ruby中,让我们将其作为一个简化示例:

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

此函数接受带有一个参数的块,然后生成最大为该数字的数字及其双精度数,并使用这些参数执行该块:

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

由于块与回调函数基本相同,因此它等效于以下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)

另一方面,Python的yield创建了一个生成器-一个返回可以按需产生值的函数的函数:

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

消耗发生器的最自然的方法是通过for循环:

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

在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

消耗Enumerator的最自然方法是使用each(这是Rubyists偏爱for的原因):

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

但是,正如我所说,即使它们做了一些稍有不同,上面的原始Ruby(带有yield)还是与上面的原始Python(带有yield)相似。消费它们的方式略有不同,但是适合每种语言的习惯用法。

在您的情况下,如果您完全像在Python中那样保留yield,则使用它的行将与Ruby的行不同

backtrack do |prev_x, prev_y, x, y|

到Python的

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

您可以在Python yield vs Ruby yield上阅读更多内容。


请注意,编写此循环的最自然的方法不是使用任何一种语言的while(我将在Python中使用range,而在Ruby中使用times),但我想拥有两种语言看起来都相似的代码,以便进行比较。

答案 1 :(得分:1)

让我们看一下博客中的代码:

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

如我们所见,该块将通过四个参数传递给backtrack方法,因此从理论上讲,在该方法的实现中,您必须向其传递回调函数。