Make the built in function len() in python - python

I'm trying to make a function to return the length of a list in python.
Code
def get_len(arr):
ind = 0
try:
x = arr[ind]
while x is not IndexError():
ind += 1
x = arr[ind]
except IndexError:
return ind
print(get_len([1, 2, 3, "one", "two"]))
Questions
Is it wrong to use try / except to get the length ?
Are there other ways to do it ?
Which resources to see the implementation of built-in function len ?

The while x is not IndexError() part is unnecessary (x will never equal an IndexError()). Here's a simpler version:
def get_len(arr):
ind = 0
while True:
try:
arr[ind]
except IndexError:
return ind
ind += 1
print(get_len([1, 2, 3, "one", "two"]))
The statement arr[ind] is sufficient to check for an error; if ind is equal to or greater than len(arr), the subscript operator will raise (not return) an IndexError.
The actual built-in function is typically not going to be one that's defined in Python itself. See for example the CPython implementation: https://github.com/python/cpython/blob/main/Objects/listobject.c#L429
Note that the real len() function is much faster than a reimplemented version can be because the list already "knows" its length and can return it instantly without having to count up the items one by one.

It will work, but it's just by accident. The while condition will always be true, because every call to IndexError() returns a new object, which is not the same object as any value in the list. You can replace that with just while True:.
The try/except is what detects the index error when you reach the end of the list.
There's no need to execute x = arr[ind] multiple times. Just put that inside the loop before incrementing ind.
def get_len(arr):
ind = 0
while True:
try:
x = arr[ind]
except IndexError:
return ind
ind += 1

Just to provide an alternative way of getting the length of a list or even arbitrarily long iterables, I've seen some library that had something like this to achieve that:
from collections import deque
def get_len(it):
try:
res, _ = deque(enumerate(it, 1), 1).pop()
except IndexError:
return 0
return res

Related

Python - Removing first two occurrences of element in list

The objective of this function is to remove the first two occurrences of n in a list.
Below is a code I had written but I still got it wrong after many hours. A friend advised me not to edit a list while iterating. However, I'm still stuck.
def remove_first_two(list,n):
if list == []:
return []
else:
count = 0
for ele in list:
if ele == n:
list.remove(ele)
count += 1
if count == 2:
break
return list
list = [1,2,2,3]
print(remove_first_two(list,2)) => [1,2,3] instead of [1,3]
Use list.remove twice with try-except. That will delete first two entries. Complexity O(n)
list_a = [1,2,3,4]
try:
list_a.remove(n)
list_a.remove(n)
# run a loop too, if it's more than 2
except:
pass
You can try find all indexes and del:
a = [1,2,3,2,3,2,4]
indices = [i for i, x in enumerate(a) if x == 2]
print(indices)
[1, 3, 5]
del a[indices[0]], a[indices[1]]
print(a)
[1, 3, 2, 2, 4]
First, don't use 'list' as its a key word in Python. Use something else, like 'alist'.
The code below does what you want and keeps the basic form of what you already have. You can of course also use the built-in .remove() method.
def remove_first_two(alist, n):
if alist == []:
return []
else:
count = 0
while count < 2:
for ele in alist:
if ele == n:
alist.remove(ele)
count += 1
return alist
alist = [1,2,2,3]
print(remove_first_two(alist,2)) # Output -> [1,3]
When your friend says "do not edit a list while iterating," he/she is right, and what he/she means is that you should create another list all together. What you are looking to do is the following:
def remove_first_two(list, n):
if list == []:
return []
else:
new_list = []
count = 0
for ele in list:
if ele == n:
if count >= 2:
new_list.append(ele)
count += 1
else:
new_list.append(ele)
return new_list
However, note that you can use use some built in functions to make your life much easier:
list.remove(x)
Remove the first item from the list whose value is equal to x. It raises a ValueError if there is no such item.
Therefore, you can more simply do:
def remove_first_two(list, n):
if list == []:
return []
for _ in range(2):
if n in list:
list.remove(n)
return list
Python updates the list if you change it while iterating.
In you test case with list = [1,2,2,3] when list[1] is deleted and Python updates list = [1,2,3]. Now Python understands you have iterated till index 1 and continues from index 2 which now contains 3. So Python encounters only one occurance of 2.
So heed your friends advice and do not edit list while iterating :)
Now you can use Python's in-built list.remove(element) to delete first ocuurence of a element. Repeat it 2 times for desired output.
Also O(n) with a single parse.
def remove_first_two(mylist,n):
counter = 0
def myfilter (i):
nonlocal counter,n
if counter > 2:
return True
else:
counter += 1
return (i != n)
return (list(filter(myfilter,mylist)))
This can also be done in python 3.8 using assignment expressions in a list comprehension:
data = [1,2,3,2,3,2,4]
count = 2
num = 2
[x for x in data if x != num or (count:=count-1) < 0]
Results:
[1, 3, 2, 2, 4]
Here is the reason why your program does not work:
When you remove an element, the for loop moves on to the next element, but by "moving on" it is actually skipping the element which now occupies the position of the deleted element. It skips the element right after the one you deleted.
The correct way to iterate over a list while you delete elements is making index progression explicit, by using a while loop instead of a for loop, and not increase the index when you delete an element:
i = 0
while i < len(my_list):
if condition:
my_list.pop(i)
else:
i += 1
However, none of this is necessary in your case! Notice that when you use my_list.remove(ele), you are not providing an index as you would with my_list.pop(i), so Python has to search for the first element that matches ele. Although remove will be slower than pop when used by themselves, here remove allows you not use any loops at all, simply do my_list.remove(n) twice!
Last touch: If your list has less than two elements matching n, one of the two my_list.remove(n) commands would return a ValueError. You can account for this exception, knowing that if it happens, your list is ready and requires no further action.
So the code you need is:
try:
my_list.remove(n)
my_list.remove(n)
except ValueError:
pass

