473,385 Members | 2,069 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,385 software developers and data experts.

how to make a generator use the last yielded value when it regainscontrol

Ok, I wrote this all by myself, which explains why it doesn't work. It
is meant to take a number and generate the next number that follows
according to the Morris sequence. It works for a single number, but what
I'd like it to do is either:

1. repeat indefinitely and have the number of times controlled elsewhere
in the program (e.g., use the morris() generator in a for loop and use
that context to tell it when to stop)

2. just make it a function that takes a second argument, that being the
number of times you want it to repeat itself and create numbers in the
sequence

Here's the code so far, and any general comments are always appreciated
as well:

def morris(seed):
seed = list(str(seed))
grouping = []
nextNum = []
for i in range(len(seed)):
try:
if seed[i] == seed[i + 1]:
grouping.append(seed[i])
else:
grouping.append(seed[i])
nextNum.append(str(len(grouping)) + seed[i])
grouping = []
except IndexError:
grouping.append(seed[i])
nextNum.append(str(len(grouping)) + seed[i])
seed = ''.join(nextNum)
yield seed

I thought the second to last line might rebind 'seed' to the new value,
and it would use that again the next time through, but it seems to just
keep using the original value each time and yielding the same output.
Apr 7 '06 #1
18 1740
John Salerno wrote:
1. repeat indefinitely and have the number of times controlled elsewhere
in the program (e.g., use the morris() generator in a for loop and use
that context to tell it when to stop)

2. just make it a function that takes a second argument, that being the
number of times you want it to repeat itself and create numbers in the
sequence


Well, I suppose I could just do:

num = 1
for x in range(some_limit):
num = morris(num)
print num,

But that isn't as much of a challenge as the other two options. :) I'd
like the function to do all the work of returning multiple numbers
(which probably means that option 1 isn't the best either)
Apr 7 '06 #2
John Salerno wrote:
2. just make it a function that takes a second argument, that being the
number of times you want it to repeat itself and create numbers in the
sequence


Here's what I've come up with so far. Probably not the most elegant
solution because of the nested function, but it does work! :)

def morris(seed, limit):
num = seed
numberSet = []

def nextNum(num):
num = list(str(num))
grouping = []
nextNum = []
for i in range(len(num)):
try:
if num[i] == num[i + 1]:
grouping.append(num[i])
else:
grouping.append(num[i])
nextNum.append(str(len(grouping)) + num[i])
grouping = []
except IndexError:
grouping.append(num[i])
nextNum.append(str(len(grouping)) + num[i])
return ''.join(nextNum)

for x in range(limit):
numberSet.append(int(num))
num = nextNum(num)

return numberSet
Apr 7 '06 #3
The generator in your original post /does/ rebind seed, but only on the
last iteration of the loop. You'll need to wrap that loop in another
loop if you want the generator to yield more than once.

As for "communicating" with a generator --- e.g. telling it to stop ---
this might be done by passing some kind of mutable argument to the
generator and then changing the value of that mutable object. However,
it's not a very elegant solution, and in this case there's not really
any reason to do it. Instead, if you want the generator to stop, just
stop asking it to yield:

for number in morris_sequence_generator(seed):
if it_is_time_to_stop():
break

And:
- No need to convert str(seed) to a list! Strings are indexable.
- Instead of using "try...except IndexError" to detect the end of the
loop, just change the loop to range(len(seed) - 1) and do the yield
after the loop finishes.
- Use xrange instead of range. range is evil.

Bonus points:
Write the generator to work on a seed which is an iterable of unknown
length.

Super bonus points:
Print the Nth element in the sequence without holding more than N
groups of {digit, number of occurences} of state information. You'll
need to do this if you want to get very far: According to Wikipedia,
the 70th term of the look-and-say sequence has 179,691,598 digits.

Apr 7 '06 #4
John Salerno wrote:
It
is meant to take a number and generate the next number that follows
according to the Morris sequence. It works for a single number, but what
I'd like it to do is either:

1. repeat indefinitely and have the number of times controlled elsewhere
in the program (e.g., use the morris() generator in a for loop and use
that context to tell it when to stop)

2. just make it a function that takes a second argument, that being the
number of times you want it to repeat itself and create numbers in the
sequence


Definitely go for (1). The Morris sequence is a great candidate to
implement as a generator. As a generator, it will be more flexible and
efficient than (2).

