Working code
def not_double_cap_word(word):
cap_count = 0
for ch in word:
if str.isupper(ch):
cap_count += 1
not_double_cap = (cap_count < 2)
return not_double_cap
...
words_no_double_caps =list(filter(not_double_cap_word,words_alnum))
What would be a different solution, say maybe using lambdas or other patterns in Python? The above creates a new list of words with any word with more than two caps removed. one two TWo => one, two.
You can definitely simplify that not_double_cap_word function, but it's still going to be the same basic solution.
First, you can use ch.isupper() instead of str.isupper(ch). Calling a method the normal way is always easier than calling it as an unbound method and passing the self explicitly.
Next, we can replace the explicit for loop with sum over a generator expression:
cap_count = sum(ch.isupper() for ch in word)
And we don't really need to define not_double_cap, cap_count < 2 seems simple enough to return directly. So:
def not_double_cap_word(word):
cap_count = sum(ch.isupper() for ch in word)
return cap_count < 2
But really, this whole thing is probably simple enough to inline directly into the main expression. While you could do that by defining a function with lambda, there's no reason to. In general, map and filter are good when what you want to do to each thing is call a function that you already have lying around; comprehensions are better when what you want to do is an expression that you'd have to wrap in a function (lambda or otherwise) to pass to map or filter. Compare:
words_no_double_caps = [word for word in words_alnum
if sum(ch.isupper() for ch in word) < 2]
words_no_double_caps = list(filter((lambda word: sum(map(
lambda ch: ch.upper(), word)) < 2), words_alnum))
(I think I got the parens on the second version right. If not… well, if I wanted to program in Lisp, I would.:)
Either way, it's performing pretty much the exact same steps as your original code, but it's more concise. Is it more readable? That's for you to decide. But that's the most important reason to choose one or the other, or something intermediate between the two.
Well, that, and whether or not you need to reuse this logic; if you do, it should definitely be defined with a def statement and given a nice name.
You can rewrite your not_double_cap_word code using sum:
def not_double_cap_word(word):
return sum(x.isupper() for x in word) < 2
If you just want ti use a lambda with filter and not use the not_double_cap_word function:
print(list(filter(lambda x: sum(s.isupper() for s in x) < 2 ,["one", "two" ,"TWo"])))
['one', 'two']
Related
def recurse( aList ):
matches = [ match for match in action if "A" in match ]
uses = " ".join(matches)
return f"Answer: { aList.index( uses )"
This is the non recursive method. I just couldn't figure out how to implement recursion in regards of lists.
Output should be Answer: n uses.
Can anybody help.
Recursion is a bad fit for this problem in Python, because lists aren' really recursive data structures. But you could write the following:
def recurse(aList):
if not aList:
return 0
return ("A" in aList[0]) + recurse(aList[1:])
Nothing in an empty list, by definition, contains "A". Otherwise, determined if "A" is in the first element of the list, and add 1 or 0 as appropriate (remember, bools are ints in Python) to the number of matches in the rest of the list.
The recursive function should only deal with the count itself; let the caller of the recursive function put the count into a string:
print("Answer: {recurse(aList)} uses"}
I'm trying to make a loop that turn a function (like f(x)=(2x+3)(2x-3)) into a better format for editing, simply by adding a '+' before numbers (it would become f(x)=(+2x+3)(+2x-3)). The problem is that in the loop, after I insert a new char in the middle of the string, the string doesn't update, so when the loop goes on and I try to access a certain index of the function string, the char isn't correct.
def rewriteFunction(function):
for i, c in enumerate(function):
newFunction += c
if(str(c).isdigit()):
if not(i == 0):
if not(Sign.isSign(function[i - 1])):
function = function[:i] + "+" + function[i:]
If possible, could you answer me by sending the exact (corrected) code, without modifying it too much, of course if that's the right method to do that. Thanks in advance!!
In one line you store your updated data in the variable newFunction, but in another you store your updates back into function. For consistency, let's never change function and apply all of our updates to newFunction.
You never initialize newFunction.
You never explicitly return anything from rewriteFunction().
Try this:
def rewriteFunction(function):
newFunction = ''
for i, c in enumerate(function):
if(str(c).isdigit()):
if not(i == 0):
if not(function[i - 1] in '+-'):
newFunction += '+'
newFunction += c
return newFunction
assert rewriteFunction('f(x)=(2x+3)(2x-3)') == 'f(x)=(+2x+3)(+2x-3)'
If your solutions isn't bound to using loops, you may give a try to regular expressions to simplify things:
>>> import re
>>> s = 'f(x)=(2x+3)(2x-3))'
>>> re.sub(r'\b(?<![+-])(\d+)', r'+\1', s)
'f(x)=(+2x+3)(+2x-3))'
Feel free to ask any questions about the solution.
The first part of the question is to check if input A and input B are anagrams, which I can do easily enough.
s = input ("Word 1?")
b = sorted(s)
c = ''.join(b)
t = input("Word 2?")
a = sorted(t)
d = ''.join(b)
if d == c:
print("Anagram!")
else:
print("Not Anagram!")
The problem is the second part of the question - I need to check if two words are anagrams if all of the punctuation is removed, the upper case letters turned to lower case, but the question assumes no spaces are used. So, for example, (ACdB;,.Eo,."kl) and (oadcbE,LK) are anagrams. The question also asks for loops to be used.
s = input ("Word 1?")
s = s.lower()
for i in range (0, len(s)):
if ord(s[i]) < 97 or ord(s[i]) >122:
s = s.replace(s[i], '')
b = sorted(s)
c = ''.join(b)
print(c)
Currently, the above code is saying the string index is out of range.
Here's the loop you need to add, in psuedocode:
s = input ("Word 1?")
s_letters = ''
for letter in s:
if it's punctuation: skip it
else if it's uppercase: add the lowercase version to s_letters
else: add it to s_letters
b = sorted(s_letters)
Except of course that you need to add the same thing for t as well. If you've learned about functions, you will want to write this as a function, and call it twice, instead of copying and pasting it with minor changes.
There are three big problems with your loop. You need to solve all three of these, not just one.
First, s = s.replace(s[i], '') doesn't replace the ith character with a space, it replaces the ith character and every other copy of the same character with a space. That's going to screw up the rest of your loop if there are any duplicates. It's also very slow, because you have to search the entire string over and over again.
The right way to replace the character at a specific index is to use slicing: s = s[:i] + s[i+1:].
Or, you could make this a lot simpler by turning the string into a list of characters (s = list(s)), you can mutate it in-place (del s[i]).
Next, we're going through the loop 6 times, checking s[0], s[1], s[2], s[3], s[4], and s[5]. But somewhere along the way, we're going to remove some of the characters (ideally three of them). So some of those indices will be past the end of the string, which will raise an IndexError. I won't explain how to fix this yet, because it ties directly into the next problem.
Modifying a sequence while you loop over it always breaks your loop.* Imagine starting with s = '123abc'. Let's step through the loop.
i = 0, so you check s[0], which is 1, so you remove it, leaving s = '23abc'.
i = 1, so you check s[1], which is 3, so you remove it, leaving s = '2abc'.
i = 2, so you check s[2], which is b, so you leave it, leaving s = '2abc'.
And so on.
The 2 got moved to s[0] by removing the 1. But you're never going to come back to i = 0 once you've passed it. So, you're never going to check the 2. You can solve this in a few different ways—iterating backward, doing a while instead of an if each time through the for, etc.—but most of those solutions will just exacerbate the previous problem.
The easy way to solve both problems is to just not modify the string while you loop over it. You could do this by, e.g., building up a list of indexes to remove as you go along, then applying that in reverse order.
But a much easier way to do it is to just build up the characters you want to keep as you go along. And that also solves the first problem for your automatically.
So:
new_s = []
for i in range (0, len(s)):
if ord(s[i]) < 97 or ord(s[i]) >122:
pass
else:
new_s.append(s[i])
b = sorted(new_s)
And with that relative minor change, your code works.
While we're at it, there are a few ways you're overcomplicating things.
First, you don't need to do ord(s[i]) < 97; you can just do s[i] < 'a'. This makes things a lot more readable.
But, even more simply, you can just use the isalpha or islower method. (Since you've already converted to lower, and you're only dealing with one character at a time, it doesn't really matter which.) Besides being more readable, and harder to get wrong, this has the advantage of working with non-ASCII characters, like é.
Finally, you almost never want to write a loop like this:
for i in range(len(s)):
That forces you to write s[i] all over the place, when you could have just looped over s in the first place:
for ch in s:
So, putting it all together, here's your code, with the two simple fixes, and the cleanup:
s = input ("Word 1?")
s = s.lower()
new_s = []
for ch in s:
if ch.isalpha():
new_s.append(ch)
b = sorted(new_s)
c = ''.join(b)
print(c)
If you know about comprehensions or higher-order functions, you'll recognize this pattern as exactly what a list comprehension does. So, you can turn the whole 4 lines of code that build new_s into either of these one-liners, which are more readable as well as being shorter:
new_s = (ch for ch in s if ch.isalpha)
new_s = filter(str.isalpha, s)
And in fact, the whole thing can become a one-liner:
b = sorted(ch for ch in s.lower() if ch.isalpha)
But your teacher asked you to use a for statement, so you'd better keep it as a for statement.
* This isn't quite true. If you only modify the part of the sequence after the current index, and you make sure the sequence aways has the right length by the time you get to each index even though it may have had a different length before you did (using a while loop instead of a for loop, to reevaluate len(seq) each time, makes this part trivial instead of hard), then it works. But it's easier to just never do it to than learn the rules and carefully analyze your code to see if you're getting away with it this time.
Please tell me why this sort function for Python isnt working :)
def sort(list):
if len(list)==0:
return list
elif len(list)==1:
return list
else:
for b in range(1,len(list)):
if list[b-1]>list[b]:
print (list[b-1])
hold = list[b-1]
list[b-1]=list[b]
list[b] = hold
a = [1,2,13,131,1,3,4]
print (sort(a))
It looks like you're attempting to implement a neighbor-sort algorithm. You need to repeat the loop N times. Since you only loop through the array once, you end up with the largest element being in its place (i.e., in the last index), but the rest is left unsorted.
You could debug your algorithm on your own, using pdb.
Or, you could use python's built-in sorting.
Lets take a look at you code. Sort is a built in Python function (at least I believe it is the same for both 2.7 and 3.X) So when you are making your own functions try to stay away from name that function with inbuilt functions unless you are going to override them (Which is a whole different topic.) This idea also applies to the parameter that you used. list is a type in the python language AKA you will not be able to use that variable name. Now for some work on your code after you change all the variables and etc...
When you are going through your function you only will swap is the 2 selected elements are next to each other when needed. This will not work with all list combinations. You have to be able to check that the current i that you are at is in the correct place. So if the end element is the lowest in the List then you have to have it swap all the way to the front of the list. There are many ways of sorting (ie. Quick sort, MergeSort,Bubble Sort) and this isnt the best way... :) Here is some help:
def sortThis(L):
if (len(L) == 0 or len(L) == 1):
return list
else:
for i in range(len(L)):
value = L[i]
j = i - 1
while (j >= 0) and (L[j] > value):
L[j+1] = L[j]
j -= 1
L[j+1] = value
a = [1,2,13,131,1,3,4]
sortThis(a)
print a
Take a look at this for more sorting Fun: QuickSort MergeSort
If it works, it would be the best sorting algotithm in the world (O(n)). Your algorithm only puts the greatest element at the end of the list. you have to apply recursively your function to list[:-1].
You should not use python reserved words
I'm sorry if this is a question answered elsewhere. Searching through Google and Stackforum I didn't find anything from which I could extrapolate the answers; but I feel like part of that is me.
I'm trying to work out lambdas as a concept, and as part of that I'm kinda looking for ways to use it.
SO, if this is a colossally stupid thing to do with lambda from a function standpoint, feel free to let me know and explain. But either way, I still want to know the answer/still want to know how to do this with the python language.
So, for testing purposes I have:
my_test = 'test_name'
testlist = ['test_name', 'test_name_dup', 'test_name_dup_1', 'test_name_dup_3']
I'm looking to use lambda to create one function that loops through and returns the first test_name_# that isn't in the testlist. The functionality will eventually be applied to filenames, but for testing purposes I had to get away from actually reading the filenames--gave me too many more ways to mess something up.
But my_test has to be able to change, and the test list will be a list of filepaths.
So, I'm looking for a function like:
new_name = lambda x: my_test + '_' + str(x)
But the initial value should be x = 1, and it should continue until new_name is not in testlist. Seems like:
bool(new_name not in testlist)
might be something work with.
But I can't figure out a way to set the initial x to 1, and have it loop through with (x+1) until the bool is true.
I know this is possible as I've found some CRAZY lambda examples out there that are looping through lines in a file. I just couldn't quite make sense of them (and didn't have any way to play with them as they were dealing with things outside my programming level.
On a related note, could I add values to the beginning of this loop? (i.e. can I have it check for test_name, then test_name_dup, then test_name_dup_#)?
Thanks in advance for the help! Lambdas (while very cool) totally mess with my head.
Lambdas are just another way of defining a function
def foo(x):
return x + x
is the same as
foo = lambda x: x + x
So let's start with a function to do what you want:
def first_missing(items, base):
for number in itertools.count():
text = base + '_' + str(number)
if text not in items:
return text
The first thing to note is that you can't use loops inside a lambda. So we'll need to rewrite this without a loop. Instead, we'll use recursion:
def first_missing(items, base, number = 0):
text = base + '_' + str(number)
if text not in items:
return text
else:
return first_missing(items, base, number + 1)
Now, we also can't use an if/else block in a lambda. But we can use a ternary expression:
def first_missing(items, base, number = 0):
text = base + '_' + str(number)
return text if text not in items else first_missing(items, base, number + 1)
We can't have local variables in a lambda, so we'll use a trick, default arguments:
def first_missing(items, base, number = 0):
def inner(text = base + '_' + str(number)):
return text if text not in items else first_missing(items, base, number + 1)
return inner()
At this point we can rewrite inner as a lambda:
def first_missing(items, base, number = 0):
inner = lambda text = base + '_' + str(number): text if text not in items else first_missing(items, base, number + 1)
return inner()
We can combine two lines to get rid of the inner local variable:
def first_missing(items, base, number = 0):
return (lambda text = base + '_' + str(number): text if text not in items else first_missing(items, base, number + 1))()
And at long last, we can make the whole thing into a lambda:
first_missing = lambda: items, base, number = 0: (lambda text = base + '_' + str(number): text if text not in items else first_missing(items, base, number + 1))()
Hopefully that gives you some insight into what you can do. But don't ever do it because, as you can tell, lambdas can make your code really hard to read.
There's no need to use a lambda in this case, a simple for loop will do:
my_test = 'test_name_dup'
testlist = ['test_name', 'test_name_dup','test_name_dup_1', 'test_name_dup_3']
for i in xrange(1, len(testlist)):
if my_test + '_' + str(i) not in testlist:
break
print my_test + '_' + str(i)
> test_name_dup_2
If you really, really want to use a lambda for this problem, you'll also have to learn about itertools, iterators, filters, etc. I'm gonna build on thg435's answer, writing it in a more idiomatic fashion and explaining it:
import itertools as it
iterator = it.dropwhile(
lambda n: '{0}_{1}'.format(my_test, n) in testlist,
it.count(1))
print my_test + '_' + str(iterator.next())
> test_name_dup_2
The key to understanding the above solution lies in the dropwhile() procedure. It takes two parameters: a predicate and an iterable, and returns an iterator that drops elements from the iterable as long as the predicate is true; afterwards, returns every element.
For the iterable, I'm passing count(1), an iterator that produces an infinite number of integers starting from 1.
Then dropwhile() starts to consume the integers until the predicate is false; this is a good opportunity for passing an in-line defined function - and here's our lambda. It receives each generated integer in turn, checking to see if the string test_name_dup_# is present in the list.
When the predicate returns false, dropwhile() returns and we can retrieve the value that made it stop by calling next() on it.
You can combine a lambda with itertools.dropwhile:
import itertools
n = itertools.dropwhile(lambda n: 'test_name_dup_%d' % n in testlist, range(1, len(testlist))).next()
As to your last question, you can write a generator for names, like:
def possible_names(prefix):
yield prefix
yield prefix + '_dup'
n = 0
while True:
n += 1
yield '%s_dup_%d' % (prefix, n)
and then use this generator with dropwhile:
unique_name = itertools.dropwhile(lambda x: x in testlist, possible_names('test_name')).next()
print unique_name
You are a bit off the track. Lambdas are nothing but "simple" functions, often used for their fast syntax in functional programming. They are the perfect companion built-in functions "map", "reduce", "filter" but also for more complicated functions defined into itertools. Therefore the most useful thing to do with them is to generate/manipulate iterable objects (especially lists). Note that lambdas will slow down your code in most of the cases if compared to list comprehensions/normal loops and will make it harder to be read. Here is an example of what you want to do with lambdas.
>>> filter(lambda i: i!=(0 if len(testlist[i].split("_"))==3 else int(testlist[i].split("_")[-1])), range(len(testlist)))[0]
2
Or you could use more complicated functions with itertools. Anyhow I strongly suggest you not to use lambdas for this kind of assignments, as the readability is terrible. I'd rather use a well structured for loop, which is also faster.
[Edit]
To prove that lambdas+builtins are not faster than list comprehensions: consider a simple problem, for x in range(1000) create a list of x shifted by 5.
$ python -m timeit 'map(lambda x: x>>5, range(1000))' 1000 loops, best of 3: 225 usec per loop
$ python -m timeit '[x>>5 for x in range(1000)]'10000 loops, best of 3: 99.1 usec per loop
You have a >100% performance increase without lambdas.
I prefer the list comprehension or iterator method. Makes for easy one liners that I feel are pretty easy to read and maintain. Quite frankly, lambdas belong some places, here I believe its less elegant a solution.
my_test = 'test_name'
prefix = 'test_name_dup_'
testlist = ['test_name','test_name_dup','test_name_dup_1','test_name_dup_3']
from itertools import count
print next('%s%d' % (prefix, i) for i in count(1) if '%s%d' % (prefix, i) not in testlist)
This returns the first not-found instance in the sequence, which I think is the cleanest.
Of course, if you prefer a list from a definite range, you can modify it to become a list comprehension:
print ['%s%d' % (prefix, i) for i in xrange(0,5) if '%s%d' % (prefix, i) not in testlist]
returns:
['test_name_dup_0', 'test_name_dup_2', 'test_name_dup_4']