Algorithm from Hacker's Delight 2nd Edition
Ported to python
A simple Hilbert curve class.
class Hilbert():
def __init__(self,order=1):
self.x = -1
self.y = 0
self._step(0)
self._hil(0, 1, order)
def _hil(self, dirs, rot, order):
if (order == 0):
return
dirs += rot
self._hil(dirs, -rot, (order-1))
self._step(dirs)
dirs -= rot
self._hil(dirs, rot, (order-1))
self._step(dirs)
self._hil(dirs, rot, (order-1))
dirs -= rot
self._step(dirs)
self._hil(dirs, -rot, (order-1))
def _step(self, dirs):
dirs %= 4
if dirs == 0:
self.x += 1
elif dirs == 1:
self.y += 1
elif dirs == 2:
self.x -= 1
else:
self.y -= 1
#prints all points
#print(self.x,self.y)
#Could I "iterate" from here.
So what I want is something that gives a (x,y) every time next() is called. I tried doing this myself but can't get it to work so any help would be appreciated. Would I have to rewrite this to work with a generator? Source
I think at least part of what you're trying to do is to turn
_hilinto a generator function, which yields thex, ypairs after each call to_step? If so, that's pretty easy.The hard part is those recursive calls. But that's not really hard at all. That's exactly what
yield fromis for:* Take some iterator (like a recursive call to a generator function) and yield each of its values.**Then there's the easy part, the non-recursive yielding of the
x, ypairs after each call to_step. You can do that with an explicityield self.x, self.yafter each call. Or you can change_stepto add areturn self.x, self.y, so you can justyield self._step(dirs). But you can also change_stepto an iterator that only iterates one value, and then you canyield fromit as well. (There's no real advantage to that here, but I think it's worth showing so you can think through how it works—especially since you asked "can I iterate from here?" in_step.)But now you've got that
__init__that just calls_hiland does nothing with the result. That's not very useful. Maybe you're trying to turn theHilbertclass itself into an iterator class?In that case, the easiest thing to do is store the generator iterator and delegate to it:
However, at this point, I'm not sure why you need this to be a class at all. The
xandyaren't really part of the object's state, they're part of the generator state, which Python would take care of magically if you just used local variables in_hil(and normal parameter-and-return passing in_step). And the only other state isself.iter, which is only necessary because you're writing a class instead of a function.* Actually, it turns out to be good for a lot more than that, as Greg Ewing describes amazingly well; we wouldn't have
asynciowithout it. But the original reason for adding it to the language was as "Syntax for Delegating to a Subgenerator".** Note that this only works if you have
yield from—meaning Python 3.3 or later. If you're still using Python 2.x, andyield fromisn't enough to get you to upgrade, you can simulate some uses of it—including this one—by changing everyyield from eggsintofor egg in eggs: yield egg. It won't be as readable, and it will be significantly slower, but it will work.