How do I write instructions inside map in Python? - python

The input is a list: ["asdf", "ghjk", "lmnop", "zyx"].
I specifically need to apply a function over all elements of the list (so, I should use map). The function should do something like f(x, k) = x + k, where k is a constant equal to 1, and x is a counter (hence, x will be 0 at first, and when gradually iterating over the list, it should increment somehow, but how?).
Assuming these, the expected output should be: [1, 2, 3, 4].
from functools import partial
def f(x, k):
return x + k
def with_partial(my_list, k=1):
func = partial(f, k=k) # partial object which acts as a function when called
x = 0 # counter
# map -> applies a funciton over all elements of my_list
rez = list(map(lambda it: (func(x)), my_list))
# if I want to increment it, it should look like: rez = list(map(lambda it: x = (func(x)), my_list))
print("Result with partial: " + str(rez))
if __name__ == "__main__":
with_partial([5, 6, 7])
with_partial(["asdf", "ghjk", "lmnop", "zyx"])
But the output is:
Result with partial: [1, 1, 1]
Result with partial: [1, 1, 1, 1]
But I want it to be like:
Result with partial: [1, 2, 3]
Result with partial: [1, 2, 3, 4]
I can use list comprehension quite easily for that type of problem, but I need to understand how can I write instructions inside map, so that I can increment my counter effectively. Or... Is there anything about partial function I should know already, but I don't? How do I solve this type of problem using functional programming?

Related

Map returns a list of all None

I am trying to write a simple python code which would find a list k nearest elements to origin using a max heap. My main question is around the usage of the map in python, I tried to code it in this way, and I am getting a list of Nones as output whereas I was expecting max_heap to be populated with values pushed by heappush/heappushpop. Can someone point what's wrong here? Also, I am wondering if I really need to declare max_heap empty list? Is there a way in which I can write this entire logic in 1 statement? Thanks!
def find_closest_k_nums(nums, k):
max_heap = []
for num in nums:
if len(max_heap) == k + 1:
heapq.heappushpop(max_heap, -num)
else:
heapq.heappush(max_heap, -num), nums
return [-s for s in heapq.nlargest(k, max_heap)]
# >>> find_closest_k_nums([1, 5, 6, 3, 8, 9, 10], 4) => [1, 3, 5, 6]
# I tried to write the above function using map but I got an empty list.
def find_closest_k_nums_with_map(nums, k):
max_heap = []
map(lambda x: heapq.heappushpop(max_heap, -x)
if len(max_heap) == k + 1 else heapq.heappush(max_heap, -x), nums)
return [-s for s in heapq.nlargest(k, max_heap)]
# >>> find_closest_k_nums_with_map([1, 5, 6, 3, 8, 9, 10], 4) => []
map returns an iterable which calls the function on demand as you ask for elements from the iterable. More simply:
>>> def increment(x):
... print(f"Calling increment on {x}")
... return x + 1
...
>>> x = [1,2,3]
>>> y = map(increment, x)
Until you iterate over y, increment is never called. Only as you call next on y does increment get called.
>>> next(y)
Calling increment on 1
2
>>> next(y)
Calling increment on 2
3
In order for the elements of nums to be added to your heap in the your second function, you need to (somehow) iterate over the elements that map will yield. For example, pass the map object to list to force iteration:
def find_closest_k_nums_with_map(nums, k):
max_heap = []
list(map(lambda x: heapq.heappushpop(max_heap, -x)
if len(max_heap) == k + 1 else heapq.heappush(max_heap, -x), nums))
return [-s for s in heapq.nlargest(k, max_heap)]
But this is terrible style. You aren't actually interested in the return value of the function getting mapped over nums, only its side effect of updating max_heap. When that is the case, just use a for loop as in your first function.

Repeating same code block for creating different values

