Lambda function and 0 - python

I want to make a function that changes each elements in list with lambda function.
a = [1,5,2,4]
def func1(list,func2):
for x in range(len(list)):
list[x] = func2(list[x])
func1(a,lambda x: x>3 and 10 or x)
print a
The result is [1,10,2,10]
This is OK. But I change '10' to '0'
func1(a,lambda x: x>3 and 0 or x)
The result is [1,5,2,4]
Why doesn't the result be [1,0,2,0]?
I'm sorry that I'm poor at English.

The problem you have is that 0 is being evaluated as False which means that using the and-or trick as a conditional expression is failing.
Python 2.5 introduced "proper" conditional expressions of the form:
x = true_value if condition else false_value
So you can replace:
lambda x: x>3 and 0 or x
with:
lambda x: 0 if x > 3 else x
Also, you could use the map function to replace func1 if you're not bothered about updating the list in place:
a = map(lambda x: 0 if x > 3 else x,a)
print a
If you do want to modify the list in place you can use the enumerate function to simplify your code a little:
def func1(list,func2):
for i,x in enumerate(list):
list[i] = func2(x)

bool(0) -> False
bool(10) -> True

a and b or c
is equivalent (nearly, since your case proves it is not) to
b if a else c
So:
a = [1,5,2,4]
def func1(li,func2):
for x,el in enumerate(li):
li[x] = func2(el)
func1(a,lambda x: 0 if x>3 else x)
print a
Remark:
name list for a user's object is not good
use of iterate()
By the way, did you notice that you are changing in a function the value of an object external to the function ?
u = 102
def f(x):
x = 90
print "u==",u
result
u== 102
In your code, a is changed because it is a mutable object
In common case, a function has a return. Yours has not, because you change a mutable object.

x>3 and 0 or x always returns x because 0 is False.
Replace it with:
(x > 3 and [0] or [x])[0]
to get:
func1(a,lambda x: (x>3 and [0] or [x])[0])
Result:
[1, 0, 2, 0]
How it works:
The real return value is put into a single element list.
Any non-empty list is True, so the and or works.
Then use [0] to get the value back out.

You could turn it around:
func1(a,lambda x: x<=3 and x or 0)
(untested)

Related

Python lambda if statement - do you have to have else?

For example say i have the below list and I want to print out only even #s - i want to use lambda.
My question is do i need to have a else in the if ?
_l = [1, 2, 3, 4, 5, 6]
_l2 = list(map(lambda x: x if x % 2 == 0 else "", _l))
I dont want to use the else ...
That's not an if statement anyway, it's a conditional expression. Python conditional expressions are of the form z if x else y, so yes, you do need the else.
However, it looks like you're really just trying to filter the list, so if you want an one-liner, use a list comprehension with an if fragment:
l2 = [x for x in l if x % 2 == 0]
If you struggle with list comprehension maybe just use filter. Might be easier to grasp if you insist on using lambda and want to write functional programming.
_l2 = list(filter(lambda x: (x % 2 == 0), _l))
Thanks guys - i can def use list comprehension also can use filter - thats what i did (posted in the comment).
I guess what i was really asking is can i have a if statement in lambda without the else ?
So for the above yes i used filter - fundamentally :
_l = [x for x in range(0,100,1)]
_l2 = list (map ( lambda x:x if x%2 == 0 else None, _l ) )
_l3 = list ( filter (None, _l2) )
But my question is Do i need to have the else ?
thanks

What does [value](x) do in this lambda code?

Here's a piece of code from this question, it's an alternative to a switch statement in Python.
result = {
'a': lambda x: x * 5,
'b': lambda x: x + 7,
'c': lambda x: x - 2
}[value](x)
I'm new to Python and I don't understand what it's doing. So I get that the first part is variable assignment - but what does [value](x) do?
The part between the braces {} defines a dictionary, with keys of 'a', 'b' and 'c'. The values for those keys are lambdas, which are anonymous functions (functions with no name). Each lambda defines a mapping of x to a function of x, with three different mappings, one for each key.
'[value]' looks up value in the dictionary. So if value = 'b', it will return lambda x: x + 7. This x is not related to the x in (x). The lambdas could have been defined as lambda y: y + 7 for example.
'(x)' then applies that lambda to whatever the value of x is.
So if value = 'b' and x = 8, the expression above will give 15 as the answer.
It is a convoluted way to replicate a switch statement. A cleaner way would be to define a helper function that will let you use a more straightfoward expression of what's going on:
def switch(v):yield lambda *c: v in c
Which you could use like this to get the equivalent result:
for case in switch(value):
if case('a'): result = x + 5
elif case('b'): result = x + 7
elif case('c'): result = x - 2
That is a very simple example which does little more than make the code more legible. When you have more complex or varied things to do in each case, then this switch pattern becomes a lot more valuable. You can also use it in a more C-Like style:
for case in switch(letter):
if case('a','A'):
y = complexCalculation(12,4)
result = y + 5
print(y)
break
if case('b','c','d'):
z = factorial(y) **2
result = z + 5
print(z)
break
else: # otherwise (all other cases)
print("invalid value provided")

