How to define a function is_member() that takes a value (i.e. a number, string, etc) x and a list of values a, and returns True if x is a member of a, False otherwise. (Note that this is exactly what the in operator does, but for the sake of the exercise I should pretend Python did not have this operator.
This is what I've come up with, but it doesn't work!
def is_member(x, a):
return x == a[::]
I can think of two (edit: three) ways to do this:
First:
def is_member(array, value):
try:
array.index(value)
except ValueError:
return False
else:
return True
Second:
def is_member(array, value):
for item in array:
if item == value:
return True
return False
EDIT: Also, third:
def is_member(array, value):
return array.count(value) > 0
Recursive solution:
def is_member(value, array):
if len(array) == 0:
return False
return value == array[0] or is_member(value, array[1:])
Using a generator expression (note that this in operator has nothing to do with the another one)
def is_member(x, a):
return any(x == y for y in a)
>>> is_member(10, xrange(1000000000000000))
True
You could simply just iterate over every element in the list then:
def is_member(col, a):
for i in xrange(len(col)):
if a == col[i]: return True
return False
>> a = [1,2,3,4]
>> is_member(a, 2)
True
>> is_member(a, 5)
False
Without using the "in" operator:
from itertools import imap
def is_member( item, array ):
return any( imap(lambda x: x == item, array ) )
which will cycle through the items of the list, one at a time, and short circuit when it hits a value that is True.
Well, there are a lot of ways to do this, of course -- but you're a little hamstrung by the prohibition of "in" anywhere in the code. Here are a few things to try.
Variations on a theme ...
def is_member(item, seq):
return sum(map(lambda x: x == item, seq)) > 0
def is_member(item, seq):
return len(filter(lambda x: x != item, seq)) != len(seq)
You may have heard that asking for forgiveness is better than asking for permission ...
def is_member(item, seq):
try:
seq.index(item)
return True
except:
return False
Or something a little more functional-flavored ...
import itertools, operator, functools
def is_member(item, seq):
not_eq = functools.partial(operator.ne, item)
return bool(list(itertools.dropwhile(not_eq, seq)))
But, since your requirements preclude the use of the looping construct which would be most reasonable, I think the experts would recommend writing your own looping framework. Something like ...
def loop(action, until):
while True:
action()
if until():
break
def is_member(item, seq):
seq = seq
sigil = [False]
def check():
if seq[0] == item:
sigil[0] = True
def til():
seq.remove(seq[0])
return not len(seq)
loop(check, til)
return sigil[0]
Let us know how it goes.
Related
I would like to make a function with an input string (for example 'abcdef'), and returns it in reverse order 'fedcba'.
However, the challenge is that the function
must use a recursive function
is without using for-loop
is without any operators
is without list slicing
This is my attempt which does not work:
def reverse(s: str) -> str
if len(s) == 0:
return None
return
How do I use the recursive function here to reverse order? I tried to think of it but i'm new to recursive calls
"without using for loops or any operators or list slicing" seems like a weird requirement, but the following function will do:
>>> def reverse(s):
... head, *tail = s
... if tail:
... return f'{reverse(tail)}{head}'
... else:
... return head
...
>>> reverse('abcdef')
'fedcba'
In the scope of lexical analysis, * is regarded as an operator, so we can replace head, *tail = s with the following:
import re
def reverse(s):
head, tail = re.split('(?!^)', s, maxsplit=1)
[...]
Or, alternatively:
def reverse(s):
__, head, tail = s.partition(next(iter(s))
[...]
Or, yet another alternative:
def reverse(s):
s = iter(s)
head, tail = next(s), ''.join(s)
[...]
Edit: I have removed the '+' operator, and also the lstrip() which does not work on repeated characters in the string (thanks #philosofool)
Here's one way to do it without list slicing. And to clarify s[0] is list indexing not list slicing, correct?
def reverse(s):
if len(s)==1:
return s
else:
s1 = list(s)
del s1[0]
return ''.join([reverse(''.join(s1)), s[0]])
reverse('abccdefg')
output is
'gfedccba'
This is not actually a solution. It meets all the requirements except the recursive one. It's a nice, purely functional solution, but not what OP asked for. Leaving it up in case anyone is interested....
from functools import reduce
def reverse_string(string):
return reduce(lambda x, y: f'{y}{x}', string)
Using only functions
def reverse(strng, pos):
if pos:
next_pos = map(lambda x: x, range(pos,0,-1))
next(next_pos) # skip first
return ''.join((strng[pos],reverse(strng, next(next_pos)))
else:
return strng[pos]
Another one inspired by #Ecin
def reverse(strng : str):
def inner(strng : list):
return ''.join((strng.pop(), inner(strng) if strng else ''))
return inner(list(strng))
In this recursive code, i'm getting the function to properly return True, but then it proceeds for 1 additional step and changes the return value to "None". I believe I don't understand return values properly. Can someone tell me why this is happening? Thank you in advance.
--
def nestedListContains(NL, target):
for i in range(0, len(NL)):
if type(NL[i]) == int:
if NL[i] == target:
return True
elif i == (len(NL) - 1):
return False
elif type(NL[i]) != int:
nestedListContains(NL[i], target)
nestedListContains([[9, 4, 5], [3, 8]], 3) #Test Case#
There are three problems with your code; first, you do nothing with the result of the recursive call. Second, you should use isinstance() to check if a value is of some type, not type(ob) ==. Third, instead of using range() and check i to see if the last value was reached, just return False after the loop if nothing was found.
In total:
def nestedListContains(NL, target):
for value in NL:
if isinstance(value, int):
test = value == target
else:
# value is not an int, so it's a list
test = nestedListContains(value, target)
if test:
return True # found target
return False # We finished the loop without finding target
This will throw a TypeError if NL isn't a list at all. Which is perhaps an even better check than isinstance() -- if it can be iterated then it's a list, and if iteration throws a TypeError it's something we should compare to target. I'll also make the naming a bit more standard:
def nested_list_contains(nested_list, target):
try:
for value in nested_list:
if nested_list_contains(value, target):
return True
return False
except TypeError:
# It's a single value
return nested_list == target
But there's an even better way. What we really want to do is flatten the nested list, and check to see if the target is in it. We can turn the above into a generator that flattens iterables recursively:
def flatten_nested_list(nested_list):
try:
for v in nested_list:
for flattened in flatten_nested_list(v):
yield flatten
except TypeError:
yield nested_list
def nested_list_contains(nested_list, target):
return target in flatten_nested_list(nested_list)
To use recursion you have to return your recursive result:
def nestedListContains(NL, target):
for i in range(0, len(NL)):
if type(NL[i]) == int:
if NL[i] == target:
return True
elif i == (len(NL) - 1):
return False
elif type(NL[i]) != int:
#you missed the return for the recursive case
ret = nestedListContains(NL[i], target)
if(type(ret) == bool): #ensure we have an actual result and not a fall through
return ret
To elaborate a little on the earlier answer, you get None when you reach the end of the function without returning a specific value:
def func():
... do something ...
is equivalent to:
def func():
... do something ...
return None
This will still happen if you pass in an empty NL, since there's no return statement after the loop.
(also, isinstance(value, int) is the preferred way to check the type of something)
For example, something like:
>>> [1, 2, 3].contains_sequence([1, 2])
True
>>> [1, 2, 3].contains_sequence([4])
False
I know that the in operator can do this for strings:
>>> "12" in "123"
True
But I'm looking for something that operates on iterables.
Referenced from https://stackoverflow.com/a/6822773/24718
modified to use a list.
from itertools import islice
def window(seq, n=2):
"""
Returns a sliding window (of width n) over data from the iterable
s -> (s0,s1,...s[n-1]), (s1,s2,...,sn), ...
"""
it = iter(seq)
result = list(islice(it, n))
if len(result) == n:
yield result
for elem in it:
result = result[1:] + [elem]
yield result
def contains_sequence(all_values, seq):
return any(seq == current_seq for current_seq in window(all_values, len(seq)))
test_iterable = [1,2,3]
search_sequence = [1,2]
result = contains_sequence(test_iterable, search_sequence)
Is there a Python builtin? No. You can accomplish this task in various ways. Here is a recipe that does it, and also gives you the position of the subsequence in the containing sequence:
def _search(forward, source, target, start=0, end=None):
"""Naive search for target in source."""
m = len(source)
n = len(target)
if end is None:
end = m
else:
end = min(end, m)
if n == 0 or (end-start) < n:
# target is empty, or longer than source, so obviously can't be found.
return None
if forward:
x = range(start, end-n+1)
else:
x = range(end-n, start-1, -1)
for i in x:
if source[i:i+n] == target:
return i
return None
As far as I know, there's no way to do this. You can roll your own function pretty easily, but I doubt that will be terribly efficient.
>>> def contains_seq(seq,subseq):
... #try: junk=seq[:]
... #except: seq=tuple(seq)
... #try: junk=subseq[:]
... #except: subseq=tuple(subseq)
... ll=len(subseq)
... for i in range(len(seq)-ll): #on python2, use xrange.
... if(seq[i:i+ll] == subseq):
... return True
... return False
...
>>> contains_seq(range(10),range(3)) #True
>>> contains_seq(range(10),[2,3,6]) #False
Note that this solution does not work with generator type objects (it only works on objects that you can slice). You could check seq to see if it is sliceable before proceeding and cast to a tuple if it isn't sliceable -- But then you get rid of the benefits of slicing. You could re-write it to check one element at a time instead of using slicing, but I have a feeling performance would suffer even more.
If preserving of order is not necessary, you can use sets (builtin):
>>> set([1,2]).issubset([1,2,3])
True
>>> set([4]).issubset([1,2,3])
False
Otherwise:
def is_subsequence(sub, iterable):
sub_pos, sub_len = 0, len(sub)
for i in iterable:
if i == sub[sub_pos]:
sub_pos += 1
if sub_pos >= sub_len:
return True
else:
sub_pos = 0
return False
>>> is_subsequence([1,2], [0,1,2,3,4])
True
>>> is_subsequence([2,1], [0,1,2,3,4]) # order preserved
False
>>> is_subsequence([1,2,4], [0,1,2,3,4])
False
This one works with any iterator.
As others have said, there's no builtin for this. Here's an implementation that is potentially more efficient than the other answers I've seen -- in particular, it scans through the iterable, just keeping track of what prefix sizes of the target sequence it's seen. But that increased efficiency comes at some expense in increased verbosity over some of the other approaches that have been suggested.
def contains_seq(iterable, seq):
"""
Returns true if the iterable contains the given sequence.
"""
# The following clause is optional -- leave it if you want to allow `seq` to
# be an arbitrary iterable; or remove it if `seq` will always be list-like.
if not isinstance(seq, collections.Sequence):
seq = tuple(seq)
if len(seq)==0: return True # corner case
partial_matches = []
for elt in iterable:
# Try extending each of the partial matches by adding the
# next element, if it matches.
partial_matches = [m+1 for m in partial_matches if elt == seq[m]]
# Check if we should start a new partial match
if elt==seq[0]:
partial_matches.append(1)
# Check if we have a complete match (partial_matches will always
# be sorted from highest to lowest, since older partial matches
# come before newer ones).
if partial_matches and partial_matches[0]==len(seq):
return True
# No match found.
return False
deque appears to be useful here:
from collections import deque
def contains(it, seq):
seq = deque(seq)
deq = deque(maxlen=len(seq))
for p in it:
deq.append(p)
if deq == seq:
return True
return False
Note that this accepts arbitrary iterables for both arguments (no slicing required).
As there's no builtin, I made a nice version:
import itertools as it
def contains(seq, sub):
seq = iter(seq)
o = object()
return any(all(i==j for i,j in zip(sub, it.chain((n,),seq,
(o for i in it.count())))) for n in seq)
This do not require any extra lists (if you use it.izip or Py3k).
>>> contains([1,2,3], [1,2])
True
>>> contains([1,2,3], [1,2,3])
True
>>> contains([1,2,3], [2,3])
True
>>> contains([1,2,3], [2,3,4])
False
Extra points if you have no trouble reading it. (It does the job, but the implementation is not to be taked too seriously). ;)
You could convert it into a string and then do matching on it
full_list = " ".join([str(x) for x in [1, 2, 3]])
seq = " ".join([str(x) for x in [1, 2]])
seq in full_list
I wrote this function to filter a list to show all items after a given item is seen. Somewhat similar to the builtin string method str.rpartition(sep). I have a feeling there is a more compact way to do this, perhaps using a list comprehension. Any ideas?
def ignore_until(the_list, match):
# Ignore all items in the_list prior to match
found = False
for index, item in enumerate(the_list):
if item == match:
found = True
break
if found:
return the_list[index:]
else:
return []
my_list = ['red','orange','yellow','green']
assert ignore_until(my_list, 'yellow') == ['yellow','green']
assert ignore_until(my_list, 'blue') == []
EDIT:
After seeing the answers for the above question, I realized that 5 of the 6 answers focused on the index() builtin method for the list data type. Actually, I need to use a regular expression, and didn't realize that omitting that from my question would affect people's answers. Here's the regex code:
import re
def ignore_until(the_list, pattern):
# Ignore all items in the_list prior to the item containing pattern.
found = False
for index, item in enumerate(the_list):
if re.search(string=item, pattern=pattern):
found = True
break
if found:
return the_list[index:]
else:
return []
my_list = ['red','orange','yellow','green']
assert ignore_until(my_list, 'yellow') == ['yellow','green']
assert ignore_until(my_list, 'blue') == []
It's not much more compact, but how about:
def ignore_until(the_list, match):
try:
return the_list[the_list.index(match):]
except ValueError:
return []
my_list = ['red','orange','yellow','green']
print ignore_until(my_list, 'yellow') # => ['yellow','green']
print ignore_until(my_list, 'blue') # => []
why not use the python yourlist.index(match) to find the index then apply list slicing. The python yourlist.index throws an error if the match is not found so you would need to take care of that.
def ignore_until(yourlist, match):
try:
return yourlist[yourlist.index(match):]
except ValueError:
return []
Here's a version that reproduces what str.partition does (i.e. returns three lists):
def partition(lst, item):
if item in lst:
n = lst.index(item)
return lst[:n], [item], lst[n+1:]
else:
return lst, [], []
print partition(range(10), 7)
and here's a version which works with arbitrary iterables, not necessary lists:
def partition(it, item):
a = [[]]
for x in it:
if x == item and len(a) == 1:
a.append([item])
a.append([])
else:
a[-1].append(x)
return a
print partition((x for x in range(10)), 7)
Improved version:
def partition(it, item):
a = []
for x in it:
if x == item:
return a, [item], list(it)
a.append(x)
return a, [], []
print partition((x for x in range(10)), 7)
print partition((x for x in range(10)), 17)
Try this:
def ignore_until(the_list, match):
try:
return [the_list[the_list.index(object):] for object in l if object == match][0]
except IndexError:
return []
A little hard to read but it's compact.
Have you thought about using the list.index() method? It returns the index of the first instance of a specified item (or else it throws an error)
def ignore_until(the_list, match):
if match in the_list:
index = the_list.index(match)
return the_list[index:]
else:
return []
source: http://docs.python.org/tutorial/datastructures.html
def ignore_until(the_list, match):
try:
return my_list[the_list.index(match):]
except ValueError:
return []
I just invented a stupid little helper function:
def has_one(seq, predicate=bool):
"""Return whether there is exactly one item in `seq` that matches
`predicate`, with a minimum of evaluation (short-circuit).
"""
iterator = (item for item in seq if predicate(item))
try:
iterator.next()
except StopIteration: # No items match predicate.
return False
try:
iterator.next()
except StopIteration: # Exactly one item matches predicate.
return True
return False # More than one item matches the predicate.
Because the most readable/idiomatic inline thing I could come up with was:
[predicate(item) for item in seq].count(True) == 1
... which is fine in my case because I know seq is small, but it just feels weird. Is there an idiom I’m forgetting here that prevents me from having to break out this helper?
Clarification
Looking back on it, this was kind of a crappily posed question, though we got some excellent answers! I was looking for either:
An obvious and readable inline idiom or stdlib function, eager evaluation being acceptable in this case.
A more obvious and readable helper function -- since it's breaking out a whole other function, only the minimum amount of evaluation seems acceptable.
#Stephan202 came up with a really cool idiom for the helper function and #Martin v. Löwis came up with a more simple inline idiom under the assumption that the predicate returns a bool. Thanks # everybody for your help!
How about calling any twice, on an iterator (Python 2.x and 3.x compatible)?
>>> def has_one(seq, predicate=bool):
... seq = (predicate(e) for e in seq)
... return any(seq) and not any(seq)
...
>>> has_one([])
False
>>> has_one([1])
True
>>> has_one([0])
False
>>> has_one([1, 2])
False
any will take at most one element which evaluates to True from the iterator. If it succeeds the first time and fails the second time, then only one element matches the predicate.
Edit: I see Robert Rossney suggests a generalized version, which checks whether exactly n elements match the predicate. Let me join in on the fun, using all:
>>> def has_n(seq, n, predicate=bool):
... seq = (predicate(e) for e in seq)
... return all(any(seq) for _ in range(n)) and not any(seq)
...
>>> has_n(range(0), 3)
False
>>> has_n(range(3), 3)
False
>>> has_n(range(4), 3)
True
>>> has_n(range(5), 3)
False
Perhaps something like this is more to your taste?
def has_one(seq,predicate=bool):
nwanted=1
n=0
for item in seq:
if predicate(item):
n+=1
if n>nwanted:
return False
return n==nwanted
This is rather like the list comprehension example, but requires only one pass over one sequence. Compared to the second has_one function, and like the list comprehension code, it generalizes more easily to other counts. I've demonstrated this (hopefully without error...) by adding in a variable for the number of items wanted.
I liked Stephan202's answer, but I like this one a little more, even though it's two lines instead of one. I like it because it's just as crazy but a tiny bit more explicit about how its craziness works:
def has_one(seq):
g = (x for x in seq)
return any(g) and not any(g)
Edit:
Here's a more generalized version that supports a predicate:
def has_exactly(seq, count, predicate = bool):
g = (predicate(x) for x in seq)
while(count > 0):
if not any(g):
return False
count -= 1
if count == 0:
return not any(g)
Not sure whether it is any better than the versions you proposed, however...
If predicate is guaranteed to return True/False only, then
sum(map(predicate, seq)) == 1
will do (although it won't stop at the second element)
How about ...
import functools
import operator
def exactly_one(seq):
"""
Handy for ensuring that exactly one of a bunch of options has been set.
>>> exactly_one((3, None, 'frotz', None))
False
>>> exactly_one((None, None, 'frotz', None))
True
"""
return 1 == functools.reduce(operator.__add__, [1 for x in seq if x])
Look, Ma! No rtfm("itertools"), no dependency on predicate() returning a boolean, minimum evaluation, just works!
Python 1.5.2 (#0, Apr 13 1999, 10:51:12) [MSC 32 bit (Intel)] on win32
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
>>> def count_in_bounds(seq, predicate=lambda x: x, low=1, high=1):
... count = 0
... for item in seq:
... if predicate(item):
... count = count + 1
... if count > high:
... return 0
... return count >= low
...
>>> seq1 = [0, 0, 1, 0, 1, 0, 1, 0, 0, 0]
>>> count_in_bounds(seq1)
0
>>> count_in_bounds(seq1, low=3, high=3)
1
>>> count_in_bounds(seq1, low=3, high=4)
1
>>> count_in_bounds(seq1, low=4, high=4)
0
>>> count_in_bounds(seq1, low=0, high=3)
1
>>> count_in_bounds(seq1, low=3, high=3)
1
>>>
Here's modified #Stephan202's answer:
from itertools import imap, repeat
def exactly_n_is_true(iterable, n, predicate=None):
it = iter(iterable) if predicate is None else imap(predicate, iterable)
return all(any(it) for _ in repeat(None, n)) and not any(it)
Differences:
predicate() is None by default. The meaning is the same as for built-in filter() and stdlib's itertools.ifilter() functions.
More explicit function and parameters names (this is subjective).
repeat() allows large n to be used.
Example:
if exactly_n_is_true(seq, 1, predicate):
# predicate() is true for exactly one item from the seq
This and this straightforward counting-loop solutions are definitely clearest.
For the sport of it, here is a variation on the any(g) and not any(g) theme that looks less magic on the surface - but it's actually similarly fragile when one comes to debug/modify it (you can't exchange the order, you have to understand how the short-circuiting and hands off a single iterator between two short-circuiting consumers...).
def cumulative_sums(values):
s = 0
for v in values:
s += v
yield s
def count_in_bounds(iterable, start=1, stop=2):
counter = cumulative_sums(bool(x) for x in iterable)
return (start in counter) and (stop not in counter)
It's trivial to also take a predicate instead of bool but I think it's better to follow any() and all() in leaving that to the caller - it's easy to pass a generator expression if needed.
Taking arbitrary [start, stop) is a nice bonus, but it's not as generic as I'd like. It's tempting to pass stop=None to emulate e.g. any(), which works, but always consumes all input; the proper emulation is kinda awkward:
def any(iterable):
return not count_in_bounds(iterable, 0, 1)
def all(iterable):
return count_in_bounds((not x for x in iterable), 0, 1)
Taking a variable number of bounds and specifying which should return True/False would get out of hand.
Perhaps a simple saturating counter is the best primitive:
def count_true(iterable, stop_at=float('inf')):
c = 0
for x in iterable:
c += bool(x)
if c >= stop_at:
break
return c
def any(iterable):
return count_true(iterable, 1) >= 1
def exactly_one(iterable):
return count_true(iterable, 2) == 1
def weird(iterable):
return count_true(iterable, 10) in {2, 3, 5, 7}
all() still requires negating the inputs, or a matching count_false() helper.