Most minimal way to repeat things in python [duplicate] - python

This question already has answers here:
Is it possible to implement a Python for range loop without an iterator variable?
(15 answers)
Closed 7 months ago.
Suppose you want to write a program that asks the user for X numbers and store them in A, then for Y numbers and store them in B
BEFORE YOU VOTE FOR CLOSING : yes, this question has been asked here, here, here, here and possibly elsewhere, but I reply to each of the proposed solutions in this question explaining why they're not what I'm looking for, so please keep reading before voting for closing IF you decide it's a duplicate. This is a serious question, see last paragraph for a small selection of languages supporting the feature I'm trying to achieve here.
A = []
B = []
# First possibilty : using while loops
# you need to have a counter
i = 0
while (i < X):
A.append(input())
# and increment it yourself
i+=1
# you need to reset it
i = 0
while (i < Y):
B.append(input())
# and increment it again
i+=1
So basically you need to repeat a certain thing x times, then another Y times, but python has only while loops and for loops. The while loop is not to repeat a certain thing N times, but to repeat things while a certain condition is True, that's why you have to use a counter, initialize it, increment it, and test against it.
Second solution is to use for loops :
# No need to create a counter
for x in xrange(x):
A.append(input())
# No need to increment the counter
# no need to reset the counter
for x in xrange(Y):
B.append(input())
This is a lot better :), but there's still something slightly anoying : why would I still have to supply "x", when I don't need x ? In my code, I don't need to know in what loop iteration I am, i just need to repeat things N times, so why do I have to create a counter at all ? Isn't there a way to completely get rid of counters ? imagine you could write something like :
# No need to supply any variable !
repeat(x):
A.append(input())
repeat(Y):
B.append(input())
Wouldn't that be more elegant ? wouldn't that reflect more accurately your intention to possible readers of your code ? unfortunately, python isn't flexible enough to allow for creating new language constructs (more advanced languages allow this). So I still have to use either for or while to loop over things. With that restriction in mind, how do I get the most closer possible to the upper code ?
Here's what I tried
def repeat(limit):
if hasattr(repeat,"counter"):
repeat.counter += 1
return repeat.counter < limit
repeat.limit = limit
repeat.counters = 0
return limit > 0 and True
while repeat(x):
A.append(input())
while repeat(Y):
B.append(input())
This solution works for simple loops, but obviously not for nested loops.
My question is
Do you know any other possible implementation that would allow me to get the same interface as repeat ?
Rebbutal of suggested solutions
suggested solution 1.1 : use itertools.repeat
you'll have to place your code inside a function and call that function inside itertools.repeat, somehow. It's different from the above repeat implementation where you can use it over arbitrary indented blocks of code.
suggestion solution 1.2 : use itertools.repeat in a for loop
import itertools
for _ in itertools.repeat(None, N):
do_something()
you're still writing a for loop, and for loops need a variable (you're providing _), which completely misses the point, look again :
while repeat(5):
do_x()
do_y()
do_z()
Nothing is provided just for the sake of looping. This is what I would like to achieve.
suggested solution 2 : use itertools.count
1) you're half the way there. Yes, you get rid of manually incrementing the counter but you still need for a counter to be created manually. Example :
c = itertools.count(1,1) # You still need this line
while( c.next() < 7):
a = 1
b = 4
d = 59
print "haha"
# No need to increment anything, that's good.
# c.incr() or whatever that would be
You also need to reset the counter to 0, this I think could be hidden inside a function if you use the with statement on it, so that at every end of the with statement the counter gets reset.
suggested solution 3 : use a with statement
I have never used it, but from what I saw, it's used for sweeping the try/catch code inside another function. How can you use this to repeat things ? I'd be excited to learn how to use my first with statement :)
suggested solution 4 : use an iterator/generator, xrange, list comprehensions...
No, no, no...
If you write :
[X() for _ in xrange(4)]
you are :
1) creating a list for values you don't care about.
2) supplying a counter (_)
if you write :
for _ in xrange(4):
X()
see point 2) above
if you write :
def repeat(n):
i = 0
while i < n :
yield i < n
i+1
Then how are you supposed to use repeat ? like this ? :
for x in repeat(5):
X()
then see point 2) above. Also, this is basically xrange, so why not use xrange instead.
Any other cool ideas ?
Some people asked me what language do I come from, because they're not familiar with this repeat(N) syntax. My main programming language is python, but I'm sure I've seen this syntax in other languages. A visit of my bookmarks followed by online search shows that the following languages have this cool syntax :
Ruby
it's just called times instead of repeat.
#!/usr/bin/env ruby
3.times do
puts "This will be printed 3 times"
end
print "Enter a number: "
num = gets.chomp.to_i
num.times do
puts "Ruby is great!"
end
Netlogo
pd repeat 36 [ fd 1 rt 10 ]
;; the turtle draws a circle
AppleScript
[applescript]
repeat 3 times
--commands to repeat
end repeat
[/applescript]
Verilog
1 module repeat_example();
2 reg [3:0] opcode;
3 reg [15:0] data;
4 reg temp;
5
6 always # (opcode or data)
7 begin
8 if (opcode == 10) begin
9 // Perform rotate
10 repeat (8) begin
11 #1 temp = data[15];
12 data = data << 1;
13 data[0] = temp;
14 end
15 end
16 end
17 // Simple test code
18 initial begin
19 $display (" TEMP DATA");
20 $monitor (" %b %b ",temp, data);
21 #1 data = 18'hF0;
22 #1 opcode = 10;
23 #10 opcode = 0;
24 #1 $finish;
25 end
26
27 endmodule
Rebol
repeat count 50 [print rejoin ["This is loop #: " count]]
Forth
LABEL 10 0 DO something LOOP will repeat something 10 times, and you didn't have to supply a counter (Forth automatically stores it in a special variable I).
: TEST 10 0 DO CR ." Hello " LOOP ;
Groovy
you can either write 1.upTo(4){ code } or 4.times{ code } to execute code 4 times. Groovy is a blast when it comes to looping, it has like half a dozen ways to do it (upTo, times, step, java for, groovy for, foreach and while).
// Using int.upto(max).
0.upto(4, createResult)
assert '01234' == result
// Using int.times.
5.times(createResult)
assert '01234' == result
// Using int.step(to, increment).
0.step 5, 1, createResult
assert '01234' == result