def morris(num):
"""Generate the Morris sequence starting at num."""
num = str(num)
yield num
while True:
result, cur, run = [], None, 0
for digit in num+'\n':
if digit == cur:
run += 1
else:
if cur is not None:
result.append(str(run))
result.append(cur)
cur, run = digit, 1
num = ''.join(result)
yield num

# Example usage:
from itertools import islice
for n in islice(morris(1), 10):
print n

# Output:
"""
1
11
21
1211
111221
312211
13112221
1113213211
31131211131221
13211311123113112211
"""

--Ben

Apr 7 '06 #5
Ben Cartwright wrote:
Definitely go for (1). The Morris sequence is a great candidate to
implement as a generator. As a generator, it will be more flexible and
efficient than (2).


Actually I was just thinking about this and it seems like, at least for
my purpose (to simply return a list of numbers), I don't need a
generator. My understanding of a generator is that you do something to
each yielded value before returning to the generator (so that you might
not return at all), but since I'm not handling the individual numbers,
just getting a list, it seems I don't need them to be yielded. Of
course, a generator would allow the process to be done over and over, I
suppose, which is what I wanted, I just couldn't figure out how to keep
using the new values.
Apr 7 '06 #6
John Salerno wrote:
Actually I was just thinking about this and it seems like, at least for
my purpose (to simply return a list of numbers), I don't need a
generator.
Yes, if it's just a list of numbers you need, a generator is more
flexibility than you need. A generator would only come in handy if,
say, you wanted to give your users the option of getting the next N
items in the sequence, *without* having to recompute everything from
scratch.

My understanding of a generator is that you do something to
each yielded value before returning to the generator (so that you might
not return at all),


A generator is just an object that spits out values upon request; it
doesn't care what the caller does with those values.

There's many different ways to use generators; a few examples:

# Get a list of the first 10
from itertools import islice
m = [n for n in islice(morris(1), 10)]

# Prompt user between each iteration
for n in morris(1):
if raw_input('keep going? ') != 'y':
break
print n

# Alternate way of writing the above
g = morris(1)
while raw_input('keep going? ') == 'y':
print g.next()

--Ben

Apr 7 '06 #7
Here's my take on the thing. It only prints one term, though.

http://www.magicpeacefarm.com/lonnie...morris.py.html

(a bit too long to post)

Apr 7 '06 #8
just couldn't help taking the bait...

def morris(seed) :

"""
m = morris('3447221')
m.next() '1324172211' m.next() '1113121411172221' m.next()

'31131112111431173211'
"""

assert isinstance(seed,basestring) and seed.isdigit(),"bad seed"

def itially(z) :
feedback.z = z
while True :
yield feedback.z

def feedback(gen) :
while True :
feedback.z = gen.next()
yield feedback.z

def morrisify(number) :
from itertools import groupby
for digit,sequence in groupby(number) :
yield str(len(tuple(sequence)))
yield digit

return feedback(''.join(morrisify(number))
for number in itially(seed))
Apr 7 '06 #9
Lonnie Princehouse wrote:
Here's my take on the thing. It only prints one term, though.

http://www.magicpeacefarm.com/lonnie...morris.py.html

(a bit too long to post)


yikes, scary! :)

there was always the hint that using itertools might be helpful, as you
guys are doing, but i could never quite figure out how, but looking at
these alternate methods is definitely helpful
Apr 7 '06 #10
John Salerno wrote:
Ben Cartwright wrote:
Definitely go for (1). The Morris sequence is a great candidate to
implement as a generator. As a generator, it will be more flexible and
efficient than (2).


Actually I was just thinking about this and it seems like, at least for
my purpose (to simply return a list of numbers), I don't need a
generator. My understanding of a generator is that you do something to
each yielded value before returning to the generator (so that you might
not return at all), but since I'm not handling the individual numbers,
just getting a list, it seems I don't need them to be yielded. Of
course, a generator would allow the process to be done over and over, I
suppose, which is what I wanted, I just couldn't figure out how to keep
using the new values.


itertools.groupby makes this very straightforward:
from itertools import groupby .... def lookandsay(seed): .... seed = str(seed)
.... while 1:
.... seed = "".join("%s%s" % (len(list(group)), item)
.... for item, group in groupby(seed))
.... yield seed
....
seq = lookandsay(1)
seq.next() '11' seq.next() '21' seq.next() '1211' seq.next() '111221' seq.next() '312211'