I'm making a program that basically calculates the missing values (x in this example) in multiple lists.
These are the lists:
L11=[1,3,5,'x',8,10]
L12=['x',3,3,'x',6,0]
L21=[6,1,1,9,2,2]
L22=[1,1,1,'x','x','x']
For example, I'm using this code block to find the x values in L22:
#How to find x:
#1--> a= calculate the sum of integers in the list
#2--> b=calculate the average of them
#3--> all values of x inside the list equal b
a22=L22.count('x')
for i in range(len(L22)):
if L22[i]=='x':
x_L22=round((sum([int(k) for k in L22 if type(k)==int]))/(len(L22)-a22))
So we find x_L22=1 and the new L22 is:
x_L22=1
L22=[1,1,1,1,1,1]
Now here is my question, I want to repeat this steps for all other lists without writing the same code. Is this possible?
Other answers focus on extracting your current code to a generic function which is useful but isn't neither sufficient nor necessary to apply the same piece of code on multiple input.
The only thing you need is to loop over your pieces of data :
L11=[1,3,5,'x',8,10]
L12=['x',3,3,'x',6,0]
L21=[6,1,1,9,2,2]
L22=[1,1,1,'x','x','x']
inputs = ( L11, L12, L21, L22 )
for input in inputs :
# your 4 previous lines on code, modified to work
# on the generic "input" instead of the specific "L22"
a=input.count('x')
for i in range(len(input)):
if input[i]=='x':
x=round((sum([int(k) for k in input if type(k)==int]))/(len(input)-a))
# or if you've extracted the above code to a function,
# just call it instead of using the above 4 lines of code.
try putting it in a function like this:
def list_foo(list_):
counted=list_.count('x')
for i in range(len(list_)):
if list_[i]=='x':
total=round((sum([int(k) for k in list_ if type(k)==int])) \
/(len(list_)-counted))
return total
use it in your main loop
x_L22 = list_foo(L22)
or x_L11 = list_foo(L11)
This is an excellent use case for functions in Python
def get_filled_list(list_of_numbers):
#How to find x:
#1--> a= calculate the sum of integers in the list
#2--> b=calculate the average of them
#3--> all values of x inside the list equal b
new_list=list_of_numbers.count('x')
for i in range(len(list_of_numbers)):
if list_of_numbers[i]=='x':
list_of_numbers = round(
(sum([int(k)
for k in list_of_numbers if type(k)==int]))/
(len(list_of_numbers)-new_list)
)
A11 = get_filled_list(L11)
# ,..
I'd write a function that receives a list as an input and returns the same list with the 'x' value replaced with a new value:
def calculateList(l):
nrX=l.count('x')
newList = []
for elem in l:
if elem == 'x':
x = int(round((sum([int(k) for k in l if type(k)==int]))/(len(l)-nrX)))
newList.append(x)
else:
newList.append(elem)
return newList
You can then call this function on all the list you have:
newL = calculateList(L22)
print(newL)
Output is:
[1, 1, 1, 1, 1, 1]
Or if you prefer you can create a list containing all the lists you want to evaluate:
allLists = [L11, L12, L21, L22]
And then you iterate over this list:
for l in allLists:
newL = calculateList(l)
print(newL)
Output is:
[1, 3, 5, 5, 8, 10]
[3, 3, 3, 3, 6, 0]
[6, 1, 1, 9, 2, 2]
[1, 1, 1, 1, 1, 1]

How to use functional programming to iterate and find maximum product of five consecutive numbers in a list?

