index into a circular array - python

I have a circular array. I created it with the following:
from itertools import cycle
myArray = ['a','b','c','d']
pool = cycle(myArray)
Now I want to print the nth item in pool where n could be larger than 4. Normally this would be a simple use of the modulo function but logically I think Python has a method which will know the number of elements in the pool (4 in this example) and automatically apply the modulo function.
For example the 1st and 5th item is 'a'. So I'm hoping for, logically, the equivalent of pool[0] and pool[4] giving me 'a'.
Is there such a method?

No, there's no built-in method to accomplish what you're attempting to do. As suggested earlier, you could use zip, but that would involve indexing into the result based on your sequence, as well as generating n elements out to the item you want.
Sometimes the simplest approach is the clearest. Use modulo to accomplish what you're after.
def fetch_circular(n):
myArray = ['a','b','c','d']
return myArray[n % 4]

I think you may be confusing arrays with generators.
The modulo function of an array is the way to go, in terms of performance.
cycle is a function which generates elements as they are requested. It is not a Cycle class with convenient methods. You can see the equivalent implementation in the documentation, and you'll probably understand what is the idea behind it:
https://docs.python.org/2/library/itertools.html#itertools.cycle

A list is definitely the way to go but if you actually had a cycle object and wanted the nth object wrapping around, you could islice:
from itertools import cycle, islice
myArray = ['a','b','c','d']
pool = cycle(myArray)
print(next(islice(pool, 5)))
a
Note once you call next(islice you have started cycling the list, if you actually want to be constantly rotating you may actually want a deque

Your pool object is already a generator and it will keep looping through myArray forever, so all you need is to zip your iterator with pool this way:
>>> pool = cycle(myA)
>>> for i,item in zip(range(10),pool):
print i,item
0 a
1 b
2 c
3 d
4 a
5 b
6 c
7 d
8 a
9 b
>>>

Related

How does heapq.merge() work with infinite generators?

I want to understand how heapq.merge() works with infinite generators. Consider this example:
>>> from heapq import merge
>>> from itertools import count
>>> m = merge(count(0, 2), count(1, 2))
>>> for _ in range(10):
... print(next(m))
...
0
1
2
3
4
5
6
7
8
9
The docs state that it does not pull the data into memory all at once. But how does it consume each of the infinite generators?
A very simple implementation of such a function could look like the following. Note, though, that for the sake of simplicity this does not handle any special (and not-so-special) cases like empty or exhausted iterables.
def merge(*iterables):
heap = [(next(it), i) for i, it in enumerate(iterables)]
heapq.heapify(heap)
while heap:
val, i = heapq.heappop(heap)
yield val
heapq.heappush(heap, (next(iterables[i]), i))
It works like this:
get the first element from each sorted iterable, together with that iterable's index in the list
yield the next smallest element from that heap
add the next element from the iterable with the same index as the one just yielded to the heap
The actual implementation is a bit more involved, but seems to work roughly along the same lines. You can get the location of your local source with heapq.__file__, which on my system is /usr/lib/python3.6/heapq.py, and check yourself.

Find the product of items in the list without using the multiplication sign

I'm new in python and I wrote code to have the product of items in a list without using the multiplication sign:
def witOutmultiply(some_list):
first_num = some_list[0]
result = 0
for n in some_list[1:]:
for i in range(n):
result += first_num
first_num = result
result = 0
return first_num
q =[2,4,5,6,10,15]
print(witOutmultiply(q))
My question is: can I use comprehensions in this case, and can I get the result with just one loop? Thanks
Yes, you can use list comprehension, sum, and range, but no other builtin functions:
q = [2,4,5,6,10,15]
mult = q[0]
for n in q[1:]:
mult = sum(mult for _ in range(n))
print(mult)
#36000
Here is an answer with no loop at all that satisfies your condition of "no multiplication sign." It is therefore very fast. The reduce function repeats an operation between members of an iterable, reducing it to a single value, while the mul function multiplies two numbers. The 1 at the end of the reduce function gives a reasonable value if the iterable (list) is empty. No multiplication sign in sight!
from operator import mul
from functools import reduce
def prod_seq(seq):
"""Return the product of the numbers in an iterable."""
return reduce(mul, seq, 1)
Comprehensions are used to build data structures. A list comprehension builds a list, a dict comprehension builds a dict, etc. Since you want a single value rather than a data structure in your computation, there's no good reason to use a comprehension.
There probably are ways to avoid using two loops, but it's not going to be easy, since your outer loop does several operations, not just one. Most of the easy ways to avoid an explicit loop will just be hiding one or more loops in some function call like sum. I think that for your chosen algorithm (doing multiplication by adding), your current code is quite good and there's no obvious way to improve it.
from numpy import prod
print(prod(q))
#36000

Extend range in Python

I need a loop containing range(3,666,2) and 2 (for the sieve of Eratosthenes, by the way). This doesn't work ("AttributeError: 'range' object has no attribute 'extend'" ... or "append"):
primes = range(3,limit,2)
primes.extend(2)
How can I do it in the simple intuitive pythonesque way?
range() in Python 3 returns a dedicated immutable sequence object. You'll have to turn it into a list to extend it:
primes = list(range(3, limit, 2))
primes.append(2)
Note that I used list.append(), not list.extend() (which expects a sequence of values, not one integer).
However, you probably want to start your loop with 2, not end it. Moreover, materializing the whole range into a list requires some memory and kills the efficiency of the object. Use iterator chaining instead:
from itertools import chain
primes = chain([2], range(3, limit, 2))
Now you can loop over primes without materializing a whole list in memory, and still include 2 at the start of the loop.
If you're only looping and don't want to materialise, then:
from itertools import chain
primes = chain([2], range(3, limit, 2))
I think the two makes more sense at the start though...

Is it possible to convert a list-type into a generator without iterating through?

I know that it's possible to convert generators into lists at a "low-level" (eg. list(i for i in xrange(10))), but is it possible to do the reverse without iterating through the list first (eg. (i for i in range(10)))?
Edit: removed the word cast for clarity in what I'm trying to achieve.
Edit 2: Actually, I think I may have misunderstood generators at a fundamental level. That'll teach me to not post SO questions before my morning coffee!
Try this: an_iterator = iter(a_list) ... docs here. Is that what you want?
You can take a list out of an iterator by using the built-in function list(...) and an iterator out of a list by using iter(...):
mylist = list(myiterator)
myiterator = iter(mylist)
Indeed, your syntax is an iterator:
iter_10 = (i for i in range(10))
instead of using [...] which gives a list.
Have a look at this answer Hidden features of Python
Indeed it's possible a list possesses the iterator interface:
list_iter = list.__iter__() # or iter(list), returns list iterator
list_iter.__next__() # list_iter.next() for python 2.x
So,
lst_iter = iter(lst)
does the trick.
Though it makes more sense to use comprehensions and make a generator out of it: e.g.
lst_iter_gt_10 = (item for item in lst if item > 10)
I'm not sure you mean, but what you typed is valid Python code:
>>> x = (i for i in range(10))
>>> x
<generator object at 0xb7f05f6c>
>>> for i in x: print i
0
1
2
3
4
5
6
7
8
9
next was key:
next(iter([]), None)
where you can replace None with whatever default you want

Strange python for syntax, how does this work, whats it called?

print max(3 for i in range(4))
#output is 3
Using Python 2.6
The 3 is throwing me off, heres my attempt at explaining whats going on.
for i in range(4) makes a loop that loops 4 times, incrementing i from 0 to 3 at the start of each loop. [no idea what the 3 means in this context...] max() returns the biggest iterable passed to it and the result is printed to screen.
3 for i in range(4) is a generator that yields 3 four times in a row and max takes an iterable and returns the element with the highest value, which is, obviously, three here.
This evaluates to:
print max([3,3,3,3])
... which is an elaborate way to say print 3.
expr for x in xs is a generator expression. Typically, you would use x in expr. For example:
[2*i for i in range(4)] #=> [0, 2, 4, 6]
It can be rewritten as:
nums = []
for i in range(4):
nums.append(3)
print max(nums) # 3! Hurrah!
I hope that makes its pointlessness more obvious.
The expression:
print max(3 for i in range(4))
is printing the result of the max() function, applied to what is inside the parentheses. Inside the parentheses however, you have a generator expression creating something similar to an array, with all elements equal to 3, but in a more efficient way than the expression:
print max([3 for i in range(4)])
which will create an array of 3s and destroy it after it is no longer needed.
Basically: because inside the parentheses you will create only values that are equal, and the max() function returns the biggest one, you do not need to create more than one element. Because with the number of elements always equal to one, the max() function becomes not needed and your code can be effectively replaced (at least in the case you have given) by the following code:
print 3
That is simply all ;)
To read more about differences between comprehension and generator expression, you can visit this documentation page.

Categories

Resources