If you want to get just part of the infinite series, use itertools.islice:
from itertools import islice
list(islice(lookandsay(1),10)) ['11', '21', '1211', '111221', '312211', '13112221', '1113213211',
'31131211131221', '13211311123113112211', '11131221133112132113212221'] list(islice(lookandsay(1),10,20)) ['3113112221232112111312211312113211',
'1321132132111213122112311311222113111221131221',
'1113122113121113123112111311222112132113213221133 1222113112211',
'3113112221131112311311121321123113213221121113122 11312111322212311322113212221',
'1321132132211331121321133112111312211213211312111 32221123113112221131112311332111213211322211312113 211',
'1113122113121113222123211211131221232112311311222 11211131221131112311332211213211321322113311213212 31231121113122113322113111221131221',
'3113112221131112311332111213122112311311221112131 22112132113213221123113112221133112132123222112111 31221131211132221232112111312111213111213211231131 122212322211331222113112211',
'1321132132211331121321231231121113112221121321132 12231121113112221121113122113121113222112132113213 22123211211131211121332211231131122211311123113321 11213122112311311123112111331121113122112132113213 211121332212311322113212221',
'1113122113121113222123211211131211121311121321123 11321322112111312211312112213211231132132211231131 12221131112311332211211131221131211132211121312211 23113111231121123222112132113213221133112132123123 11211131122211213211331121321123123211231131122211 21113122113121113123112112322111213211322211312113 211',
'3113112221131112311332111213122112311311123112111 33112111312211213211312111322211231131122211311122 12211131221121321131211132221121321132132211331121 32123222112311311222113111231132231121113112221121 32113311213211221121332211211131221131211132221232 11211131211121311121321123113213221121113122123211 21113122112131112131221121321132132211231131122211 31112311311121321122112132231121113122113322113111 221131221']

HTH
Michael

Apr 8 '06 #11
Michael Spencer wrote:
itertools.groupby makes this very straightforward:


I was considering this function, but then it seemed like it was only
used for determing consecutive numbers like 1, 2, 3 -- not consecutive
equivalent numbers like 1, 1, 1. But is that not right?
Apr 8 '06 #12
John Salerno wrote:
Michael Spencer wrote:
itertools.groupby makes this very straightforward:


I was considering this function, but then it seemed like it was only
used for determing consecutive numbers like 1, 2, 3 -- not consecutive
equivalent numbers like 1, 1, 1. But is that not right?

data = [1, 1, 1, 2, 2, 3, 4, 4, 3, 2, 2, 1, 1, 2, 2,4, 2, 2]

from itertools import groupby
for k, g in groupby( data ):
print k, list(g)

1 [1, 1, 1]
2 [2, 2]
3 [3]
4 [4, 4]
3 [3]
2 [2, 2]
1 [1, 1]
2 [2, 2]
4 [4]
2 [2, 2]

for k, g in groupby( data, lambda x: x<2 ):
print k, list(g)

True [1, 1, 1]
False [2, 2, 3, 4, 4, 3, 2, 2]
True [1, 1]
False [2, 2, 4, 2, 2]

Gerard

Apr 8 '06 #13
John Salerno wrote:
Michael Spencer wrote:
itertools.groupby makes this very straightforward:


I was considering this function, but then it seemed like it was only
used for determing consecutive numbers like 1, 2, 3 -- not consecutive
equivalent numbers like 1, 1, 1. But is that not right?

With one argument, groupby assembles groups of equal consecutive elements:
list((key, list(group)) for key, group in groupby("AAABBCAAA")) [('A', ['A', 'A', 'A']), ('B', ['B', 'B']), ('C', ['C']), ('A', ['A', 'A', 'A'])]

With a second keyfunc argument, groupby assembles groups where keyfunc(element)
is equal for consecutive elements list((key, list(group)) for key, group in groupby("AAAaaaAAA",str.isupper)) [(True, ['A', 'A', 'A']), (False, ['a', 'a', 'a']), (True, ['A', 'A', 'A'])]


HTH
Michael

Apr 8 '06 #14
Gerard Flanagan wrote:
John Salerno wrote:
Michael Spencer wrote:
itertools.groupby makes this very straightforward:

I was considering this function, but then it seemed like it was only
used for determing consecutive numbers like 1, 2, 3 -- not consecutive
equivalent numbers like 1, 1, 1. But is that not right?

data = [1, 1, 1, 2, 2, 3, 4, 4, 3, 2, 2, 1, 1, 2, 2,4, 2, 2]

from itertools import groupby
for k, g in groupby( data ):
print k, list(g)

