Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I am going on with refactoring my code base while migrating from Python 2 to Python 3. I am using generators to make reusable components this time as I am more comfortable with them this time then last time. But there are times when I am stuck at their use. Like below example. These two are essentially same with one infinitely generating and other with a limit.

Is there a way to make these as one function? Normally an extra optional parameter as limit will do the trick but with generators I am not sure how to use them. Is there some way to do that?

def fibonacci_inf(a, b):
    """Lazily generates Fibonacci numbers Infinitely"""
    while True:
        yield b
        a, b = b, a + b


def fibonacci(a, b, num):
    """Lazily generates Fibonacci numbers"""
    while b < num:
        yield b
        a, b = b, a + b
share|improve this question
    
I don't use Python much, but why wouldn't an optional argument work here? Pseudocode: while(!optionalArgumentExists || optionalArgumentCondition). –  Ocelot20 Mar 30 at 15:27
    
@Ocelot20 Exactly my point. But I couldn't wrap my head around actual syntax without making this a mess. –  Aseem Bansal Mar 30 at 15:28

2 Answers 2

up vote 8 down vote accepted

You could just implement the infinite generator, and use itertools.takewhile to limit it:

>>> list(itertools.takewhile(lambda x : x < 8, fibonacci_inf(0,1)))
[1, 1, 2, 3, 5]
share|improve this answer
    
Having spent some time with Haskell I can really appreciate this now. Another reason why I should go over the standard library again once in a while. –  Aseem Bansal Mar 30 at 17:28

Something like this should work:

def sequence(start, end = None):
    loopIndefinitely = end is None   

    curr = start    
    while loopIndefinitely or curr <= end:
        yield curr
        curr += 1

## Prints: 1 2 3 4 5 6 7 8 9 10
for i in sequence(1, 10):
    print(i)

## Prints: 1 2 3 4 5 6 7 8 9 10
for j in sequence(1):
    if(j > 10):
        break
    else:
        print(j)

Used sequence instead since the variable names more clearly show the use vs. a, b, num.

I think it's important to:

  1. Clearly define what argument values lead to what functionality (for example, defining a clearly named variable like loopIndefinitely to show immediately what an end value of None means).
  2. Determine when it's appropriate to use an optional argument. This one is a bit tougher, but it may have been clearer to define two functions called range and infiniteSequence. In this example the code is pretty simple to share or even duplicate, but with a more complex function you may want to have two functions that both point to a third one that has most of the reusable code.
share|improve this answer
    
One variation when the logic is this simple is to check the optional argument once and branch--either to separate functions as you suggest or inline. It's a minor performance improvement but may help to highlight how the optional argument applies. –  David Harkness Mar 30 at 16:19

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.