Python reduce & Lambda with multiple params - python-3.x

Is There any way by which I could use 3 variables in reduce(lambda f) combination like
reduce(lambda a, b, c : a*b+c, <list_for_a&b>, <range_for_c>)
I can use map() function. But thought of knowing a new possibility, if any.

You can zip the two sequences and them work on the result.
values_for_a_and_b = ... # I assume this is a list of tuples
values_for_c = ... #
product = zip(values_for_a_and_b, values_for_c) # This gives a list like [((a, b), c)]
Now it looks like you're trying to do a map, rather than a reduce.
You can use the product as follows:
map(lambda x: x[0][0] * x[0][1] + x[1], product)
But since Python doesn't have pattern matching, it's not really elegant.

Related

python 'concatenate' requires extra parentheses

I'm trying to concatenate 3 lists. When I try to use concatenate, like so, I get an error (TypeError: 'list' object cannot be interpreted as an integer):
import numpy as np
a = [1]
b = [2]
c = [3]
z = np.concatenate(a, b, c)
But if I put "extra" parentheses, it works like so:
z = np.concatenate((a, b, c))
Why?
I am not sure what library you are using (concatenate is not a built-in python 3.x function). However, I'll explain what I think is going on.
When you call concatenate(a, b, c), the function concatenate is sent three parameters: a, b, and c. concatenate then performs some logic that is (presumably) not the desired behavior.
When you call concatenate((a, b, c)), a tuple (effectively a list that cannot be changed) is created with a value of (a, b, c), which is evaluated to ([1], [2], [3]). Then this tuple is passed to the concatenate function. The following code is actually equivalent to your second code snippet:
a = [1]
b = [2]
c = [3]
y = (a, b, c) # This evaluates to ([1], [2], [3]).
z = concatenate(y)
I hope I've explained this clearly enough. Here's an article that explains tuples in more depth, if I haven't: https://www.w3schools.com/python/python_tuples.asp
EDIT: Thanks for including the library. Here's the code for what you're probably trying to do:
import numpy as np
a = [1]
b = [2]
c = [3]
z = np.array(a + b + c) # Lists can be concatenated using the `+` operator. Then, to make a numpy array, just call the constructor

How to iterate a dynamic nested loop over a particular values as its range, in python?

If I take a boolean expression and number of input variables from user, then how can I evaluate the expression (to create its truth table) by using dynamic nested loops instead of doing it like this:
expression= "a and (b or a)"
inputs=2
if inputs==2:
for a in range(0,2):
for b in range(0,2):
x = eval(expression)
print(a,b,x)
if inputs==3:
for a in range(0,2):
for b in range(0,2):
for c in range(0,2):
x = eval(expression)
print(a,b,x)
It limits the number of variables user can evaluate expression on, as I can not write loops manually. I tried using itertools.product() for this, but I don't know how to give values to the iterating variables.
from itertools import product
variables=['a','b','c']
for items in product(*variables):
rows=(eval(expression))
print(rows)
As you can see, it obviously gives error that a,b,c are undefined in eval(expression). How can I iterate each one of them over [0,1] ?
You could do it using itertools.product() simply
import itertools
inputs = int(input())
temp = [[0, 1]] * inputs
for combination in itertools.product(*temp):
print(combination)
I think you were on the right path here and itertools.product() is what you are looking for. The following is a one-liner, but you could expand it if you want. Note that I am not using eval, but rather a list comprehension paired with the already mentioned itertools.product(). Note also that you can use and, or and () in normal code.
import itertools
rows = [a and (b or c) for a, c, b in itertools.product([True, False], [True, False], [True, False])]
print(rows)
If you are using user input, it might make sense to define truthy values.
Also if you want the inputs for the truth-table, you can also do this first:
import itertools
inputs = [[a, b, c] for a, b, c in itertools.product([True, False], [True, False], [True, False])]
rows = [a and (b or c) for a, c, b in inputs]
print(rows)
Edit: If you have a lot of inputs, the logical expression would likely include any() and all().
Edit2: Now I understand what you mean. Here is a full version with a dynamic number of inputs using the above-mentioned any() and all(). In this example of a logical condition, the first half of the array (rounded down if it is an uneven length) needs to be true and at least one of the second half. Feel free to adapt the expression as you see fit. Note that your original expression does not make much sense. If the first part a is Truthy then the second part where you check for a or b is necessarily also Truthy because we already checked for a. That is why I picked a slightly modified version.
import itertools
input_num = int(input())
arrs = [[False, True]] * input_num
inputs = [x for x in itertools.product(*arrs)]
rows = [all(x[:int(len(x)/2)]) and any(x[int(len(x)/2):]) if len(x) > 1 else bool(x) for x in inputs ]
print(rows)

Lambda functions with multiple conditions

