What's the Pythonic way of flipping between two values? - python

EDIT: My original question was a bit unclear, so I have updated it. Sorry for the inconvenience.
Suppose I have some different values: A, B, C, D, ..., and some variable x that is equal to one of these. Furthermore suppose I know that the opposite of A is B, the opposite of C is D, and so on.
What I want is some clean way of mirroring the boolean x = not x but for these generic values. Right now all I can think of is a horrible if-else chain:
if x == A:
x = B
elif x == B:
x = A
elif x == C:
x = D
elif x == D:
x = C
elif ...
To give some indication of what I'm after, in Haskell I could define this as as:
foo :: x -> x
foo A = B
foo B = A
foo C = D
foo D = C
...
and then simply call foo x.

Build a dict with 2-way mappings. Something like
pairs = [(A, B), (C, D), ...]
d = dict([*pairs, *map(reversed, pairs)])
Then x = d[x].
If for whatever reason you don't like the idea of instantiating a full list of the pairs and reversed pairs, you can use itertools.chain:
d = dict(chain(pairs, map(reversed, pairs)))

One way to do it is
x = B if x == A else A

After editing my question, I came to the realization I can do this:
def flip(x):
if x == A: return B
if x == B: return A
if x == C: return D
if x == D: return C
...
Still, if anyone knows of a better way, feel free to post an answer.

Related

Using arguments as variable names: Why does it solve this problem?

I'm going through some material about functions and I'm writing Python code to make some sense of the pseudocode examples.
The goal is printing the variables after I call the function, then check the new values.
def my_sum(x, y, z):
z = x + y
return x, y, z
A = 1
B = 2
C = 0
my_sum(A, B, C)
my_sum(B, C, A)
my_sum(C, A, B)
my_sum(A, B, C)
my_sum(B, C, A)
print(A, B, C)
My first instinct was to write this procedural approach, but when I do the calling the program won't give the right answer, because A, B and C aren't saving whatever is happening inside the function. So A is always 1, B is 2 and so forth
It turns out when I assign the calling with the arguments, the variables A, B and C receive the new values and they're now keeping it. Finally it prints 21, 8, 13, which is the answer.
A, B, C = my_sum(A, B, C)
B, C, A = my_sum(B, C, A)
C, A, B = my_sum(C, A, B)
A, B, C = my_sum(A, B, C)
B, C, A = my_sum(B, C, A)
How would you implement it or what are the other ways of writing this algorithm?
The thing is I can't wrap my head around why this works at all! It was just a random guess that happened to solve my problem.
python don't have pass by reference option, only pass by value, so your construction is correct, because you returning NEW values (in tuple form), not changing value of variables, that you are passing in.
In Python, an assignment made to a parameter name never affects the value of the name that the caller uses. They are separate names that initially reference the same object, but once the parameter name is assigned something else (like a sum), it references a different object.
Your second attempt works because the function returns a tuple with the values of the three paramater names and your main program unpacks that tuple back into its own names.
However, since the function doesn't need the original value of the third argument, and it doesn't touch the first two arguments, the caller doesn't really need to pass the third argument, and doesn't need to update its own names for the first two arguments... So the function could be designed to only take two arguments and return the new value:
def my_sum(x, y):
return x + y
A = 1
B = 2
C = my_sum(A, B)
A = my_sum(B, C)
B = my_sum(C, A)
C = my_sum(A, B)
A = my_sum(B, C)
Lets start with your function definition and one call.
def my_sum(x, y, z):
z = x + y
return x, y, z
A = 1
B = 2
C = 0
my_sum(A, B, C)
Without the function, this is functionally the same as:
A = 1
B = 2
C = 0
x = A
y = B
z = C
z = x + y
_ = x, y, z
# x, y, and z are discarded since you don't do anything with the return value
You shouldn't expect this to change A, B, or C or if you do you have a misconception about how python variables or names work.
Python variables or names are just a dict with a name pointing to a value.
A = 1
B = 2
C = 0
my_sum(A, B, C)
# this is a very condensed version of what python does in the background
dict_of_globals = dict()
dict_of_globals['A'] = 1
dict_of_globals['B'] = 2
dict_of_globals['C'] = 3
my_sum_local_dict = dict()
my_sum_local_dict['x'] = dict_of_globals['A']
my_sum_local_dict['y'] = dict_of_globals['B']
my_sum_local_dict['z'] = dict_of_globals['C']
# and so on..
Since you only ever assign 1 to dict_of_globals['A'], it would be unreasonable to expect it to be anything other than 1.
The reason this works:
A, B, C = my_sum(A, B, C)
is because you are assigning the return value back to A.
A = x # etc..
# or:
dict_of_globals['A'] = my_sum_local_dict['x']