WARNING: DO NOT DO THIS IN PRODUCTION!
I highly recommend you go with one of the many list comprehension solutions that have been offered, because those are infinitely less hacky. However, if you're adventurous and really, really, really want to do this, read on...
We can adapt your repeat solution slightly so that nesting works, by storing state that changes depending on where it's being called. How do we do that? By inspecting the stack, of course!
import inspect
state = {}
def repeat(limit):
s = tuple((frame, line) for frame, _, line, _, _, _ in inspect.stack()[1:])
counter = state.setdefault(s, 0)
if counter < limit:
state[s] += 1
return True
else:
del state[s]
return False
We set up a state dict to keep track of current counters for each unique place the repeat function is called, by keying off of a tuple of (stack_frame, line_number) tuples. This has the same caveat as your original repeat function in that break literally breaks everything, but for the most part seems to work. Example:
while repeat(2):
print("foo")
while repeat(3):
print("bar")
Output:
foo
bar
bar
bar
foo
bar
bar
bar

Surely you are over-thinking things. If I understand your (rather lengthy) question correctly you want to avoid repeating code. In this case you first port of call should be to write a function. This seems relatively simple: what you need is a function that takes an argument x (I have used n simply because it reminds me of integers) and returns a list containing that many input elements. Something like (untested):
def read_inputs(n):
ret_val = []
for i in range(n):
ret_val.append(input())
return retval
The remainder of your code is simply:
A = read_inputs(X)
B = read_inputs(Y)