Filtering out odd numbers and squaring them in a tuple in Python

This is a question from a school assignment, and appreciate any assistance in pointing out my mistake.
First of all, with respect to the map() function in Python, I have been tasked (non-optionable) to redefine it as such to make it specific to a tuple:
def map(fn, seq):
if seq == ():
return ()
else:
return (fn(seq[0]),) + map(fn, seq[1:])
Using this, I am supposed to define another function square_odd(tuple) that takes in a tuple containing integers as an argument, and specifically square the terms only if they are odd. Essentially, for a tuple input of (5,6,7,8,9), I should be returned (25, 6, 49, 8, 81).
My idea is simply to utilise lambda to write a function, paired with an if to filter out the odd numbers. Here's what I have:
def square_odd(tpl):
return map(lambda x: x*x if x%2 == 0, tpl)
Python has returned me a SyntaxError at the comma before tpl. Can anyone point out what may be the issue with this? Must I write an else condition as well? I have tried adding else x but I am only returned the integer value of the first value.
That's because you should write else statement.
map(lambda x: x*x if x%2 == 1 else x, tp1)
and tried this code as simple. And also you should fixed x%2 == 0.
This condition will find even number.
return tuple(map(lambda x: x*x if x%2 == 1 else x, tp1))
if with lambda expects an else statement. So it should be like this:
def square_odd(tpl):
return map(lambda x: x if x%2 == 0 else x*x, tpl)
Which means, take x if x%2==0 i.e. if it is even, else take x*x which is it's square.
def map(fn, seq):
if seq == ():
return ()
else:
return (fn(seq[0]),) + map(fn, seq[1:])
def square_odd(tpl):
return map(lambda x: x**2 if x%2 else x, tpl)
t = (1,2,3,4,5)
print(square_odd(t))
I have done this and the output is (1, 2, 9, 4, 25).
PS the syntax error raise because the ternary operator A if condition else B cannot be used as A if condition

Lambda Expressions and Strings

