Exercise:
Write a function called is_sorted that takes a list as a parameter and returns True if the list is sorted in ascending order and False otherwise. You can assume (as a precondition) that the elements of the list can be compared with the relational operators <, >, etc.
For example, is_sorted([1,2,2]) should return True and is_sorted(['b', 'a']) should return False.
So far I have:
def is_sorted(stuff):
for i in stuff:
if stuff[i+1] > stuff[i]:
return True
else:
return False
numbers = [1, 0, 5, 2, 8]
print is_sorted(numbers)
But it seems to return True every time I change the numbers list around. How do I change this so it works?
The line
for i in stuff:
Iterates through items, not indices. If you put 5 or 8 first, you will get an IndexError. Also, you return if the check passes for the first item, much too early! You can return False if any are out of order, but all must be in order to return True.
Instead, try using enumerate to get both item and index:
def is_sorted(stuff):
for index, item in enumerate(stuff):
try:
if item > stuff[index + 1]:
return False
except IndexError:
return True
Alternatively, use zip to compare pair wise:
def is_sorted(stuff):
return all(b >= a for a, b in
zip(stuff, stuff[1:]))
for i in stuff will enumerate each of the elements. What you'd like to do is enumerate the indices of the elements, so change the loop to
for i in range(len(stuff))
Next, you dont want to return True if you come across a subsequent element greater than the current. just return True after testing each pair of adjacent elements, so something like:
def is_sorted(stuff):
for i in range(1,len(stuff)):
if stuff[i - 1] > stuff[i]:
return False
return True
Building off #jonrsharpe's answer,
This is a great application of the itertools pairwise recipe:
from itertools import tee, izip
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None) #or b.next() on Python 2.5 and below
return izip(a, b)
def is_sorted(iterable):
return all(a <= b for a,b in pairwise(iterable))
This avoids indexed access or slicing, which allows you to operate on generators and greatly reduces memory footprint.
is_sorted(xrange(5000000))
Unfortunately, it won't help you for infinite generators like is_sorted(count(0))
It looks like you're conflating for item in stuff with for i in range(len(stuff)).
Say stuff = ['a', 'b', 'c', 'd'].
With for item in stuff, you'd be iterating over each item in stuff: 'a', 'b', 'c', and 'd'.
With for i in range(len(stuff)), you'd be iterating over each index of stuff: 0, 1, 2, 3.
Note that the variable names item and i are common conventions for these statements but are not mandatory--you can replace item and i with anything you want. Therefore your 2nd line of code (for i in stuff) is executing #1 above, not #2 as you were expecting.
To have your code execute #2, you'd have to use range(len(stuff) in your 2nd line of code instead of just stuff.
I know this is an old thread but I am trying to refine my solution to this problem.
I want to make sure no body sneaks in an integer or a string in a predominantly string or integer list resp. For instance, ['a', 'b', 2]
I wrote a python program for this but it seems to me it is getting a bit unwieldy. Is there any better solution to this?
def is_sorted(stuff):
is_int = 1
not_int = 0
if type(stuff[0]) == int:
of_type = is_int
else:
of_type = not_int
for i in range(1,len(stuff)):
if (of_type == is_int and (type(stuff[i - 1]) == int and type(stuff[i]) == int)) or \
(of_type == not_int and (type(stuff[i - 1]) == str and type(stuff[i]) == str)):
if stuff[i - 1] > stuff[i]:
return False
else:
print "I saw what you did there!"
return False
return True
print is_sorted(['b', 'a']) //False
print is_sorted(['a', 'b']) //True
print is_sorted([1, 2]) //True
print is_sorted(['a', 'b', 2]) //False; "I saw what you did there!"
def isSorted(stuff):
if len(stuff) == 0:
output = 'True'
if len(stuff) == 1:
output = 'True'
for i in range(0,len(stuff)-1):
if stuff[i+1] > stuff[i]:
output = 'True'
else:
return False
return output
Related
I have a float number x and a list range list_ = [[a, b], [c,d], [e,f]]
How can check if the number x is in the list. It means the function will return True in case of
a<=x <=b
or
c<=x <=d
or
e<=x <=f
Otherwise, the function will return False. Could you help me to write a Python code for the function
function (x, list_)--> True/False
Clean solution:
def function(x, list_):
return any([l[0] < x < l[1] for l in list_])
Optimized solution:
def function(x, list_):
for l in list_:
if l[0] < x < l[1]:
return True
return False
The idiomatic solution would be this:
def f(x: int, ls: List[Tuple[float, float]]) -> bool:
return any(a <= x <=b for (a, b) in ls)
Take specific note of the following:
Naming a function function is a super poor idea.
It is abnormal and therefore a poor idea to name a variable list_ just to avoid overriding a keyword.
Using the form any ensures that you quickly quit when you find a valid solution.
You can quickly destructure your tuple (or list, if you happen to pass a list) using the for (a, b) in ls clause.
This solution is as quick as if you use a for clause, but all of that is premature optimization anyway.
Using an explicit destructing ensures you have two and only two elements for your sublist.
It was requested that I check certain inputs:
>>> f(10.1, [[8.1, 12.1], [110, 120]])
True
Seems to work!
If you're running into NameError, the issue is simply one of the importation of types. You can either define f like so:
def f(x, ls):
... // As you would otherwise
Or import the required types to make the type-hinting work properly:
from typing import List, Tuple
def f(x: int, ls: List[Tuple[float, float]]) -> bool:
... // As you would otherwise
This has little to do with the original question or solution - it's just standard for type hinting in python.
def function(x,list__):
for [a,b] in list_data:
if a<=x<=b:
return True
return False
You can simply iterate through the list and find whether it's in range or not.
I'm generating the variable and the list randomly and calling a function that iterates and checks whether the variable lies within the range of any of the members of the list.
import numpy as np
def find_if_in_range(list_, var) -> bool:
for i in list_:
if i[0] <= var <= i[-1]:
return True
return False
var = np.random.randint(10)
list_ = np.random.randint(10, size=(3,2), dtype=np.uint8)
print(f"var: {var}")
print(f"list: {list_}")
res = find_if_in_range(list_, var)
print(res)
Output:
var: 0
list: [[0 6]
[2 7]
[7 9]]
True
Hope this helps.
Cheers.
Write a function expanding(l) that takes as input a list of integer l and returns True if the absolute difference between each adjacent pair of elements strictly increases.
I tried to execute this code but this isn't returning correct value for some lists.
def expanding(l):
for i in range(0,len(l)-3):
if (abs(l[i+2]-l[i+1])>abs(l[i+1]-l[i])):
Answer=True
else:
Answer=False
return Answer
expanding([1,3,7,2,-3]) should be False but the output is True.
Use a temporary variable to store the difference, and exit once you reach a non-increasing difference.
def expanding(l):
dif = abs(l[1] - l[0])
for i in range(1, len(l)-1):
temp = abs(l[i+1] - l[i])
# Non-increasing difference, return
if temp < dif:
return False
else:
dif = temp
# All differences are increasing
return True
Yet another solution using iterators:
from itertools import tee, islice, starmap
from operator import lt, sub
def pairwise(x):
a, b = tee(x, 2)
return zip(a, islice(b, 1, None))
a = [1,3,7,2,-3]
pairs = pairwise(a) # this will be (a[0], a[1]), (a[1], a[2]), ...
# The next will produce the result abs(a[1]-a[0]), abs(a[2]-a[1]), ...
differences = map(abs, starmap(sub, pairs))
# This will be abs(a[2]-a[1])>abs(a[1]-a[0]), abs(a[3]-a[2])>abs(a[2]-a[1]), ...
cmp = starmap(lt, pairwise(differences))
# Differences strictly increases if all items in cmp are evaluated to True...
result = all(cmp)
print(result)
The output for such input is False
Numpy is your friend:
import numpy as np
x=np.array([1,3,7,2,-3])
(np.diff(abs(x[1:] - x[:-1])) > 0).all() # returns False
If you're fancy a function, I would do like this:
def expanding(l):
# convert list to np.array if a list was passed
if isinstance(l, list):
l = np.array(l)
return (np.diff(abs(l[1:] - l[:-1])) > 0).all()
I would recommend writing the condition in an iterator that you then pass to any so to make sure that on the first occurrence of a non-expanding difference the iteration stops and False is returned.
Here is how this would look:
def expanding(l):
return not any((abs(l[i-1]-l[i])>=abs(l[i]-l[i+1]) for i in range(1,len(l)-1)))
def expanding(L):
x=list()
for i in range(len(L)-2):
if abs(L[i+1]-L[i+2]) > abs(L[i]-L[i+1]):
x.append(True)
else:
x.append(False)
return all(x)
print(expanding([1,3,7,2,9]))
Output:
True
Explanation:
when we calculate difference list for the given list -> [2,4,5,7]
The difference list is strictly increasing.
Print(expanding([1,3,7,2,-3]))
Output:
False
Explanation:
when we calculate difference list for the given list -> [2,4,5,5]
The difference list is not increasing (i.e., 5>5 is False)
def expanding(l):
for i in range(0,len(l)-2):
if (abs(l[i+2]-l[i+1])>abs(l[i+1]-l[i])):
Answer=True
else:
Answer=False
return Answer
return Answer
expanding([1,3,7,2,-3])
As soon as you know that one item is out of order you should answer False for the whole list without waiting,so that other pairs not change the answer.
Also note the change from range(0,len(l)-3) to range(0,len(l)-2). The original implementation was missing the last pair of list elements.
Your logic is a little bit wrong. Once you know that one item is out of order you should answer False for the whole list.
def expanding(l):
for i in range(0,len(l)-3):
if (abs(l[i+2]-l[i+1])<=abs(l[i+1]-l[i])):
return False
return True
Code Written By Debkanta Mondal
Python3
def expanding(l):
dif=[]
for x,y in zip(l,l[1:]):
diff=abs(y-x)
dif.append(diff)
return all(i<j for i,j in zip(dif,dif[1:]))
print(expanding([1,3,7,2,-3]) )
If I have this:
a='abcdefghij'
b='de'
Then this finds b in a:
b in a => True
Is there a way of doing an similar thing with lists?
Like this:
a=list('abcdefghij')
b=list('de')
b in a => False
The 'False' result is understandable - because its rightly looking for an element 'de', rather than (what I happen to want it to do) 'd' followed by 'e'
This is works, I know:
a=['a', 'b', 'c', ['d', 'e'], 'f', 'g', 'h']
b=list('de')
b in a => True
I can crunch the data to get what I want - but is there a short Pythonic way of doing this?
To clarify: I need to preserve ordering here (b=['e','d'], should return False).
And if it helps, what I have is a list of lists: these lists represents all possible paths (a list of visited nodes) from node-1 to node-x in a directed graph: I want to 'factor' out common paths in any longer paths. (So looking for all irreducible 'atomic' paths which constituent all the longer paths).
Related
Best Way To Determine if a Sequence is in another sequence in Python
I suspect there are more pythonic ways of doing it, but at least it gets the job done:
l=list('abcdefgh')
pat=list('de')
print pat in l # Returns False
print any(l[i:i+len(pat)]==pat for i in xrange(len(l)-len(pat)+1))
Don't know if this is very pythonic, but I would do it in this way:
def is_sublist(a, b):
if not a: return True
if not b: return False
return b[:len(a)] == a or is_sublist(a, b[1:])
Shorter solution is offered in this discussion, but it suffers from the same problems as solutions with set - it doesn't consider order of elements.
UPDATE:
Inspired by MAK I introduced more concise and clear version of my code.
UPDATE:
There are performance concerns about this method, due to list copying in slices. Also, as it is recursive, you can encounter recursion limit for long lists. To eliminate copying, you can use Numpy slices which creates views, not copies. If you encounter performance or recursion limit issues you should use solution without recursion.
I think this will be faster - It uses C implementation list.index to search for the first element, and goes from there on.
def find_sublist(sub, bigger):
if not bigger:
return -1
if not sub:
return 0
first, rest = sub[0], sub[1:]
pos = 0
try:
while True:
pos = bigger.index(first, pos) + 1
if not rest or bigger[pos:pos+len(rest)] == rest:
return pos
except ValueError:
return -1
data = list('abcdfghdesdkflksdkeeddefaksda')
print find_sublist(list('def'), data)
Note that this returns the position of the sublist in the list, not just True or False. If you want just a bool you could use this:
def is_sublist(sub, bigger):
return find_sublist(sub, bigger) >= 0
I timed the accepted solution, my earlier solution and a new one with an index. The one with the index is clearly best.
EDIT: I timed nosklo's solution, it's even much better than what I came up with. :)
def is_sublist_index(a, b):
if not a:
return True
index = 0
for elem in b:
if elem == a[index]:
index += 1
if index == len(a):
return True
elif elem == a[0]:
index = 1
else:
index = 0
return False
def is_sublist(a, b):
return str(a)[1:-1] in str(b)[1:-1]
def is_sublist_copylist(a, b):
if a == []: return True
if b == []: return False
return b[:len(a)] == a or is_sublist_copylist(a, b[1:])
from timeit import Timer
print Timer('is_sublist([99999], range(100000))', setup='from __main__ import is_sublist').timeit(number=100)
print Timer('is_sublist_copylist([99999], range(100000))', setup='from __main__ import is_sublist_copylist').timeit(number=100)
print Timer('is_sublist_index([99999], range(100000))', setup='from __main__ import is_sublist_index').timeit(number=100)
print Timer('sublist_nosklo([99999], range(100000))', setup='from __main__ import sublist_nosklo').timeit(number=100)
Output in seconds:
4.51677298546
4.5824368
1.87861895561
0.357429027557
So, if you aren't concerned about the order the subset appears, you can do:
a=list('abcdefghij')
b=list('de')
set(b).issubset(set(a))
True
Edit after you clarify: If you need to preserve order, and the list is indeed characters as in your question, you can use:
''.join(a).find(''.join(b)) > 0
This should work with whatever couple of lists, preserving the order.
Is checking if b is a sub list of a
def is_sublist(b,a):
if len(b) > len(a):
return False
if a == b:
return True
i = 0
while i <= len(a) - len(b):
if a[i] == b[0]:
flag = True
j = 1
while i+j < len(a) and j < len(b):
if a[i+j] != b[j]:
flag = False
j += 1
if flag:
return True
i += 1
return False
>>>''.join(b) in ''.join(a)
True
Not sure how complex your application is, but for pattern matching in lists, pyparsing is very smart and easy to use.
Use the lists' string representation and remove the square braces. :)
def is_sublist(a, b):
return str(a)[1:-1] in str(b)
EDIT: Right, there are false positives ... e.g. is_sublist([1], [11]). Crappy answer. :)
I'm currently working my way through a "List" unit and in one of the exercises we need to create an anagram (for those who don't know; two words are an anagram if you can rearrange the letters from one to spell the other).
The easier solution that comes to mind is:
def is_anagram(a, b):
return sorted(a) == sorted(b)
print is_anagram('god', 'dog')
It works, but it doesn't really satisfy me. If we were in this situation for example:
def is_anagram(a, b):
return sorted(a) == sorted(b)
print is_anagram('god', 'dyog') #extra letter 'd' in second argument
>>> False
Return is False, although we should be able to to build the word 'god' out of 'dyog'. Perhaps this game/problem isn't called an anagram, but I've been trying to figure it out anyway.
Technically my solution is to:
1- Iterate through every element of b.
2- As I do, I check if that element exists in a.
3- If all of them do; then we can create a out of b.
4- Otherwise, we can't.
I just can't seem to get it to work. For the record, I do not know how to use lambda
Some tests:
print is_anagram('god', 'ddog') #should return True
print is_anagram('god', '9d8s0og') #should return True
print is_anagram('god', '_#10d_o g') #should return True
Thanks :)
Since the other answers do not, at time of writing, support reversing the order:
Contains type hints for convenience, because why not?
# Just strip hints out if you're in Python < 3.5.
def is_anagram(a: str, b: str) -> bool:
long, short = (a, b) if len(a) > len(b) else (b, a)
cut = [x for x in long if x in short]
return sorted(cut) == sorted(short)
If, in future, you do learn to use lambda, an equivalent is:
# Again, strip hints out if you're in Python < 3.5.
def is_anagram(a: str, b: str) -> bool:
long, short = (a, b) if len(a) > len(b) else (b, a)
# Parentheses around lambda are not necessary, but may help readability
cut = filter((lambda x: x in short), long)
return sorted(cut) == sorted(short)
If you need to check if a word could be created from b you can do this
def is_anagram(a,b):
b_list = list(b)
for i_a in a:
if i_a in b_list:
b_list.remove(i_a)
else:
return False
return True
UPDATE(Explanation)
b_list = list(b) makes a list of str objects(characters).
>>> b = 'asdfqwer'
>>> b_list = list(b)
>>> b_list
['a', 's', 'd', 'f', 'q', 'w', 'e', 'r']
What basically is happening in the answer: We check if every character in a listed in b_list, when occurrence happens we remove that character from b_list(we do that to eliminate possibility of returning True with input 'good', 'god'). So if there is no occurrence of another character of a in rest of b_list then it's not advanced anagram.
def is_anagram(a, b):
test = sorted(a) == sorted(b)
testset = b in a
testset1 = a in b
if testset == True:
return True
if testset1 == True:
return True
else:
return False
No better than the other solution. But if you like verbose code, here's mine.
Try that:
def is_anagram(a, b):
word = [filter(lambda x: x in a, sub) for sub in b]
return ''.join(word)[0:len(a)]
Example:
>>> is_anagram('some','somexcv')
'some'
Note: This code returns the common word if there is any or '' otherwise. (it doesn't return True/False as the OP asked - realized that after, but anyway this code can easily change to adapt the True/False type of result - I won't fix it now such there are great answers in this post that already do :) )
Brief explanation:
This code takes(filter) every letter(sub) on b word that exists in a word also. Finally returns the word which must have the same length as a([0:len(a)]). The last part with len needs for cases like this:
is_anagram('some','somenothing')
I think all this sorting is a red herring; the letters should be counted instead.
def is_anagram(a, b):
return all(a.count(c) <= b.count(c) for c in set(a))
If you want efficiency, you can construct a dictionary that counts all the letters in b and decrements these counts for each letter in a. If any count is below 0, there are not enough instances of the character in b to create a. This is an O(a + b) algorithm.
from collections import defaultdict
def is_anagram(a, b):
b_dict = defaultdict(int)
for char in b:
b_dict[char] += 1
for char in a:
b_dict[char] -= 1
if b_dict[char] < 0:
return False
return True
Iterating over b is inevitable since you need to count all the letters there. This will exit as soon as a character in a is not found, so I don't think you can improve this algorithm much.
guys, I wrote a function to test if two inputs (a and b) have the same data structure, this way:
print same_structure([1, 0, 1], [a, b, c])
#>>> True
#print same_structure([1, [0], 1], [2, 5, 3])
>>> False
#print same_structure([1, [2, [3, [4, 5]]]], ['a', ['b', ['c', ['d', 'e']]]])
>>> True
#print same_structure([1, [2, [3, [4, 5]]]], ['a', ['b', ['c', ['de']]]])
>>> False
This function has (in my implementation) to use recursion. I'm very beginner in recursion and I still have difficulty on thinking recursively. In addition, to avoid cheating the answer, I want to use my own (messy) code and through it learn to use the recursive call (using this code line: 'same_structure return (a [i], b [e])' properly). Could someone show how to properly use the recursion in code below?
Thanks in advance for any help!!!
def is_list(p):
return isinstance(p, list)
def same_structure(a,b):
if not is_list(a) and not is_list(b):
print '#1'
return True
else:
if is_list(a) and is_list(b):
print '#2'
if len(a) != len(b):
print '#3'
return False
if len(a) == len(b):
print '#4'
for e in range(len(a)):
print 'e = ', e, 'a[e]= ', a[e], 'b[e]=', b[e]
return same_structure(a[e], b[e])
else:
return False
The following works:
def same_structure(a, b):
if isinstance(a, list) and isinstance(b, list) and len(a) == len(b):
return all(same_structure(A, B) for A, B in zip(a, b))
return (not isinstance(a, list) and not isinstance(b, list))
When writing recursive functions, you first need to come up with the base case, where you just return a value instead of calling any recursion. The base case here is one of the following conditions:
a is a list and b isn't, or vice-versa: return False
a and b are both lists, but they have different lengths: return False
neither a or b are lists: return True
If a and b are both lists and they have the same length, we need to now check each element of these lists recursively. zip(a, b) provides a convenient way to iterate over the elements from each list together, and if the result of same_structure() is False for any two sub-elements, we want the entire function to return False. This is why all() is used, if you are unfamiliar with all() it is equivalent (but more efficient) to the following loop:
match = True
for A, B in zip(a, b):
if not same_structure(A, B):
match = False
break
return match
Here is how you could rewrite your function without changing too much, the logic is actually very similar to my solution, but just below the print '#4' you were returning from that loop too early:
def same_structure(a,b):
if not is_list(a) and not is_list(b):
print '#1'
return True
else:
if is_list(a) and is_list(b):
print '#2'
if len(a) != len(b):
print '#3'
return False
if len(a) == len(b):
print '#4'
for e in range(len(a)):
print 'e = ', e, 'a[e]= ', a[e], 'b[e]=', b[e]
if not same_structure(a[e], b[e]):
return False
return True
else:
return False
Try this solution, it works with all your examples and it's written in a recursive, functional-programming style but without using zip, all, etc. only slices to reduce the size of the list at each step:
def same_structure(a, b):
if a == [] or b == []:
return a == b
elif is_list(a[0]) != is_list(b[0]):
return False
elif not is_list(a[0]):
return same_structure(a[1:], b[1:])
else:
return same_structure(a[0], b[0]) and same_structure(a[1:], b[1:])
You're only doing the first recursive call, since you're returning right away.
If I understood what you want to do, you need to is call same_structure with the sub-element, and check it's return value (not Return it immediately). If the result of any of the calls is false, return false, otherwise, return true.