Related

How to make this leetcode method more efficient, without using count variable or perhaps another way?

This is for leetcode problem: https://leetcode.com/problems/majority-element
There is something wrong with the way I create solutions, and not sure how to stop doing it. Basically the problem is I always create a count variable. Here is it called greatest_count. For the if statement, I create a conditional, which I think is fine, but I feel like I don't need the additional greatest_count variable here but not sure a better way to write it. I always seem to think I need to count it and check it against the previous counts. Do I need this? How can I write this without needing the count variable? or without using the greatest unique? Any ways to optimize this would be great to know.
Problem area:
if unique_count > greatest_count:
greatest_count = unique_count
greatest_unique = i
Here is the full code:
class Solution:
def majorityElement(self, nums):
unique_nums = set(nums)
greatest_unique = 0
greatest_count = 0
for i in unique_nums:
unique_count = nums.count(i)
if unique_count > greatest_count:
greatest_count = unique_count
greatest_unique = i
return greatest_unique
Thank you
In order to get this to work in O(n) time and O(1) space, you would need a different approach. For example, find the majority bit for each of the 32 bits of the numbers and build the answer from the collected bits that are present in more than half the numbers:
def majorityElement(nums):
m = 0
for b in range(32): # go through all 32 bits
c = sum(n&(2**b)!=0 for n in nums) # count numbers with bit b set
if c>len(nums)//2: m |= 2**b # more than half, keep that bit
return m if m<2**31 else m-2**32 # turn Python's int to 32 bit signed
majorityElement([3,2,3]) # 3
majorityElement([-3,-3,1,1,1,-3,-3]) # -3
This is O(n) (linear) time because it runs through the list a fixed number of times. It is O(1) space because it does not use memory proportionally to the size of the list.

Python - Stacking Cups?

I have to write a function ('def stackHeights') where you are suppose to take an argument a number of cups, and returns the maximum height of a stack that can be built with that number of cups. For example if you have 7 cups, you can build the stack of height 3, but you don't have enough for a stack of height 4, because you only have one cup for the bottom row and you need 4.
"Hint: build from top to bottom using a while.
Output:
>>> stackHeight (7)
3
>>> stackHeight (3)
2
>>> stackHeight (12)
4
This is what I have right now:
def stackHeight(nCups):
nCups = int(input())
cups = {}
for i in range(nCups):
line = input().split()
if line[0].isnumeric():
cups[int(line[0])/2] = line[1]
else:
cups[int(line[1])] = line[0]
print("\n".join([cups[k] for k in sorted(cups.keys())]))
What am I doing wrong? The code doesn't seem to run for some reason. Keep in mind that I'm still fairly new to programming, so sorry for the cluster.
There are quite a few problems I notice with your code. First of all, you have input() way too many times, and input() will freeze the program whilst waiting for a user input. You probably want input("string that tells the user what to put here"). Also, you have a variable nCups, but nCups is being set to input(), so it is completely making the point of the variable useless. Also, if you want it how it is in your example, you don't want to print() in the loop. Another thing is that when you do cups={}, you are making it a dictionary, but you are later using an integer for the index, so you want a list [].
You're referring to a sequence of numbers called the Triangular Numbers, which has an equation to calculate the n-th number:
T(n) = n(n+1)/2
Using the quadratic formula you can invert this to:
T'(n) = (sqrt(8n+1)-1)/2
And thus your code will be:
def stackHeight(nCups):
return ((8*nCups+1)**0.5-1) // 2
And testing:
>>> def stackHeight(nCups):
... return ((8*nCups+1)**0.5-1)//2
...
>>> stackHeight(7)
3.0
>>> stackHeight(3)
2.0
>>> stackHeight(12)
4.0

Python For Loop Using Math Operators