Is there any way to perform this type of recursion in C++ or python?

Let us say I have a function called my_func(a,b,s,t). Suppose, I want a and b to be passed by value, but I want s and t to be passed by reference. As in, I want to some how pass in let us say (4,5,s',t'). The function performs computations by calling my_func(a/2,b/2,s/2,t/2). The thing is, there is a base case at the "bottom" of the recursion that gives concrete values to s and t.
Let me give a mini example:
def e_euclid(a,b,s,t):
if (a == b):
s = 4
t = -3
return a
if (a%2 == 0 and b%2 == 0):
if (s%2 == 0 and t%2 == 0):
return 2*e_euclid(a/2,b/2,s/2,t/2)
else:
return 2*e_euclid(a/2,b/2,(s+b)/2,(t-a)/2)
...
So, I would call this function as e_euclid(a,b, something, something) but then I would have to supply concrete values for s and t. Can you guys kind of see what I'm trying to do here?
Doing recursion where I return (s,t) would lead to a tough computation that I don't wish to perform, so I would like to do it this way.
Your code seems broken, already that base case (?) with a == b and s = 4 and t = -3 doesn't make sense. But see this C++ implementation and my Python translation using single-element lists instead of C++'s references:
def gcd(a, b, x=[None], y=[None]):
if b == 0:
x[0] = 1
y[0] = 0
return a
x1, y1 = [None], [None]
d = gcd(b, a % b, x1, y1)
x[0] = y1[0]
y[0] = x1[0] - y1[0] * (a // b)
return d
a, b = 123, 321
x, y = [None], [None]
print(gcd(a, b, x, y), x, y, a*x[0] + b*y[0])
Output (Try it online!):
3 [47] [-18] 3
I think you're trying to use the binary version of the algorithm, that should be doable the same way.

Python while loop. What is this construct called?

On the Literate Programs site, I was looking at the Python code for the GCD algorithm.
def gcd(a,b):
""" the euclidean algorithm """
while a:
a, b = b%a, a
return b
What is going on in the body? Expression evaluation? Is it a compressed form of another structure?
There are two things going on here:
a, b = b%a, a
First, a tuple is created with the contents (b%a, a). Then the contents of that tuple are unpacked and assigned to the names a and b.
Looks like shorthand for:
while a > 0:
temp = a
a = b%a
b = temp
return b
a is receiving the result of b%a while b is receiving the value of a
Works the same as:
while a > 0:
tmp = a
a = b%a
b = tmp
return b
See this post for more information on switching variables: Is there a standardized method to swap two variables in Python?

Python Homework - Not making sense

OK, our professor explained (kinda) this problem, but it still doesn't make much sense.
Question: Implement the function knice(f,a,b,k) that will return 1 if for some integer a <= x <= b and some integer n <= k, n applications of f on x will be x, (e.g. f(f(f...(f(x)))) = x) and 0 if not.
What the professor provided was:
def knice(f,a,b,k):
f(f(f(...(f(x)))) = x
for i = a to b:
y = f(i)
if y = i break
for j = z to k:
y = f(y)
if y = i break
Personally, that example makes no sense to me, so looking to see if I can get clarification.
OP EDIT 1/19/2012 3:03pm CST
This is the final function that was figured out with the help of the GTA:
def f(x):
return 2*x-3
def knice(f,a,b,k):
x = a
while x <= b:
n = 1
y = f(x)
if y == x:
return 1
while n <= k:
y = f(y)
n=n+1
if y == x:
return 1
x=x+1
return 0
Ignore his code; you should write whatever you feel comfortable with and work out the kinks later.
You want to work out whether
f(a) = a, or f(f(a)) = a, or ..., or f^n(a) = a, or,
f(a+1) = a+1, or f(f(a+1)) = a+1, or ..., or f^n(a+1) = a+1, or,
...
f(b) = b, or f(f(b)) = b, or ..., or f^n(b) = b.
An obvious algorithm should come to mind immediately: try all these values one-by-one! You will need two (nested) loops, because you are iterating over a rectangle of values. Can you now see what to do?
Yeah, I can see why that might be confusing.
Was f(f(f(...(f(x)))) = x wrapped in triple-double-quotes? That's a function documentation string, sort of like commenting your code. It shouldn't have been stand-alone without something protecting it.
Imagine f was called increment_by_one.
Calling increment_by_one 10 times like that on an x of 2 would give 12. No matter how many times you increment, you never seem to get back 2.
Now imagine f was called multiply_by_one.
Calling multiply_by_one 5 times like that on an x of 3 would give 3. Sweet.
So, some example outputs you can test against (you have to write the functions)
knice(increment_by_one, 1, 3, 5) would return 0.
knice(multiply_by_one, 1, 3, 5) would return 1.
As another hint, indentation is important in python.
Here's a concrete example. Start small, and suppose you called knice(f, a=1, b=2, k=1). For k==1, we don't have to worry about iterating the function. The only values of x to consider are 1 and 2, so knice can return 1 (i.e., True) if f(1)==1 or f(2)==2.
Now suppose you called knice(f, a=1, b=2, k=2). You'll have to check f(f(1)) and f(f(2)) as well.
As k gets bigger, you'll have to call f more. And as the range between a and b gets bigger, you'll have to try more values of x as an argument to f.

How to compare multiple variables to the same value?

I am using Python and I would like to have an if statement with many variables in it.
Such as:
if A, B, C, and D >= 2:
print (A, B, C, and D)
I realize that this is not the correct syntax and that is exactly the question I am asking — what is the correct Python syntax for this type of an if statement?
You want to test a condition for all the variables:
if all(x >= 2 for x in (A, B, C, D)):
print(A, B, C, D)
This should be helpful if you're testing a long list of variables with the same condition.
If you needed to check:
if A, B, C, or D >= 2:
Then you want to test a condition for any of the variables:
if any(x >= 2 for x in (A, B, C, D)):
print(A, B, C, D)
Another idea:
if min(A, B, C, D) >= 2:
print A, B, C, D
I'd probably write this as
v = A, B, C, D
if all(i >= 2 for i in v):
print v
If you have ten variables that you are treating as a group like this, you probably want to make them elements of a list, or values in a dictionary, or attributes of an object. For example:
my_dict = {'A': 1, 'B': 2, 'C': 3 }
if all(x > 2 for x in my_dict.values()):
print "They're all more than two!"
How about:
if A >= 2 and B >= 2 and C >= 2 and D >= 2:
print A, B, C, D
For the general case, there isn't a shorter way for indicating that the same condition must be true for all variables - unless you're willing to put the variables in a list, for that take a look at some of the other answers.
Depending on what you are trying to accomplish, passing a list to a function may work.
def foo(lst):
for i in lst:
if i < 2:
return
print lst
Except that she's probably asking for this:
if A >= 2 and B >= 2 and C >= 2 and D >= 2:

Categories