Q1 of this problem set is the following:
Write a Python procedure fix_machine to take 2 string inputs and
returns the 2nd input string as the output if all of its characters
can be found in the 1st input string and "Give me something that's not
useless next time." if it's impossible. Letters that are present in
the 1st input string may be used as many times as necessary to create
the 2nd string (you don't need to keep track of repeat usage).
For example:
print fix_machine('UdaciousUdacitee', 'Udacity') should output "Give me something that's not useless next time."
and:
print fix_machine('buy me dat Unicorn', 'Udacity') should output 'Udacity'
This is the solution I came up with by myself:
def fix_machine(debris, product):
i = -1
while True:
i = i +1
if debris.find(product[i]) == -1:
return "Give me something that's not useless next time."
break
word = product[0:i+1]
if word == product:
break
return word
I found this other sample solution online:
def fix_machine(debris, product):
x = 0
while x < len(product):
if debris.find(product[x]) == -1:
return "Give me something that's not useless next time."
x += 1
return product
My code executes correctly, but I was wondering if it makes sense the way I'm using the break function. Any recommendation about how to better understand while-loops and optimize them?
The two are roughly equivalent in terms of what they do. You are looping until you break, whereas the second solution always loops to what it knows to be the maximum number of loops needed (unless it finds the failure case) - the length of the product argument. Both will run in the same O(n) amount of time, so while you have some awkwardness in your code it's not practically different.
There are other approaches to go about this that are clearer to understand. For instance, since you don't care about repeat variables, it might be easier to take your product as input to a set:
required_characters = {c for c in product} # puts all characters into a set, dropping repeats (because sets can only have a given value once)
for c in required_characters:
if c not in debris:
return "Give me something that's not useless next time."
return product
The functional difference here is that you are looping through product once, and can stop looping through debris the moment you find all the characters. But this also runs in O(n) - which is as fast as you can actually do this. The real question is what is clearest to you, the programmer, and clearest to whomever reads your code.
In the comments, #alani notes that you can also do something like this:
if set(product) <= set(debris):
return product
return "Give me..."
This does something similar, using set comparison. The slight downside here is that you're putting both inputs into sets (an O(n) operation), which means you're guaranteed the maximum runtime - nothing will quit early if it knows it is a condition that can't be satisfied. Still, it is even more concise and therefore clear. For reasonable inputs a totally valid approach.
while loops can be used for any type of looping but for situations where you're iterating over a collection (like the letters in a word) a for loop is much more appropriate. Generally a while loop is more useful when you don't know in advance how many times the loop will execute.
Here's a sample of iterating through both words looking for a letter in product that doesn't appear in debris:
NOT_FOUND_MSG = "Give me something that's not useless next time."
def fix_machine(debris, product):
for p_char in product:
found = False
for d_char in debris:
if p_char == d_char:
found = True
break
if not found:
return NOT_FOUND_MSG
return product
We can clarify (and maybe optimize) by using in to check if p_char is in debris without explicitly iterating over it:
def fix_machine(debris, product):
for p_char in product:
if p_char not in debris:
return NOT_FOUND_MSG
return product
Let's trim it down some more by using an alternate form of the for loop:
def fix_machine(debris, product):
if any(p_char not in debris for p_char in product):
return NOT_FOUND_MSG
return product
Finally an alternative which eliminates explicit loops altogether by taking advantage of the properties of a set:
def fix_machine(debris, product):
return product if set(product).issubset(debris) else NOT_FOUND_MSG
I'm going to answer this under the assumption this is part of an exercise on while loops.
Your solution was a good attempt, but there are a few things that you can still improve. I'm going to walk through your code and point them out.
def fix_machine(debris, product):
i = -1 # THING 1
while True:
i = i +1 # ALSO THING 1
if debris.find(product[i]) == -1:
return "Give me something that's not useless next time."
break # THING 2
word = product[0:i+1] # THING 3
if word == product:
break
return word
Thing 1: Initializing your index variable like this may work, but it isn't very clean. Instead you should do what the example you provided did and initialize to zero then increment at the end of your loop. This is easier to read and better practice overall.
Thing 2: This break statement will never be reached. The return statement will end your function (and by extension, the loop).
Thing 3: This is a clever idea, but a better idea would be to break once you've iterated over the entirety of the product. That way you don't have to check for equality every iteration.
If you fix all of these you'll notice you end up with the second example:
def fix_machine(debris, product):
x = 0 #THING 1 (Initialize to zero)
while x < len(product): #THING 3 (stop at end of product)
if debris.find(product[x]) == -1:
return "Give me something that's not useless next time." # THING 2 (no break)
x += 1 # THING 1(INCREMENT AT END)
return product
Related
Every now and then I fall on this pattern when looping through an array:
for a in b:
if a==y:
#do somethinng
return 1
there is no else statement because I need to check the full array for a==y (let's say a is name in a dir list) before returning anything.
It would also be a good idea to return something only if no element in the array fulfills the condition a==y
How should I re organize the code in this sense?
How do I tell the caller if the loop doesn't 'succeed' in finding 'y'?
If you are expecting to encounter y only once or only take into account its first occurrence (given that you have a return statement within the condition), then here's another method:
if any(a == y for a in b):
#do something
return 1
else:
return 0
It would probably be a good idea to return something only if no element in the array fulfills the condition x==b but I don't know how to re organize the code in this sense.
The usual thing is to put the return reflecting the fact that nothing matched after the loop:
for a in b:
if a==y:
#do somethinng
return 1
return 0 # or whatever
In some cases, it may be appropriate to throw an exception after the loop instead, if the fact that nothing matched within the loop is an exceptional condition, not a normal case.
Please be sure to check out navneethc's answer using any as well.
I am trying to make word chains, but cant get around recursive searching.
I want to return a list of the words reuired to get to the target word
get_words_quicker returns a list of words that can be made by just changing one letter.
def dig(InWord, OutWord, Depth):
if Depth == 0:
return False
else:
d = Depth - 1;
wordC = 0;
wordS = [];
for q in get_words_quicker(InWord):
wordC+=1
if(OutWord == q):
return q
wordS.append(q)
for i in range(0,wordC):
return dig(wordS[i],OutWord,d)
Any help/questions would be much appreciated.
ANALYSIS
There is nowhere in your code that you form a list to return. The one place where you make a list is appending to wordS, but you never return this list, and your recursive call passes only one element (a single word) from that list.
As jasonharper already pointed out, your final loop can iterate once and return whatever the recursion gives it, or it can fall off the end and return None (rather than "nothing").
You have two other returns in the code: one returns False, and the other will return q, but only if q has the same value as OutWord.
Since there is no code where you use the result or alter the return value in any way, the only possibilities for your code's return value are None, False, and OutWord.
REPAIR
I'm afraid that I'm not sure how to fix this routine your way, since you haven't really described how you intended this code to carry out the high-level tasks you describe. The abbreviated variable names hide their purposes. wordC is a counter whose only function is to hold the length of the list returned from get_words_quicker -- which could be done much more easily.
If you can clean up the code, improve the data flow and/or documentation to something that shows only one or two disruptions in logic, perhaps we can fix the remaining problems. As it stands, I hesitate to try -- you'd have my solution, not yours.
Given a string, lets say "TATA__", I need to find the total number of differences between adjacent characters in that string. i.e. there is a difference between T and A, but not a difference between A and A, or _ and _.
My code more or less tells me this. But when a string such as "TTAA__" is given, it doesn't work as planned.
I need to take a character in that string, and check if the character next to it is not equal to the first character. If it is indeed not equal, I need to add 1 to a running count. If it is equal, nothing is added to the count.
This what I have so far:
def num_diffs(state):
count = 0
for char in state:
if char != state[char2]:
count += 1
char2 += 1
return count
When I run it using num_diffs("TATA__") I get 4 as the response. When I run it with num_diffs("TTAA__") I also get 4. Whereas the answer should be 2.
If any of that makes sense at all, could anyone help in fixing it/pointing out where my error lies? I have a feeling is has to do with state[char2]. Sorry if this seems like a trivial problem, it's just that I'm totally new to the Python language.
import operator
def num_diffs(state):
return sum(map(operator.ne, state, state[1:]))
To open this up a bit, it maps !=, operator.ne, over state and state beginning at the 2nd character. The map function accepts multible iterables as arguments and passes elements from those one by one as positional arguments to given function, until one of the iterables is exhausted (state[1:] in this case will stop first).
The map results in an iterable of boolean values, but since bool in python inherits from int you can treat it as such in some contexts. Here we are interested in the True values, because they represent the points where the adjacent characters differed. Calling sum over that mapping is an obvious next step.
Apart from the string slicing the whole thing runs using iterators in python3. It is possible to use iterators over the string state too, if one wants to avoid slicing huge strings:
import operator
from itertools import islice
def num_diffs(state):
return sum(map(operator.ne,
state,
islice(state, 1, len(state))))
There are a couple of ways you might do this.
First, you could iterate through the string using an index, and compare each character with the character at the previous index.
Second, you could keep track of the previous character in a separate variable. The second seems closer to your attempt.
def num_diffs(s):
count = 0
prev = None
for ch in s:
if prev is not None and prev!=ch:
count += 1
prev = ch
return count
prev is the character from the previous loop iteration. You assign it to ch (the current character) at the end of each iteration so it will be available in the next.
You might want to investigate Python's groupby function which helps with this kind of analysis.
from itertools import groupby
def num_diffs(seq):
return len(list(groupby(seq))) - 1
for test in ["TATA__", "TTAA__"]:
print(test, num_diffs(test))
This would display:
TATA__ 4
TTAA__ 2
The groupby() function works by grouping identical entries together. It returns a key and a group, the key being the matching single entry, and the group being a list of the matching entries. So each time it returns, it is telling you there is a difference.
Trying to make as little modifications to your original code as possible:
def num_diffs(state):
count = 0
for char2 in range(1, len(state)):
if state[char2 - 1] != state[char2]:
count += 1
return count
One of the problems with your original code was that the char2 variable was not initialized within the body of the function, so it was impossible to predict the function's behaviour.
However, working with indices is not the most Pythonic way and it is error prone (see comments for a mistake that I made). You may want rewrite the function in such a way that it does one loop over a pair of strings, a pair of characters at a time:
def num_diffs(state):
count = 0
for char1, char2 in zip(state[:-1], state[1:]):
if char1 != char2:
count += 1
return count
Finally, that very logic can be written much more succinctly — see #Ilja's answer.
so basically a guy helped me improve my code. The problem is, it's still dead disappointing and does not work. What I wanna do, is reset the lenRecur.number so I could use the function again, using other strings and getting correct answers (not too big answers)
I guess, the problem is with hasattr. But I can't remove it, because if I do, my string length calculator won't work.
Anyway, even if I add lenRecur.number = 0 after the function, it still doesn't work.
It's like impossible, because when the function hits "return", it's done, period. If I reset it before "return", it will return 0, not correct answer so yeah I'm in deep trouble here.
def lenRecur(aStr):
if not hasattr(lenRecur, 'number'):
lenRecur.number = 0
'''
aStr: a string
returns: int, the length of aStr
'''
if aStr == '':
return lenRecur.number
else:
lenRecur.number += 1
return lenRecur(aStr[:-1])
P.s. the goal of my program(?) / script(?) is to measure the length of input string, without using input() method. As trying to re-create the length() method, with using more primitive means.
The script will have to have many different inputs, so it shall reset.
If you just want a recursive strength-length function, that's easy:
def len_recur(a_str):
if not a_str:
return 0
else:
return 1 + len_recur(a_str[1:])
Of course that's not tail-recursive, but then Python doesn't optimize tail recursion anyway, so it doesn't matter.
And if you want it to be tail recursive just for the hell of it—or because you've read Paul Butler's trick for Tail Recursion in Python and want to try it out—you still don't want to do that by storing the accumulator as an attribute of the function. Just use the usual trick of defining a local function (or using a mutable default parameter, if you prefer):
def len_tail_recur(a_str):
def tail(a_str, acc):
if not a_str:
return acc
else:
return tail(a_str[1:], acc+1)
return tail(a_str, 0)
If you want to convert this to a real tail-recursive function so it doesn't bomb on lists of 1001 elements, and don't understand the Paul Butler link above, see my answer to Get length of list in Python using recursion, which solves exactly this problem. (Another answer to that question also shows how to solve the problem with log N recursive calls instead of N, which is a different way of getting around the problem, unless you have impossibly long lists.)
All that being said, even though your implementation is the wrong way to do it, it actually works just fine. (So far, I've been PEP8-ifying your code to look more like idiomatic Python; from here on I'm just going to copy-paste as-is, but your real code should look like the above.)
def lenRecur(aStr):
if not hasattr(lenRecur, 'number'):
lenRecur.number = 0
'''
aStr: a string
returns: int, the length of aStr
'''
if aStr == '':
return lenRecur.number
else:
lenRecur.number += 1
return lenRecur(aStr[:-1])
print lenRecur('abc')
lenRecur.number = 0
print lenRecur('abcd')
This prints 3, then 4. Of course you have to set lenRecur.number from outside the function, because inside the function you still need the value. But you can solve that with the same kind of wrapper:
def lenRecur(aStr):
lenRecur.number = 0
'''
aStr: a string
returns: int, the length of aStr
'''
def recur(aStr):
if aStr == '':
return lenRecur.number
else:
lenRecur.number += 1
return recur(aStr[:-1])
return recur(aStr)
You don't have to use a state variable inside the function. If you want to make a recursive length calculator, then just do
def lenRecur (aStr):
if (aStr == ""):
return 0
else
return lenRecur (aStr [:-1]) + 1
Also note that this style has no error checking etc, but for the purposes of learning about recursion, it works fine.
If you're trying to understand recursion by implementing the length function using recursion then you can use something like this:
#!python
def lenRecur(something, curlen=0):
if something:
return lenRecur(something[1:], curlen+1)
else:
return curlen
... I won't claim that this is particularly good code. But it should work with any sort of sequence (string, list, tuple) ... anything for which the [1:] slice operation will work so long as it doesn't exceed the maximum recursion limit in your running Python instance.
In your example you're trying to implement a similar concept by using hasattr to "monkey patch" your function's object with a "number" attribute. In my example I'm using a default parameter as a way of passing the variable down into the recursive calls.
So in the initial call curlen is zero (calling this with the "optional" extra argument would give bogus results). From that call the function calls itself with a slice of the original sequence (string) that lops off the head (making it one shorter) and with the optional (curlen) argument incremented. At the end the string/sequence is of zero length, the zero is returned up through each of the previous (recursive) calls.
It's a lame way to accomplish this, and it could be a starting point for a discussion on tail recursion elimination (Google for it). But it will work without monkey patching your function/object.
New to Python and trying to understand recursion. I'm trying to make a program that prints out the number of times string 'key' is found in string 'target' using a recursive function, as in Problem 1 of the MIT intro course problem set. I'm having a problem trying to figure out how the function will run. I've read the documentation and some tutorials on it, but does anyone have any tips on how to better comprehend recursion to help me fix this code?
from string import *
def countR(target,key):
numb = 0
if target.find(key) == -1:
print numb
else:
numb +=1
return countR(target[find(target,key):],key)
countR('ajdkhkfjsfkajslfajlfjsaiflaskfal','a')
By recursion you want to split the problem into smaller sub-problems that you can solve independently and then combine their solution together to get the final solution.
In your case you can split the task in two parts: Checking where (if) first occurence of key exists and then counting recursively for the rest.
Is there a key in there:
- No: Return 0.
- Yes: Remove key and say that the number of keys is 1 + number of key in the rest
In Code:
def countR(target,key):
if target.find(key) == -1:
return 0
else:
return 1+ countR(target[target.find(key)+len(key):],key)
Edit:
The following code then prints the desired result:
print(countR('ajdkhkfjsfkajslfajlfjsaiflaskfal','a'))
This is not how recursion works. numb is useless - every time you enter the recursion, numb is created again as 0, so it can only be 0 or 1 - never the actual result you seek.
Recursion works by finding the answer the a smaller problem, and using it to solve the big problem. In this case, you need to find the number of appearances of the key in a string that does not contain the first appearance, and add 1 to it.
Also, you need to actually advance the slice so the string you just found won't appear again.
from string import *
def countR(target,key):
if target.find(key) == -1:
return 0
else:
return 1+countR(target[target.find(key)+len(key):],key)
print(countR('ajdkhkfjsfkajslfajlfjsaiflaskfal','a'))
Most recursive functions that I've seen make a point of returning an interesting value upon which higher frames build. Your function doesn't do that, which is probably why it's confusing you. Here's a recursive function that gives you the factorial of an integer:
def factorial(n):
"""return the factorial of any positive integer n"""
if n > 1:
return n * factorial(n - 1)
else:
return 1 # Cheating a little bit by ignoring illegal values of n
The above function demonstrates what I'd call the "normal" kind of recursion – the value returned by inner frames is operated upon by outer frames.
Your function is a little unusual in that it:
Doesn't always return a value.
Outer frames don't do anything with the returned value of inner frames.
Let's see if we can refactor it to follow a more conventional recursion pattern. (Written as spoiler syntax so you can see if you can get it on your own, first):
def countR(target,key):
idx = target.find(key)`
if idx > -1:
return 1 + countR(target[idx + 1:], key)
else:
return 0
Here, countR adds 1 each time it finds a target, and then recurs upon the remainder of the string. If it doesn't find a match it still returns a value, but it does two critical things:
When added to outer frames, doesn't change the value.
Doesn't recur any further.
(OK, so the critical things are things it doesn't do. You get the picture.)
Meta/Edit: Despite this meta article it's apparently not possible to actually properly format code in spoiler text. So I'll leave it unformatted until that feature is fixed, or forever, whichever comes first.
If key is not found in target, print numb, else create a new string that starts after the the found occurrence (so cut away the beginning) and continue the search from there.