1 [1, 1, 1]
2 [2, 2]
3 [3]
4 [4, 4]
3 [3]
2 [2, 2]
1 [1, 1]
2 [2, 2]
4 [4]
2 [2, 2]

for k, g in groupby( data, lambda x: x<2 ):
print k, list(g)

True [1, 1, 1]
False [2, 2, 3, 4, 4, 3, 2, 2]
True [1, 1]
False [2, 2, 4, 2, 2]

Gerard


Interesting. After following along with the doc example, it seemed like
I had to do complicated stuff with the keys parameter, and I kind of
lost track of it all.
Apr 9 '06 #15
Lonnie Princehouse wrote:
Here's my take on the thing. It only prints one term, though.

http://www.magicpeacefarm.com/lonnie...morris.py.html

(a bit too long to post)


excerpt :

def morris(seed, n):
"""..."""
if n == 1:
return seed
else:
return length_encode(morris(seed,n-1))

What's wrong with the following ?

def morris(seed,n) :
"""..."""
for k in xrange(n-1) :
seed=length_encode(seed)
return seed

or even

def morris(seed,n) :
return reduce(lambda x,y:y(x),n*[length_encode],seed)

I'd defend using recursion when it allows a more concise expression of
an algorithm, but not in other cases.

Mmmhhh, btw, strangely, it looks like a hole in the library that you
can't write eg

morris= lambda seed,n: reduce(operator.__rcall__,n*[length_encode],seed)
Apr 10 '06 #16
> What's wrong with the following ?

def morris(seed,n) :
"""..."""
for k in xrange(n-1) :
seed=length_encode(seed)
return seed


Nothing's wrong with it.

I happen to think the recursive version is more elegant, but that's
just me ;-)

Apr 10 '06 #17
Em Seg, 2006-04-10 Ã*s 10:05 -0700, Lonnie Princehouse escreveu:
I happen to think the recursive version is more elegant, but that's
just me ;-)


It may be elegant, but it's not efficient when you talk about Python.
Method calls are expensive:

$ python2.4 -mtimeit 'pass'
10000000 loops, best of 3: 0.0585 usec per loop
$ python2.4 -mtimeit -s 'def x(): pass' 'x()'
1000000 loops, best of 3: 0.291 usec per loop
$ calc 0.291/0.0585
~4.97435897435897435897
$ calc 0.291-0.0585
0.2325
This happens because of the dynamic nature of Python and its lack of
tail call optimization. IOW, avoid recursive methods when possible (I
usually write those for the first version of a method then rethink it
using a non-recursive approach), specially if they are part of a hot
spot.

--
Felipe.

Apr 10 '06 #18
In general, you're right - if speed is a concern, loops should be used
instead of recursion in Python when possible.

In this case, the recursive overhead is insignificant compared to the
expense of iterating over the sequence at every iteration. By the time
there are 70 stack frames of recursion (i.e. not much), the sequence is
more than 170 million elements long.

Apr 10 '06 #19

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

4
by: Wai Yip Tung | last post by:
I'm attempting to turn some process than uses callback to return result into a more user friendly generator. I'm hitting some road block so any pointer would be appriciated. Let say there is an...
5
by: Jerzy Karczmarczuk | last post by:
I thought that the following sequence gl=0 def gen(x): global gl gl=x yield x s=gen(1)
11
by: vbgunz | last post by:
I am afraid that this is the first time in which I would probably need something explained to me as if I were a little child. I am having a hard time getting this through my thick skull. What in...
3
by: andy.leszczynski | last post by:
Hi, I might understand why this does not work, but I am not convinced it should not - following: def nnn(): print 'inside' yield 1 def nn():
4
by: Kenneth McDonald | last post by:
I'm trying to write a 'flatten' generator which, when give a generator/iterator that can yield iterators, generators, and other data types, will 'flatten' everything so that it in turns yields...
3
by: metaperl | last post by:
For this program: def reverse(data): for index in range(len(data)-1, -1, -1): yield data r = reverse("golf") for char in r: print char
10
by: AA Arens | last post by:
I do have a database with customer info in it. To avoid it will be taken out of our office, is it possible to make it not-readable after a certain period? then every let say seven days, I needs to...
3
by: Dieter Maurer | last post by:
I met the following surprising behaviour .... for i in range(3): .... def gen1(): .... yield i .... yield i, gen1() .... .... 0 0 1 1
2
by: psaffrey | last post by:
I'm trying to implement an interactive graph visualisation tool using matplotlib. I want to use a spring layout, where nodes repulse each other and edges act as springs to pull connected nodes...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.