Lambda Expressions and Strings - python

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.

Related

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")

What does this Lambda function mean?

def largestNumber(self, num):
num = [str(x) for x in num]
num.sort(cmp=lambda x, y: cmp(y+x, x+y))
return ''.join(num).lstrip('0') or '0'
What I would like to know is what is exactly is happening in the num.sort line where the lambda function takes in an x and y and does this cmp() function. This is the specific question that the code is being used for if anyone wants to see this as well https://leetcode.com/problems/largest-number/#/description
I'd recommend you to use Visualize python with the following code:
class Solution:
def largestNumber(self, num):
num = [str(x) for x in num]
num.sort(cmp=lambda x, y: cmp(y+x, x+y))
return ''.join(num).lstrip('0') or '0'
print Solution().largestNumber([4,6,2,7,8,2,4,10])
To get the concept clear,
num = [str(x) for x in num]
This takes every element of list num as x and converts it to a string and the result list is stored as num
num.sort(cmp=lambda x, y: cmp(y+x, x+y))
cmp:
cmp(x, y)ΒΆ
Compare the two objects x and y and return an integer according to the outcome. The return value is negative if x < y, zero if x == y and
strictly positive if x > y.
Lambda Expression:
Small anonymous functions can be created with the lambda keyword. This
function returns the sum of its two arguments: lambda a, b: a+b.
Hope this helps!
Yes, the lambda function does exactly the comparisons you suggest: "998" to "989", etc. This way, the list items are sorted into lexicographic order, largest first. You have to worry about the concatenated combinations to get the proper order for largest numbers: '9' vs '98' is a typical example of why you need concatenation, as you want "998" in the result.

Using math.factorial in a lambda function with reduce()

I'm attempting to write a function that calculates the number of unique permutations of a string. For example aaa would return 1 and abc would return 6.
I'm writing the method like this:
(Pseudocode:)
len(string)! / (A!*B!*C!*...)
where A,B,C are the number of occurrences of each unique character. For example, the string 'aaa' would be 3! / 3! = 1, while 'abc' would be 3! / (1! * 1! * 1!) = 6.
My code so far is like this:
def permutations(n):
'''
returns the number of UNIQUE permutations of n
'''
from math import factorial
lst = []
n = str(n)
for l in set(n):
lst.append(n.count(l))
return factorial(len(n)) / reduce(lambda x,y: factorial(x) * factorial(y), lst)
Everything works fine, except when I try to pass a string that has only one unique character, i.e. aaa - I get the wrong answer:
>>> perm('abc')
6
>>> perm('aaa')
2
>>> perm('aaaa')
6
Now, I can tell the problem is in running the lambda function with factorials on a list of length 1. I don't know why, though. Most other lambda functions works on a list of length 1 even if its expecting two elements:
>>> reduce(lambda x,y: x * y, [3])
3
>>> reduce(lambda x,y: x + y, [3])
3
This one doesn't:
>>> reduce(lambda x,y: ord(x) + ord(y), ['a'])
'a'
>>> reduce(lambda x,y: ord(x) + ord(y), ['a','b'])
195
Is there something I should be doing differently? I know I can rewrite the function in many different ways that will circumvent this, (e.g. not using lambda), but I'm looking for why this specifically doesn't work.
See the documentation for reduce(), there is an optional 'initializer' argument that is placed before all other elements in the list so that the behavior for one element lists is consistent, for example, for your ord() lambda you could set initializer to the the character with an ord() of 0:
>>> reduce(lambda x, y: ord(x) + ord(y), ['a'], chr(0))
97
Python's reduce function doesn't always know what the default (initial) value should be. There should be a version that takes an initial value. Supply a sensible initial value and your reduce should work beautifully.
Also, from the comments, you should probably just use factorial on the second argument in your lambda:
reduce(lambda x,y: x * factorial(y), lst, 1)
If you want len(s)! / A!*B!*C! then the use of reduce() won't work, as it will calculate factorial(factorial(A)*factorial(B))*factorial(C). In other words, it really needs the operation to be commutative.
Instead, you'll need to generate the list of factorials, then multiply them together:
import operator
reduce(operator.mul, [factorial(x) for x in lst])
Reduce works by first computing the result for the first two elements in the sequence and then pseudo-recursively follows from there. A list of size 1 is a special case.
I would use a list comprehension here:
prod( [ factorial(val) for val in lst ] )
Good luck!

Lambda function and 0

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)

A shorter way to express this? C = A if A else B

I find myself repeating this a lot:
val = x if x else y
Sometimes x goes several levels deep into a class or dictionary so it gets very long:
val = obj.elements[0].something if obj.elements[0].something else y
It looks ugly and forces me to type a lot more. Any known ways to shorten this? Perhaps a builtin like this exists?
val = first_try(x, y)
I guess I could easily write my own but was hoping for a built in.
first_try = lambda x,y: x if x else y
The or operator returns the first argument that converts to True:
val = x or y
E.g.:
>>> None or 'OK'
'OK'
This to me seems like a case of trying to make code a little too terse. I'd probably do something like this:
x = a.b.c.d[0]
val = x if x else y
One extra line, and a whole lot less to digest at once.
Use getattr with the optional default argument. In your long example above, this would be:
val = getattr(obj.elements[0], 'something', y)

Categories

Resources