How to count the amount of times an index has been changed in a list

So basically, I'm creating a function that takes a list and 2 values. The function replaces all occurrences of the first value with the second, The function has to then return the number of replacements that were completed.
I've managed to make the code work, but when it prints the number of replacements that were completed, it says 1, when there were in fact 2 replacements completed. It's as if it only checks for the first index and then stops. I don't where I went wrong.
def functionTwo(numberList, checkNum, checkNumTwo):
try:
while True:
num = numberList.index(checkNum)
numberList[num] = checkNumTwo
amount = numberList.count(checkNumTwo)
return amount
except ValueError:
pass
numberList = [4,8,22,43,42,12,1,10,1,10,32,28,8,42,13]
result = functionTwo(numberList, 1, 3)
print(result)
Using index repeatedly is an overly complex way to do it, it searches the entire list over and over, which is O(n2). Just iterate over the list once and replace the elements as you go, incrementing a counter.
def functionTwo(numberList, checkNum, checkNumTwo):
counter = 0
for i, val in enumerate(numberList):
if val == checkNum:
numberList[i] = checkNumTwo
counter += 1
return counter
However, to fix your code, just move return amount into the except ValueError: block. Your code was returning after the first replacement, so of course it returned only 1.
def functionTwo(numberList, checkNum, checkNumTwo):
try:
while True:
num = numberList.index(checkNum)
numberList[num] = checkNumTwo
except ValueError:
return numberList.count(checkNumTwo)
Note that using count() assumes that the list didn't contain any occurrences of checkNumTwo initially, because they'll be counted.

Python: Function to find out if a tuple contains even numbers?

So what I want is a function that gets a tuple input from a user and then figures out if that tuple contains an even number. I want to know how to do it with a for or while loop. I've tried it but the code doesn't work. I've kind of got something but it's not working:
def ifEven(x):
i = -1
if isinstance(x, tuple):
while i < len(x):
for i in x:
i = i + 1
if x[i] % 2 == 0:
return True
else:
return False
You should read the documentation about for statement in Python: https://docs.python.org/2/tutorial/controlflow.html#for-statements.
Here is a working code:
def ifEven(x):
if isinstance(x, tuple):
for i in x:
if i % 2 == 0:
return True
return False
That being said, it can be rewritten as a one-liner using Python's generator expressions:
def isEven(x):
return any(v % 2 == 0 for v in x)
You indentation looks a bit off but that might just be how you copy/pasted. True is "True" in python not "true" as in your return function which might also be messing you up.
In for loop i itself initializes and start iterating over the tuple. The content of i at each iteration is one of the element from the tuple.
Let's have a deeper look into it. let your tuple be (3, 4, 6, 7)
In first iteration :-
for i in x # i=3
i = i+1 # i = 4
if x[i] %2 == 0 : # x[4] % 2 == 0 => This will give you IndexError, since there is no index 4 in the tuple x(python uses zero based indexing)
So you don't need to increment i separately as for increments the iterating variable upto the end of the sequence provided (in your case it's upto the last element in the tuple x)
And since i posses the element from the tuple itself, so by incrementing it you are incrementing the value in i. And then you are accessing the element with incremented index from the tuple. That is, if tuple element is 3, you are first incrementing it by 1, so it becomes 4, and then you are checking whether the element in 4th index of the tuple is even or not i.e. if x[4] is even or not. But since the tuple that I have considered, is of lengthe 4, so it will throw IndexError
So, here you don't need to initialize i = -1, don't need to use while, no need to increment i inside for loop. Rest solution is given by Selcuk.

Search function python

