Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using generator send() within a for loop

I implemented graph traversal as a generator function which yields the node being visited.

Sometimes the user needs to tell the traversal function that the edges outgoing from a particular node shouldn't be followed; in order to support that, the traversal checks the value sent back to it (using generator send() method), and if it's True, regards the node as a leaf for traversal purposes.

The problem is that the simplest user loop is kinda long:

# simplified thanks to @tobias_k
# bfs is the traversal generator function
traversal = bfs(g, start_node)
try:
  n = next(traversal)
  while True:
    # process(n) returns True if don't want to follow edges out of n
    n = traversal.send(process(n))
except StopIteration:
    pass

Is there any way to improve this?

I thought something like this should work:

for n in bfs(g, start_node):
  ???.send(process(n))

but I feel I'm missing the knowledge of some python syntax.

like image 279
max Avatar asked Apr 25 '16 08:04

max


People also ask

What does the send () method do to a generator?

The send() method returns the next value yielded by the generator, or raises StopIteration if the generator exits without yielding another value. When send() is called to start the generator, it must be called with None as the argument, because there is no yield expression that could receive the value.

Can you iterate over a generator?

Simply speaking, a generator is a function that returns an object (iterator) which we can iterate over (one value at a time).

Is generator faster than for loop Python?

This generator uses an iterator, because the "for" loop is implemented using an iterator. If you time these, the generator is consistently faster.

How do you use the send method in Python?

Sending Values to Generators The send() method resumes the generator and sends a value that will be used to continue with the next yield . The method returns the new value yielded by the generator. The syntax is send() or send(value) . Without any value, the send method is equivalent to a next() call.


1 Answers

I don't see a way to do this in a regular for loop. However, you could create another generator, that iterates another generator, using some "follow-function" to determine whether to follow the current element, thus encapsulating the tricky parts of your code into a separate function.

def checking_generator(generator, follow_function):
    try:
      x = next(generator)
      while True:
        yield x
        x = generator.send(follow_function(x))
    except StopIteration:
        pass

for n in checking_generator(bfs(g, start_node), process):
    print(n)
like image 109
tobias_k Avatar answered Sep 24 '22 15:09

tobias_k