I am just learning about lambda expressions, and I am wondering how I could use it to count the number of vowels in a string. For example,
I am using the following:
result = lambda i, y: i + 1 for x in y if x in "aeoiuAEIOU"
print(result(0,s)
However, I recieve a syntax error.
Also, just to clarify my understanding, a lambda expression returns the expression following the colon. Could someone please point me in the right direction?
It's just the parse that's ambiguous. Consider
result = lambda i, y: (i + 1 for x in y if x in "aeoiuAEIOU")
Of course, now it will return a generator object, which won't print as nicely. If you want a list, you might try
result = lambda i, y: [i + 1 for x in y if x in "aeoiuAEIOU"]
Thank you Silvio for your help! Populating a list like you suggested gave me an answer that wasn't quite as clear as I wanted as it returned
[1, 1, 1, 1, 1]
While a generator returned
<generator object <lambda>.<locals>.<genexpr> at 0x0000028884166C00>
In the end, I used sum on the generator to get
result = lambda i, y: sum(i + 1 for x in y if x in "aeoiuAEIOU")
print(result(0, s))
Which gave me the answer of 5.
To me this is an improper use of lambda to begin with, as lambda's are supposed to be function definitions with no name or more or less a throw away function. Also the reason you got a Syntax Error is your generator expression is incorrect, use sum for something like this.
result = lambda y: sum(1 for x in y if x in "aeoiuAEIOU")
You can even use map for this:
result = lambda y: sum(map(y.count, "aeoiuAEIOU"))
But this could be done more legibly and imo more correctly with an actual function definition and you can include more statements
#more appropriately named count_vowels vs result and s vs y
def count_vowels(s):
_s = s.lower()
return sum(map(_s.count, 'aeiou')
You can also use re (regex) for this too:
import re
def count_vowels(s): return len(re.findall("[aeiou]", s, re.I))
The re.I ignores case so no need to worry about capsvs uncaps
You can also just use one parameter in lambda:
result = lambda y: sum(x in "aeoiuAEIOU" for x in y)
and the syntax error is not special with lambda:
In [10]: def bar(i, y):
...: return i + 1 for x in y if x in "aeoiuAEIOU"
File "<ipython-input-10-96fcd0959145>", line 2
return i + 1 for x in y if x in "aeoiuAEIOU"
^
SyntaxError: invalid syntax
the real issue is that i + 1 for x in y if x in "aeoiuAEIOU" is an invalid expression in Python.

For loops (novice)

I recently started learning Python, and the concept of for loops is still a little confusing for me. I understand that it generally follows the format for x in y, where y is just some list.
The for-each loop for (int n: someArray)
becomes for n in someArray,
And the for loop for (i = 0; i < 9; i-=2) can be represented by for i in range(0, 9, -2)
Suppose instead of a constant increment, I wanted i*=2, or even i*=i. Is this possible, or would I have to use a while loop instead?
As you say, a for loop iterates through the elements of a list. The list can contain anything you like, so you can construct a list beforehand that contains each step.
A for loop can also iterate over a "generator", which is a small piece of code instead of an actual list. In Python, range() is actually a generator (in Python 2.x though, range() returned a list while xrange() was the generator).
For example:
def doubler(x):
while True:
yield x
x *= 2
for i in doubler(1):
print i
The above for loop will print
1
2
4
8
and so on, until you press Ctrl+C.
You can use a generator expression to do this efficiently and with little excess code:
for i in (2**x for x in range(10)): #In Python 2.x, use `xrange()`.
...
Generator expressions work just like defining a manual generator (as in Greg Hewgill's answer), with a syntax similar to a list comprehension. They are evaluated lazily - meaning that they don't generate a list at the start of the operation, which can cause much better performance on large iterables.
So this generator works by waiting until it is asked for a value, then asking range(10) for a value, doubling that value, and passing it back to the for loop. It does this repeatedly until the range() generator yields no more values.
Bear in mind that the 'list' part of the Python can be any iterable sequence.
Examples:
A string:
for c in 'abcdefg':
# deal with the string on a character by character basis...
A file:
with open('somefile','r') as f:
for line in f:
# deal with the file line by line
A dictionary:
d={1:'one',2:'two',3:'three'}
for key, value in d.items():
# deal with the key:value pairs from a dict
A slice of a list:
l=range(100)
for e in l[10:20:2]:
# ever other element between 10 and 20 in l
etc etc etc etc
So it really is a lot deeper than 'just some list'
As others have stated, just set the iterable to be what you want it to be for your example questions:
for e in (i*i for i in range(10)):
# the squares of the sequence 0-9...
l=[1,5,10,15]
for i in (i*2 for i in l):
# the list l as a sequence * 2...
You will want to use list comprehensions for this
print [x**2 for x in xrange(10)] # X to the 2nd power.
and
print [x**x for x in xrange(10)] # X to the Xth power.
The list comprehension syntax is a follows:
[EXPRESSION for VARIABLE in ITERABLE if CONDITION]
Under the hood, it acts similar to the map and filter function:
def f(VARIABLE): return EXPRESSION
def c(VARIABLE): return CONDITION
filter(c, map(f, ITERABLE))
Example given:
def square(x): return x**2
print map(square, xrange(10))
and
def hypercube(x): return x**x
print map(hypercube, xrange(10))
Which can be used as alternative approach if you don't like list comprehensions.
You could as well use a for loop, but that would step away from being Python idiomatic...
Just for an alternative, how about generalizing the iterate/increment operation to a lambda function so you can do something like this:
for i in seq(1, 9, lambda x: x*2):
print i
...
1
2
4
8
Where seq is defined below:
#!/bin/python
from timeit import timeit
def seq(a, b, f):
x = a;
while x < b:
yield x
x = f(x)
def testSeq():
l = tuple(seq(1, 100000000, lambda x: x*2))
#print l
def testGen():
l = tuple((2**x for x in range(27)))
#print l
testSeq();
testGen();
print "seq", timeit('testSeq()', 'from __main__ import testSeq', number = 1000000)
print "gen", timeit('testGen()', 'from __main__ import testGen', number = 1000000)
The difference in performance isn't that much:
seq 7.98655080795
gen 6.19856786728
[EDIT]
To support reverse iteration and with a default argument...
def seq(a, b, f = None):
x = a;
if b > a:
if f == None:
f = lambda x: x+1
while x < b:
yield x
x = f(x)
else:
if f == None:
f = lambda x: x-1
while x > b:
yield x
x = f(x)
for i in seq(8, 0, lambda x: x/2):
print i
Note: This behaves differently to range/xrange in which the direction </> test is chosen by the iterator sign, rather than the difference between start and end values.

Categories

Resources