Hi Im trying to create a search function in python, that goes through a list and searches for an element in it.
so far ive got
def search_func(list, x)
if list < 0:
return("failure")
else:
x = list[0]
while x > list:
x = list [0] + 1 <---- how would you tell python to go to the next element in the list ?
if (x = TargetValue):
return "success"
else
return "failure"
Well, you current code isn't very Pythonic. And there are several mistakes! you have to use indexes to acces an element in a list, correcting your code it looks like this:
def search_func(lst, x):
if len(lst) <= 0: # this is how you test if the list is empty
return "failure"
i = 0 # we'll use this as index to traverse the list
while i < len(lst): # this is how you test to see if the index is valid
if lst[i] == x: # this is how you check the current element
return "success"
i += 1 # this is how you advance to the next element
else: # this executes only if the loop didn't find the element
return "failure"
... But notice that in Python you rarely use while to traverse a list, a much more natural and simpler approach is to use for, which automatically binds a variable to each element, without having to use indexes:
def search_func(lst, x):
if not lst: # shorter way to test if the list is empty
return "failure"
for e in lst: # look how easy is to traverse the list!
if e == x: # we no longer care about indexes
return "success"
else:
return "failure"
But we can be even more Pythonic! the functionality you want to implement is so common that's already built into lists. Just use in to test if an element is inside a list:
def search_func(lst, x):
if lst and x in lst: # test for emptiness and for membership
return "success"
else:
return "failure"
Are you saying you want to see if an element is in a list? If so, there is no need for a function like that. Just use in:
>>> lst = [1, 2, 3]
>>> 1 in lst
True
>>> 4 in lst
False
>>>
This method is a lot more efficient.
If you have to do it without in, I suppose this will work:
def search_func(lst, x):
return "success" if lst.count(x) else "failure"
you dont need to write a function for searching, just use
x in llist
Update:
def search_func(llist,x):
for i in llist:
if i==x:
return True
return False
You are making your problem more complex, while solving any problem just think before starting to code. You are using while loops and so on which may sometimes becomes an infinite loop. You should use a for loop to solve it. This is better than while loop. So just check which condition helps you. That's it you are almost done.
def search_func(lst,x):
for e in lst: #here e defines elements in the given list
if e==x: #if condition checks whether element is equal to x
return True
else:
return False
def search(query, result_set):
if isinstance(query, str):
query = query.split()
assert isinstance(query, list)
results = []
for i in result_set:
if all(quer.casefold() in str(i).casefold() for quer in query):
results.append(i)
return results
Works best.

Recursive Function in Python adding Odd Values in List

My task is to create a recursive function in Python that takes a list and a value of 0 as its inputs and then adds up all of the odd numbers on the list and returns that value. Below is the code that I have and it keeps returning that the list index is out of range. No matter what I do I can not get it to work.
def addodds2(x,y):
total=0
a=x[y]
while y<len(x):
if a%2!=0:
total+=a
return(addodds2(x,y+1))
else:
return(addodds2(x,y+1))
return(total)
print(addodds2([3,2,4,7,2,4,1,3,2],0))
Since you are trying to solve this recursively, I don't think you want that while loop.
When you are trying to solve a problem recursively, you need two parts: you need a part that does some of the work, and you need a part that handles reaching the end of the work. This is the "basis case".
Often when solving problems like this, if you have a zero-length list you hit the basis case immediately. What should be the result for a zero-length list? I'd say 0.
So, here's the basic outline of a function to add together all the numbers in a list:
Check the length, and if you are already at the end or after the end, return 0. Otherwise, return the current item added to a recursive call (with the index value incremented).
Get that working, and then modify it so it only adds the odd values.
P.S. This seems like homework, so I didn't want to just give you the code. It's easier to remember this stuff if you actually figure it out yourself. Good luck!
Your code should be (the comments explain my corrections):
def addodds2(x,y):
total=0
if y<len(x): #you don't need a while there
a=x[y] #you have to do this operation if y<len(x), otherwise you would get the index error you are getting
if a%2!=0:
total+=a
return total+addodds2(x,y+1) #you have to sum the current total to the result returned by the addodds2() function (otherwise you would got 0 as the final result)
return total
print(addodds2([3,2,4,7,2,4,1,3,2],0))
while y<len(x)
So the last y which is smaller than len(x) is y = len(x) - 1, so it’s the very last item of the list.
addodds2(x,y+1)
Then you try to access the element after that item, which does not exist, so you get the IndexError.
This code can be very short and elegant:
def add_odds(lst, i=0):
try:
return (lst[i] if lst[i] % 2 == 0 else 0) + add_odds(lst, i+1)
except IndexError:
return 0
Note that, in a truly functional style, you wouldn't keep track of an index either. In Python, it would be rather inefficient, though, but recursion isn't recommended in Python anyway.
def add_odds2(lst):
try:
return (lst[-1] if lst[-1] % 2 == 0 else 0) + add_odds2(lst[:-1])
except IndexError:
return 0
To make it work with any kind of sequence, you can do the following:
def add_odds3(it):
it = iter(it)
try:
value = next(it)
return (value if value % 2 == 0 else 0) + add_odds3(it)
except StopIteration:
return 0
It's much more efficient, though there's not much sense in using an iterator recursively...
I realize that little of this is relevant for your (educational) purposes, but I just wanted to show (all of) you some nice Python. :)

Categories

Resources