I currently try to port a piece of code from Ruby to Python to do a bit algorithm research. I have no experience in Ruby and don't know how to handle the 'yield' keyword.
The code is for a Myers diff algorithm and fully described in this blog
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?
Almost identically. Though the semantics of Python's and Ruby's yield
is somewhat different, in this case they coincide almost exactly.
Ruby's yield
invokes a block that is passed into the function, giving it its parameters.
Python's yield
makes the function a generator, and generates one output from it.
Both of them only make sense in context of a function, so just your while
loop is too short a context to use it. But let's take something like it as a simplified example, in 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:
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:
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 num, double in numbers_and_doubles(5):
print(f"{num} * 2 = {double}")
In Ruby, the closest literal translation is 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
):
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
). 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
backtrack do |prev_x, prev_y, x, y|
to Python's
for prev_x, prev_y, x, y in backtrack():
You can read more at 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.