1

I'm trying to build a 'skipper' in Python using iterators.

The idea is that given the following:

' '.join([str(i) for i in skippy([1,'a','b','b',2,1,1,1,'c','d','b'])

We get

1 b b 2 1 d b

As output. The rule being everytime we hit an integer x, we skip the following x items in the iterable.

So far I have:

def skippy(it):
    p = 0    
    for x in it:
        if type(x) == int:
           for x in range(x):
              p = next(it)
           yield p

And this doesn't work as expected, any ideas on how to fix it?

3
  • How would you ever get output with integers in it? As soon as you hit an int, you should be skipping that many elements again. Commented Oct 27, 2015 at 20:09
  • @MorganThrapp: I think the idea is you yield that integer, but then skip the N following elements. Commented Oct 27, 2015 at 20:10
  • @BrenBarn is correct! Commented Oct 27, 2015 at 20:11

3 Answers 3

5

Not sure what you're trying to achieve with using a separate p and x. You can just do this:

def skippy(it):
    it = iter(it)
    for x in it:
        if type(x) is int:
            yield x
            for skip in range(x):
                next(it)
        else:
            yield x

>>> ' '.join([str(i) for i in skippy(iter([1,'a','b','b',2,1,1,1,'c','d','b']))])
'1 b b 2 1 d b'

You need to call iter on the argument if you want to be able to pass a list in directly, because a list is an iterable, not an iterator, and you can only call next on an iterator.

Sign up to request clarification or add additional context in comments.

5 Comments

This is exactly what I was trying to achieve, I was trying to create a pointer of sorts with p and x. I guess this is much simpler! Thank you
I was going to post nearly the same answer, except I dispensed with the else and put the yield before the type check (you yield on both branches of the if-statement). That would make it shorter.
Note: The explicit loop with next calls would probably be done more efficiently (and without raising StopIteration in the generator, which is a source of bugs and now deprecated), by using part of the consume pattern from the itertools recipes. Instead of the for loop with repeated next calls, do: next(itertools.islice(it, x, x), None) which doesn't raise StopIteration directly, allowing generator to exit safely. Also runs faster for long skips by pushing work to C layer.
@ShadowRanger: You make a fair point about the implications of the naked next(it), but in this case the problem can be solved with next(it, None).
@StevenRumbalski: That ends up continuing to skip over and over even if the skip count takes you beyond the end of the list (islice will stop on the first StopIteration). In any event, the full consume pattern it optimizes further (for speed & code size; why generate unused range of ints?), and it's generally accepted as a Pythonic idiom (or at least, close enough; you might split it off into a separate method following the itertools consume recipe to improve the self-documenting/modular aspects of the code).
1

You can skip (no pun intended) the yield and do basic operations:

def skippy(it):
  skip = 0
  answer = []
  for x in it:
    if skip>0:
      skip -= 1;
    elif type(x) == int:
      skip = x
      answer.append(str(x))
    else:
      answer.append(str(x))
  return ' '.join(answer)

Comments

1

You can create your own skipper iteration class and define the next function to do what you want like so

class Skippy:
    def __init__(self, l):
        self.l = l
        self.current = 0
        self.high = len(l)

    def __iter__(self):
        return self

    def next(self):
        if self.current >= self.high:
            raise StopIteration
        else:
            current = self.l[self.current]
            self.current += 1
            if type(current) == int:
                self.current += self.l[self.current-1]
            return current

l = ' '.join([str(i) for i in Skippy([1,'a','b','b',2,1,1,1,'c','d','b'])])
print l

1 b b 2 1 d b

2 Comments

This works, but a generator function is simpler and better. (See @BrenBarn's answer.)
@StevenRumbalski I know but it's always good to know your options

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.