Consider the following code snippet.
## Heading ##
def pos(l):
count_positive=0
for i in l:
if i>0:
count_positive+=1
return count_positive
def even(l):
count_even=0
for i in l:
if i%2==0:
count_even+=1
return count_even
list_int=[[2,-3,-6,10],[10,34,26,87],[1,-2,-3,4]]
list_int=sorted(list_int,key=lambda x: (pos(x),even(x)))
print(list_int)
Lambda functions can't have more than one expression.
But in the code above, I am trying to sort the 2D list first based on number of positive elements and then based on number of even elements. Doesn't this mean that there are two expressions in the lambda function? Or is it like, as I have enclosed the two conditions inside a tuple, it would be considered as one single condition?
Doesn't this mean that there are two expressions in the lambda function?
Yes, there's two expressions, but there's only one expression being returned. When you do
lambda x: (pos(x), even(x))
you're creating a lambda that returns a single value: a tuple with two elements. You can see this by doing
f = lambda x: (pos(x), even(x))
print(f([1, 2, 3, 4])) # Outputs (4, 2)
print(f([-1, -2, -3, -4])) # Outputs (0, 2)
Since it returns a tuple, when you use this as your sorted key, it compares using tuple comparison.
About lambdas, Python documentation says
[Lambdas] are syntactically restricted to a single expression.
It means that you can't do
f = lambda x: pos(x), even(x)
# It'll be parsed as f = ((lambda x: pos(x)), (even(x)))
f = lambda x: return pos(x); return even(x)
# It'll throw a SyntaxError
Or is it like, as I have enclosed the two conditions inside a tuple, it would be considered as one single condition?
No, because this doesn't make sense. A single condition would only be considered if you connect them explicitely with a operator, such as addition:
f = lambda x: pos(x) + even(x)
# Sorts the lists based on the sum of the count of the positive
# numbers with the count of the even numbers
You have one expression - a tuple that calls two functions to get its values. A single "expression" can call multiple functions.
What "Lambda functions can't have more than one expression" means if that you can't have multiple "lines of code" within a lambda, meaning you can't do something like:
lambda x: print(x) return x+1

create a dictionary/list with two pairs of key : value / item which have iterating forms

I want to get a dictionary and list like
{f1(0):g1(0), f2(0):g2(0),f1(1):g1(1), f2(1):g2(1)}
[f1(0),f2(0),f1(1),f2(1)]
where f1(x), f2(x) and g1(x), g2(x) are some defined functions.
There are two generating groups f1(x):g1(x),f2(x):g2(x) and for i in range(2).
We can define this dictionary by following code
new_dict = {f1(x):g1(x) for x in range(2)}
new_dict.update({f2(x):g2(x) for x in range(2)})
However list has order and it seems that I have to create such list by for loop.
new_list= list()
for i in range(2)
new_list.append(f1(i))
new_list.append(f2(i))
I wonder whether there is some elegant way like following:
{f1(x):g1(x), f2(x):g2(x) for x in range(2)}
[f1(x) , f2(x) for x in range(2)]
Use a list of the functions in the comprehension.
{f(x): g(x) for x in range(2) for f, g in [(f1, g1), (f2, g2)]}
The list could also possibly be a dict, depending on how much you care about ordering (like if a key might get overwritten) But note that Python 3.7 dicts are now specified to remember the insertion order (and there's always OrderedDict)
new_dict = {f(x): g(x) for x in range(2) for f, g in {f1:g1, f2:g2}.items()}
You can convert the keys to a list like
new_list = [*new_dict]
This will have the same insertion order, but the dict cannot contain duplicate keys, so the list made from it won't either. This may or may not be what you want.
If you do want to keep duplicates, make a list of pairs first, and then use that to make the dict and list.
pairs = [(f(x), g(x)) for x in range(2) for f, g in [(f1, g1), (f2, g2)]]
new_dict = dict(pairs)
new_list = [f for f, _ in pairs]
If you're still using an old version of Python, note that you can also make an OrderedDict from pairs like this,
from collections import OrderedDict
new_dict = OrderedDict((f(x), g(x)) for x in range(2) for f, g in [(f1, g1), (f2, g2)])
I wonder whether there is some elegant way like following:
{f1(x):g1(x), f2(x):g2(x) for x in range(2)}
[f1(x) , f2(x) for x in range(2)]
You can have a varying number of outputs each loop by using chain.
from itertools import chain
[*chain.from_iterable([f1(x), f2(x)] for x in range(2))]
dict(*chain.from_iterable([(f1(x), g1(x)), (f2(x), g2(x))] for x in range(2)))
You can also do the chaining yourself, but it takes more loops.
[f for fs in ((f1(x), f2(x)) for x in range(2)) for f in fs]

List comprehension in typing.Union

Rather than write out all the combinations, I've tried the following:
from typing import Union, Tuple
from itertools import product as prod
class A: pass
v = (A, None)
annot = Union[Tuple[x, y] for x, y in prod(v, v)] # breaks ofc
Obviously this breaks because I'm trying to treat it like a list comprehension. Doing the following (which is similar to the above anyway) breaks also:
X = [Tuple[x, y] for x, y in prod(v, v)]
Union[X] # breaks
Union[*X] # also breaks
Union[*[X]] # breaks again
But is there some way of doing this?
Just for clarity, the end result I want is:
Union[Tuple[A, A], Tuple[A, NoneType], Tuple[NoneType, A], Tuple[NoneType, NoneType]]

Resources