I have to use functional programming to implement the following function takes in a list of numbers from 0 to 9. The goal is to find the five consecutive elements of the list that have the greatest product. The function should return tuple of the index of the greatest product and the value of the greatest product without using the max function.
I can easily implement this without functional programming but I am having trouble implementing it without any loops.
This is my approach so far but the part that I am stuck on is how to loop through the array to find those consecutive five numbers without loops. I am trying to use map to do that but I don't think it is correct. Is it possible to incorporate enumerate in any way? Any help is appreciated.
def find_products(L):
val = map(lambda a: reduce(lambda x,y: x*y, L),L)
print (val)
This doesn't have any explicit loops or call the max function. The function assumes that there're at least five elements in the input list and outputs a tuple (start_index, max_product).
from functools import reduce, partial
import operator
def f(l):
win = zip(l, l[1:], l[2:], l[3:], l[4:])
products = map(partial(reduce, operator.mul), win)
return reduce(lambda x, y: x if x[1] > y[1] else y, enumerate(products))
In [2]: f([1, 2, 3, 4, 7, 8, 9])
Out[2]: (2, 6048)
In [3]: f([2, 6, 7, 9, 1, 4, 3, 5, 6, 1, 2, 4])
Out[3]: (1, 1512)
win = zip(l, l[1:], l[2:], l[3:], l[4:]) creates a sliding window iterator of size 5 over the input list. products = map(partial(reduce, operator.mul), win) is an iterator calling partial(reduce, operator.mul) (translates to reduce(operator.mul, ...)) on every element of win. reduce(lambda x, y: x if x[1] > y[1] else y, enumerate(products)) adds a counter to products and returns the index-value pair with the highest value.
If you need a more general version and/or the input list is large you'd use itertools.islice:
from itertools import islice
def f(l, n=5):
win = zip(*(islice(l, i, None) for i in range(n)))
...
The code above uses a generator expression which is a loop, technically. A pure functional version of that might look like
from itertools import islice
def f(l, n=5):
win = zip(*map(lambda i: islice(l, i, None), range(n)))
...
from functools import reduce #only for python3, python2 doesn't need import
def find_products(L):
if len(L)==0:
return 0
if len(L) <= 5:
return reduce( lambda x,y:x*y, L)
pdts = ( reduce(lambda a,b:a*b,L[pos:pos+5]) for pos in range(len(L)-4)) # or pdts = map(lambda pos: reduce(lambda a,b:a*b,L[pos:pos+5],0),range(len(L)-4))
mx = reduce(lambda x,y: x if x>y else y, pdts)
return mx
pdts contains all the possible 5 tuple products, and then using reduce to mimic the max function, we find the maximum among the products.
You could do the following:
For each start index in range(0, len(L) - 5)
Map the index to the tuple of start and the product of items L[start:start + 5]
Reduce the tuples to the one that has the highest product
Get the first value of the resulting tuple = the start index of the 5 elements that have the highest product
Return the slice L[result:result + 5]
This algorithm could be further improved to avoid re-calculating sub-products, but use a "rolling product", that is updated as you reduce from left to right, dividing by the element that was dropped, and multiplying by the new element that was added.
Here is a Haskell solution, which is purely functional:
import Data.List
multiply :: [Int] -> Int
multiply = foldr (*) 1
consecutiveProducts :: [Int] -> [(Int,Int)]
consecutiveProducts xs =
[(i,multiply $ take 5 h) | (i,h) <- zipped, length h >= 5]
where
indices = reverse [0..(length xs)]
zipped = zip indices (tails xs)
myComp (i1,h1) (i2,h2) = compare h2 h1
main = print $ head $ sortBy myComp $ consecutiveProducts [4,5,3,1,5,3,2,3,5]
Here is what it does:
Starting in the last line, it computes the consecutive products from that list.
tails xs gives all the subsets starting with different starting values:
> tails [4,5,3,1,5,3,2,3,5]
[[4,5,3,1,5,3,2,3,5],[5,3,1,5,3,2,3,5],[3,1,5,3,2,3,5],[1,5,3,2,3,5],[5,3,2,3,5],[3,2,3,5],[2,3,5],[3,5],[5],[]]
From these tails we only take those that are at least 5 elements long.
Then we zip them with natural numbers such that we have the starting index associated with it.
From each of the subsets we take the first five elements.
These five elements are passed to the multiply function. There those are reduced to a single number, the product.
After that we go back to the last line, we sort the list by the product value descending.
From the resulting list we only take the first element.
And then we print the result, which is (5,450) for my input data.
This solution uses reduce to calculate a 5-value product, list comprehension for generating all of those products, tuple creation for having the index to go with each, reduce again to get the best tuple.
An if else operator is used to catch the case when there are no 5 values in the input.
from functools import reduce
def find_products(values):
return None if len(values) < 5 else reduce(
lambda best, this: this if this[1] > best[1] else best,
[(i, reduce(lambda a,b: a*b, values[i:i+5], 1)) for i in range(0, len(values)-4)]
)
result = find_products([1, 0, 8, 3, 5, 1, 0, 2, 2, 3, 2, 2, 1])
print (result)
Output for the example call is:
(7, 48)
A Pure Python Solution using recursion
First, we need to create a recursive function to find the product of a list:
def product(l, i=0, s=1):
s *= l[i]
if i+1 < len(l):
return product(l, i+1, s)
return s
which we can do some tests for:
>>> product([1, 2, 3])
6
>>> product([1, 1, 1])
3
>>> product([2, 2, 2])
8
Then, we can use this function in another recursive function to solve your problem:
def find_products(l, i=0, t=(0, -1)):
p = product(l[i:i+5])
if p > t[1]:
t = (i, p)
if i+5 < len(l):
return find_products(l, i+1, t)
return t
which works!
Here are some tests to show it working:
>>> find_products([1, 1, 5, 5, 5, 5, 5, 1, 1])
(2, 3125)
>>> find_products([1, 1, 1, 1, 1, 0, 0, 0, 0])
(0, 1)
>>> find_products([1, 4, 5, 2, 7, 9, 3, 1, 1])
(1, 2520)
want one liner using max and without max try this
from numpy import prod
l=[2,6,7,9,1,4,3]
max([prod(l[i:i+5]) for i in range(len(l))])
sorted([prod(l[i:i+5]) for i in range(len(l))])[-1] // without max
Imperative paradigm is often:
state = state0
while condition:
# change state
This is the "natural" way of programming for lot of people and you know how do that in this way.
The pure functional paradigm forbid variables, which have some advantages . It works with functions which communicates through parameters(IN) and return values(OUT). It frequently uses recursive functions.
A generic functional recursive scheme is :
f = lambda *args : result(*args) if condition(*args) else f(*newparams(*args))
Here we can find a solution with (l,i,imax,prodmax) as parameters, and:
condition = lambda l,i,_,__ : i>=len(l)-5
result = lambda _,__,*args : args
newparams = lambda l,i,imax,prodmax: (l, i+1, imax, prodmax) \
if l[i]*l[i+1]*l[i+2]*l[i+3]*l[i+4] <= prodmax \
else (l, i+1, i, l[i]*l[i+1]*l[i+2]*l[i+3]*l[i+4])
None other than functions have been defined.
You can even define no functions to do that, see here for example, but readability suffers even more.
Run :
In [1]: f([random.randint(0,9) for i in range (997)],0,0,0)
Out[1]: (386, 59049)
Python limits this approach by setting recursive depth to 2000, and from Python 3, by hiding functional tools in the module functools.