Ok, I'm in the process of learning Python, and had a quick question about for loops. I was wondering if you could use math operators in them, like JavaScript. For example, could I do:
for i = 0, i < 5, i++:
#code here
Now, I'm quite aware that Python doesn't support i++, and I think it doesn't support the commas either. So if I can do it that way, could you provide a sample.
Thanks
You would use a range loop:
for i in range(5):
#code here
If you want to increment in a loop you would use a while loop:
i = 0
while i < 5:
i += 1
To decrement you would use i -= 1.
Just as a loop is introduced by for, does not imply the same behaviour for different languages.
Python's for loop iterates over objects. Something like the C-for loop does not exist.
The C for loop (for ( <init> ; <cond> ; <update> ) <statement>, however, is actually identical to the C code:
<init>;
while ( <cond> ) {
<statement>
<update>
}
So, with the additional information that Python does have a while loop which behaves like the C-while loop, you should now be able to implement something like the C for loop in Python. I'll leave that as an exercise:-)
Note: as generating an evenly spaced sequence of integer values is a common case, Python provides the range() (Python 3) or xrange() (Python 2) function. This does create a RangeObject which (basically) yields the next value for a sequence given by start, stop and step arguments.
Quick answer
You may use:
for i in range(5):
# code here
or
i = 0
while i < 5:
i = i + 1 # or i += 1
Boring/pedantic answer
When I was learning Python I disliked the syntax; why should a simple for loop require a second keyword, range? The answer, I believe, is due to the fundamental role of the list in Python's prescriptive syntax. Repeated annoyances by range made me think about how the data were described (or not) before the loop, which in turn led me to think more Pythonically about the design of the data.
Let's say you want to populate a list with the first five perfect squares. You could:
squares = []
for i in range(5):
squares.append(i**2)
Alternatively, you could use comprehension:
initial_values = range(5) # we've declared the initial values
squares = [i**2 for i in initial_values]
Or more compactly:
squares = [i**2 for i in range(5)]
I routinely encounter problems where there's no Pythonic way to write the code, and I end up writing C-like Python (as in the Quick answer above). But just as often I find there's a more elegant and readable way to do things, and usually this indicates some imperfections in the antecedent data design.

Python For loops in While loop, starting for loop with a specific value

I've started learning Python a few days ago and I ran into a program while coding something.
Here's what I want to do using C++ code
number = SOME_NUMBER;
while(1) {
for(int i=number; i<sizeOfArray; i++) {
// do something
}
number = 0;
}
Basically, for the very first iteration of my for loop, I want to start i at number.
Then for every other time i go through the for loop, I want to start it at 0.
My kind of hacky idea that I can think of right now is to do something like:
number = SOME_NUMBER
for i in range(0, len(array)):
if i != number:
continue
// do something
while True:
for i in range(0, len(array)):
// do something
Is this the best way or is there a better way?
what is the problem with this?
starting_num = SOME_NUMBER
while True:
for i in xrange(starting_num, len(array)):
# do code
starting_num = 0
it does exactly what you want.
however, i think there are better ways to do things especially if the solution seems "hacky".
if you gave an idea of what you wanted to do, maybe there is a better way
I don't see why you couldn't just do the same thing you are in C:
number = SOME_NUMBER
while True:
for i in range(number, len(array)):
# do something
number = 0
BTW, depending on which version of Python you're using, xrange may be preferable over range. In Python 2.x, range will produce an actual list of all the numbers. xrange will produce an iterator, and consumes far less memory when the range is large.
In Python, stepping over a collection in the traditional sense is not ideal. The ability to loop - to iterate - over an object is controlled by the object, so you don't need to manually step through counters as you would in the for loop in C++.
As I understand it, what you are trying to do here is execute the same piece of code over each item in a list (there are no arrays in Python), a multiple number of times.
To do that:
def whatever_function(foo):
# some code here that works on each item on the list
# foo is an item of the list
while True:
map(whatever_function, some_list)

Python IndentationError - How to refactor?

I am doing a Project Euler question for programming practice in order to self-teach myself. I know perfectly well how to do the question mathematically, as well as how to do it programmatically.
However, I have to have come up with some insane code to do it; 100 nested loops and Python hilariously raises this error, and probably rightfully so, on 100 levels of indentation:
IndentationError: too many levels of indentation
tally = 0
ceiling = 100
for integer_1 in range(0, 100, 1):
for integer_2 in range(0, 100 - integer_1, 2):
for integer_3 in range(0, 100 - integer_1 - integer_2, 3):
for integer_4 ....
for integer_5 ....
etc.
etc.
all the way to integer_100
I have looked through google for solutions but this issue is so rare it has almost no literature on the subject and I could only find this other stack overflow question ( Python IndentationError: too many levels of indentation ) which I could not find much useful in for my question.
My question is - is there a way to take my solution and find some workaround or refactor it in a way that has it work? I am truly stumped.
EDIT:
Thanks to nneonneo's answer, I was able to solve the question. My code is here just for future reference of people looking for ways to properly refactor their code.
from time import time
t = time()
count_rec_dict = {}
# for finding ways to sum to 100
def count_rec(cursum, level):
global count_rec_dict
# 99 is the last integer that we could be using,
# so prevent the algorithm from going further.
if level == 99:
if cursum == 100:
return 1
else:
return 0
res = 0
for i in xrange(0, 101-cursum, level+1):
# fetch branch value from the dictionary
if (cursum+i, level+1) in count_rec_dict:
res += count_rec_dict[(cursum+i, level+1)]
# add branch value to the dictionary
else:
count_rec_dict[(cursum+i, level+1)] = count_rec(cursum+i, level+1)
res += count_rec_dict[(cursum+i, level+1)]
return res}
print count_rec(0, 0)
print time() - t
which runs in an astonishing 0.041 seconds on my computer. WOW!!!!! I learned some new things today!
A recursive solution should do nicely, though I'm certain there is an entirely different solution to the problem that doesn't require this kind of manipulation.
def count_rec(cursum, level):
if level == 100:
return 1
res = 0
for i in xrange(0, 100-cursum, level+1):
res += count_rec(cursum+i, level+1)
return res
print count_rec(0, 0)
Interestingly enough, if you memoize this function, it will actually have a reasonable running time (such is the power of dynamic programming). Have fun!
One way to avoid the indentation error is to put the loops in separate functions, each one nested only one level deep.
Alternatively, you could use recursion to call a function over and over again, each time with a smaller range and higher nesting level.
That being said, your algorithm will have an impossibly long running time no matter how you code it. You need a better algorithm :-)
To do this using exactly your algorithm (restricting each next number to one that can possibly fit in the required sum), you really do need recursion - but the true brute force method can be a one-liner:
sum(sum(i) == 100 for i in itertools.product(xrange(100), repeat=100))
Naturally, this will be a fair bit slower than a true refactoring of your algorithm (in fact, as mentioned in the comments, it turns out to be intractable).
The most effective solution is based on the idea of arithmetic carrying.
You have lists of maximum values and steps,
and also a list of current values. For each time you want to update those 100 variables, you do this:
inc_index = -1
currentvalue[inc_index] += stepval[inc_index]
# I use >= rather than > here to replicate range()s behaviour that range(0,100) generates numbers from 0 to 99.
while currentvalue[inc_index] >= maxval[inc_index]:
currentvalue[inc_index] = 0
inc_index -= 1
currentvalue[inc_index] += stepval[inc_index]
# now regenerate maxes for all subordinate indices
while inc_index < -1:
maxval[inc_index + 1] = 100 - sum (currentvalue[:inc_index])
inc_index += 1
When an IndexError is raised, you've finished looping (run out of 'digits' to carry into.)

Categories

Resources