【问题标题】:Porting from Ruby to Python: What to do with 'yield'从 Ruby 移植到 Python:如何处理“yield”
【发布时间】:2019-08-20 13:55:45
【问题描述】:

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

代码用于Myers diff 算法,并在此blog

中有完整描述

这是我看不懂的代码sn-p:

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

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

【问题讨论】:

标签: python ruby language-comparisons


【解决方案1】:

几乎相同。尽管 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

由于block和回调函数基本是一回事,所以就相当于这个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(这是Ruby 主义者比for 更喜欢的方式):

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

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

在您的情况下,如果您将 yield 完全保留在 Python 中,则使用它的行将与 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),但我希望两者的代码看起来相似语言,用于比较。

【讨论】:

    【解决方案2】:

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

    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 方法,因此理论上在您实现此方法时,您必须将回调函数传递给它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-18
      • 2018-08-04
      • 2021-02-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多