Creating a recursive function that gets the sum of all possible subsets in a list of integers

If i were to get the sum of all possible subset-combinations in the list [1,2,3] i would use the code below:
def f():
for i in range(2):
for j in range(2):
for k in range(2):
x = i*1 + j*2 + k*3
print x
f()
How can i make a recursive function that does this for any list?
I can solve this using itertools.combinations but i would like to learn the recursive way.
Thanks
Let's write a recursive function to output all combinations of all subsets of a list.
For a given list, the combinations are the the list itself, plus all combinations of the list minus each member. That's easy to translate straight to Python:
def combinations(seq):
yield seq
for i in range(len(seq)):
for combination in combinations(seq[:i] + seq[i+1:]):
yield combination
However, this will obviously yield duplicates. For example, the list [1, 2, 3] contains both [1, 2] and [1, 3], and they both contain [1]. So, how do you eliminate those duplicates? Simple, just tell each sub-list how many elements to skip:
def combinations(seq, toskip=0):
yield seq
for i in range(toskip, len(seq)):
for combination in combinations(seq[:i] + seq[i+1:], i):
yield combination
Now, you want to sum all combinations? That's easy:
>>> a = [1, 2, 3]
>>> map(sum, combinations(a))
[6, 5, 3, 0, 2, 4, 1, 3]
def allsums(a):
x = a[0]
if len(a) > 1:
yy = allsums(a[1:])
return set(x + y for y in yy).union(yy)
else:
return set([0, x])

Python generator with external break condition

I need to iterate over ascending sequences x of n (= 5, f.i.) integers, finding all sequences for which a function f(*x) returns True.
Assume that if f_n(*y) is False for a particular y, then f_n(*z) id False for any z with z_i >= y_i. So f_n is monotonic in all its arguments.
This kind of generator function could be used in the following way to determine all ascending sequences of integers that have a sum of squares < 100
for sequence in generate_sequences(5):
if sum_squares_is_at_least(sequence, 100):
# some code to trigger the breaking of the generator loop
else:
print sequence
Clarification:
The problem here is that we need to iterate of n elements individually. Initially, we iterate [1,1,1,1,1] to [1,1,1,1,x], and then we have to continue with [1,1,1,2,2] to [1,1,1,2,y], eventually ending with [a,b,c,d,e]. It seems that the generator should look something like this, but needs some code to break out of the for and/or while loops if necessary (determined externally):
def generate_sequences(length, minimum = 1):
if length == []:
yield []
else:
element = minimum
while True:
for sequence in generate_sequences(length - 1, element):
yield element + [sequence]
element += 1
Example:
For n = 3, and sum of squares no larger than 20, the following sequences would be generated:
[1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 1, 4], [1, 2, 2], [1, 2, 3], [1, 3, 3], [2, 2, 2], [2, 2, 3]
Note that in the general case, I cannot use the information that 4 is the upper bound for each element. This would also seriously impact the running time for larger examples.
Are you looking for itertools.takewhile?
>>> from itertools import takewhile
>>> def gen(): #infinite generator
... i=0
... while True:
... yield range(i,i+5)
... i = i+1
...
>>> [ x for x in takewhile( lambda x:sum(x)<20, gen() ) ]
[[0, 1, 2, 3, 4], [1, 2, 3, 4, 5]]
>>>
import itertools as it
it.takewhile(lambda x: sum_squares_is_at_least(x, 100), generate_sequences(5))
If you are now sure about the 5 in the generate_sequences, then just let it yield the numbers as long as it is called:
def generate_sequences():
i = 0 # or anything
while True:
yield [i, i] # or anything
i = i + 1 # or anything
Then use it this way:
it.takewhile(lambda x: sum_squares_is_at_least(x, 100), generate_sequences())
I would solve it with recursion by starting with a given list then appending another number (with logic to prevent going over sum of squares target)
def makegen(N): #make a generator with max sumSquares: N
def gen(l=[]): #empty list is valid with sum == 0
yield l
if l:
i = l[-1] #keep it sorted to only include combinations not permutations
else:
i = 1 #only first iteration
sumsquare = sum(x*x for x in l) #find out how much more we can add
while sumsquare + i*i < N: #increase the appended number until we exceed target
for x in gen(l+[i]): #recurse with appended list
yield x
i += 1
return gen
calling our generator generator (tee hee :D) in the following fashion allows us to have any maximum sum of squares we desire
for x in makegen(26)():
print x

Categories