How do I define my own range function in Python - python

Below I've attached a homework problem. So far my only code is.
def myRange(start, stop=None, step=None):
if stop == None:
stop = start
if step == None:
step = 1
while True:
if step > 0 and start >= stop:
break
elif step < 0 and start <= stop:
break
yield ('%g' % start)
start = start + step
break
lyst = myRange(start, stop=None, step=None)
for num in lyst:
print(num)
myRange(0, 10, 2)
I'm trying to get rid of the yeild and the '%g' statement and replace it with a return statement if anyone has any idea about how to do that. As well as that I am trying to make it so this truly works like a function and you can pass variables into it and get the range as a list, but I'm not sure how to do that either. Any help would be appreciated. I am very lost on this one.
Define and test a function myRange. This function should behave like
Python’s standard range function, with the required and optional
arguments, but it should return a list. Do not use the range function
in your implementation! (Hints: Study Python’s help on range to
determine the names, positions, and what to do with your function’s
parameters. Use a default value of None for the two optional
parameters. If these parameters both equal None, then the function has
been called with just the stop value. If just the third parameter
equals None, then the function has been called with a start value as
well. Thus, the first part of the function’s code establishes what the
values of the parameters are or should be. The rest of the code uses
those values to build a list by counting up or down.)

myRange(start=None, stop=10, step=None):
Dealing with stop=10
You set a default value of stop = 10. However, you created were asked to mimic the built-in
range function. The built in range does not have default stop=10.
So, do not set a default value in your version if
there is no default value in the original
Minimum number of args needed
Your myRange has 3 default arguments. Have you have ever seen the following code?
for x in range():
print(x)
Hint: range requires at least one argument.
Default value for start
10 is the stop. What is the first number printed by the following code? Is it 0? Is it 1? 2? 5? 10? What is it?
for x in range(10):
print(x)
Put the print outside of myRange
The indented block of code should not contain any print statements
def myRange(start=None, stop=10, step=None):
lyst = (list(range(stop)))
# print (lyst) Noooo!
lyst = myRange()
print(lyst) # YES!
You need a return statement
If a function has output we write return output You're supposed to return a list
This function should behave like Python’s standard range function, with the required and optional arguments, but it should return a list.
def myRange(start=None, stop=10, step=None):
lyst = list(range(stop))
# print (lyst) Noooo!
return lyst # New feature! return the output:)
lyst = myRange()
print(lyst) # YES!
Edit
I have decided to give you a function signature. You will still have to fill in the body. Don't worry about most of the code, leave it alone. Simply write your code at the very end, replacing the print("start:", start, "stop:", stop, "step:", step)
is_none = lambda x:\
x == None
count_non_none = lambda args, *, is_none=is_none: {\
sum(1 for _ in filter(is_none, args))
}
def myRange(_a1, _a2=None, _a3=None, *, start=None, stop=None, step=None):
args_non_none = count_non_none((_a1, _a2, _a3))
kwargs_non_none = count_non_none((start, stop, step))
total_non_none = args_non_none + kwargs_non_none
if total_non_none == 1:
# the one arg represents `stop`
assert(not start)
assert(not step)
stop = stop if stop else _a1
elif total_non_none == 2:
if args_non_none == 2:
start = _a1
stop = _a2
else # at least one keyword argument
assert(not (start and step))
L = [_a1, _a2, _a3]
while len(L) > 0:
if not start:
start = L.pop(0)
elif not stop:
stop = L.pop(0)
elif not step:
step = L.pop(0)
elif total_non_none == 3:
L = [_a1, _a2, _a3]
while len(L) > 0:
if not start:
start = L.pop(0)
elif not stop:
stop = L.pop(0)
elif not step:
step = L.pop(0)
else:
raise TypeError("more than 3 arguments were passed in")
if start == None:
start = 0
if step == None:
step = 1
print("start:", start, "stop:", stop, "step:", step)

Python allows you to accept any number of arguments and keyword arguments like this:
def myRange(*args, **kwargs):
# do something
args will be a tuple, and kwargs will be a dictionary.
For example, calling myRange(21, 'foo', name='shado') will have args be (21, 'foo') and kwargs be {'name': 'shado'}.
args and kwargs don't technically have to be args and kwargs but it is convention to name them that.
using this I imagine you can construct some conditionals based on the length of args
def myRange(*args):
if len(args) == 1:
start, end, step = 0, args[0], 1
# conditionals for other cases
another way to do it would be like this:
def myRange(start, stop=None, step=None):
if stop == None and step == None:
start, stop, step = 0, start, 1
# handle the other conditions
basically just handling every case separately
For making the list I would use a while loop.

Defined range w/o using a range function.
def myRange(*args):
myList=[]
if len(args) is 1:
k=0
if args[0] > 0:
while k < args[0]:
myList.append(k)
k=k+1
else:
while k > args[0]:
myList.append(k)
k=k-1
elif len(args) is 2:
k = args[0]
if args[0] < args[1]:
while k < args[1]:
myList.append(k)
k=k+1
else:
while k > args[1]:
myList.append(k)
k=k-1
else:
k=args[0]
if args[0] < args[1]:
while k < args[1]:
myList.append(k)
k=k+args[2]
else:
while k > args[1]:
myList.append(k)
k=k+args[2]
return myList

def myRange(start, step=None, stop=None):
if step == None and stop == None:
start, step, stop = 0, 1, start
while start < stop:
yield start
start += step
elif stop == None:
start, step, stop = start, 1, step
while start < stop:
yield start
start += step
while start < stop:
yield start
start += step
for i in myRange(1, 10, 100):
print(i)
or you can also implement with args too.
def custome_range(*args):
if len(args) == 1:
start, step, stop = 0, 1, args[0]
while start < stop:
yield start
start += step
elif len(args) == 2:
start, step, stop = args[0], 1, args[1]
while start < stop:
yield start
start += step
elif len(args) == 3:
start, step, stop = args[0], args[1], args[2]
while start < stop:
yield start
start += step
for i in custome_range(1, 10, 100):
print(i)

You can write this exclusively through if statements to determine which arguments are given when the function was called, followed by while statements to deliver the numbers in the range_list according to each parameter.
def myRange(a, b = None, step = None):
range_list = []
if b == None:
b = 0
if step == None:
while a > b:
range_list.append(b)
b += 1
while a < b:
range_list.append(a)
a += 1
elif step > 0:
while a < b:
range_list.append(a)
a += step
elif step < 0:
while a > b:
range_list.append(a)
a += step
else:
range_list = [a]
return range_list

Related

Rewrite the recursion function with loop

To rewrite the function without using recursion.
def func(num):
if num < 2:
return 1
else:
return func(num - 1) + func(num - 2)
# TODO:to rewrite the func using 'loop' instead of 'recursion'.
Below may be a solution, but i do not think it is a good way as i am using the convience of 'list'.
def func(num):
"""
I use a list to simulate a queue,
it is initialized with the param 'num' passed in,
and then judge it, if it ge 2, pop it and append the two nums 'num - 1', 'num - 2'.
And repeat it, if the number is lt 2, the index move to right by 1.
When the index is equal to the 'length - 1' of the queue, the queue if fulled with '0' and '1',
because 'func(0) = 1', 'func(1) = 1', the result is the length of the queue.
"""
index = 0
queue = [num]
while True:
value = queue[index]
if value >= 2:
queue.pop(index)
queue.append(value - 1)
queue.append(value - 2)
else:
if index == len(queue) - 1:
break
index += 1
return len(queue)
The task was interesting for me to solve, I cope with it the following way:
def f(num):
if num < 2: # margin condition
return 1
start_list = [num] # create initial list
while True:
new_list = [] # list to store new elements
changes = 0 # check if there was elements >=2
for element in start_list:
if element >= 2:
new_list.append(element - 1)
new_list.append(element - 2)
changes += 1
else:
new_list.append(element)
start_list = new_list # change start_list for new run
print(start_list)
if changes == 0:
return len(start_list)
I made performance check num = 25
my solution speed: 0.14056860000000002
recursion speed: 0.03910620000000001
your not recursion solution: 1.6991333
So it is 10 times faster.
Thank you for the task!

Heapsort in Python does not sorting

I am new to python and I am trying to implement heapsort from my C++ implementation. This code is unable to sort input.
I have written another program to test function build_max_heap and this function is unable to build max heap.
def max_heapify(thelist, lst_size, idx):
largest = idx
left_child = (2 * idx) + 1
right_child = (2 * idx) + 2
if left_child < lst_size and thelist[left_child] > thelist[largest]:
largest = left_child
elif right_child < len(thelist) and thelist[right_child] > thelist[largest]:
largest = right_child
if largest != idx:
thelist[idx], thelist[largest] = thelist[largest], thelist[idx]
max_heapify(thelist, lst_size, largest)
def build_max_heap(thelist, lst_size):
for curr_idx in range(lst_size // 2 - 1, -1, -1):
max_heapify(thelist, lst_size, curr_idx)
def heap_sort(thelist):
if len(thelist) == 0:
print("Empty list!!")
elif len(thelist) == 1:
print("Only one element!!")
else:
build_max_heap(thelist, len(thelist))
for curr_idx in range(len(thelist) -1, 0, -1):
thelist[curr_idx], thelist[0] = thelist[0], thelist[curr_idx]
max_heapify(thelist, curr_idx, 0)
There are two errors in your heapify function:
The second branch should not be an elif, but an if, since you'll want to select the right child, even if the left child is greater than its parent, but when the right child is even greater.
You don't want to use len(thelist) there, as your function should rely on the argument lst_size. This is needed because in the heap_sort function calls are made that pass a value for this argument that is (and must be) less than the actual list length.
So change:
elif right_child < len(thelist) and thelist[right_child] > thelist[largest]:
to:
if right_child < lst_size and thelist[right_child] > thelist[largest]:

Recursion in Python using function parameter to keep track but return value going missing

When recursively passing this string into this counting function, and declaring a set as a mutable function parameter to keep track of the results through the recursion, everything seems to work fine when stepping through with a debugger (and also by the print statements in the end test case), however the return result is None. What's going on here that's causing that?
def count_ways(data, l = set()):
if len(data) == 0:
print(l) # Shows my set has stuff
print(len(l)) # Shows my set has length
return(len(l)) # Why is None being returned?!
one_dig = int(data[:1])
two_digs = int(data[:2])
if (one_dig > 0):
l.add(one_dig)
if (10 <= two_digs <= 26):
l.add(two_digs)
count_ways(data[1:])
print(count_ways("124136"))
You need to return from the non-edge condition as well.
def count_ways(data, l = set()):
if len(data) == 0:
print(l) # Shows my set has stuff
print(len(l)) # Shows my set has length
return(len(l)) # Why is None being returned?!
one_dig = int(data[:1])
two_digs = int(data[:2])
if (one_dig > 0):
l.add(one_dig)
if (10 <= two_digs <= 26):
l.add(two_digs)
return count_ways(data[1:]) # return this!!
print(count_ways("124136"))
Just in case you're interested in another option, you don't really need the argument. You can just return the union of a local set with the result of the recursion:
def count_ways(data):
l = set()
if len(data) == 0:
return l
one_dig = int(data[:1])
two_digs = int(data[:2])
if (one_dig > 0):
l.add(one_dig)
if (10 <= two_digs <= 26):
l.add(two_digs)
return l | count_ways(data[1:])
print(count_ways("124136"))
# {1, 2, 3, 4, 6, 12, 13, 24}

python3 binary search not working [duplicate]

I am trying to implement the binary search in python and have written it as follows. However, I can't make it stop whenever needle_element is larger than the largest element in the array.
Can you help? Thanks.
def binary_search(array, needle_element):
mid = (len(array)) / 2
if not len(array):
raise "Error"
if needle_element == array[mid]:
return mid
elif needle_element > array[mid]:
return mid + binary_search(array[mid:],needle_element)
elif needle_element < array[mid]:
return binary_search(array[:mid],needle_element)
else:
raise "Error"
It would be much better to work with a lower and upper indexes as Lasse V. Karlsen was suggesting in a comment to the question.
This would be the code:
def binary_search(array, target):
lower = 0
upper = len(array)
while lower < upper: # use < instead of <=
x = lower + (upper - lower) // 2
val = array[x]
if target == val:
return x
elif target > val:
if lower == x: # these two are the actual lines
break # you're looking for
lower = x
elif target < val:
upper = x
lower < upper will stop once you have reached the smaller number (from the left side)
if lower == x: break will stop once you've reached the higher number (from the right side)
Example:
>>> binary_search([1,5,8,10], 5) # return 1
1
>>> binary_search([1,5,8,10], 0) # return None
>>> binary_search([1,5,8,10], 15) # return None
Why not use the bisect module? It should do the job you need---less code for you to maintain and test.
array[mid:] creates a new sub-copy everytime you call it = slow. Also you use recursion, which in Python is slow, too.
Try this:
def binarysearch(sequence, value):
lo, hi = 0, len(sequence) - 1
while lo <= hi:
mid = (lo + hi) // 2
if sequence[mid] < value:
lo = mid + 1
elif value < sequence[mid]:
hi = mid - 1
else:
return mid
return None
In the case that needle_element > array[mid], you currently pass array[mid:] to the recursive call. But you know that array[mid] is too small, so you can pass array[mid+1:] instead (and adjust the returned index accordingly).
If the needle is larger than all the elements in the array, doing it this way will eventually give you an empty array, and an error will be raised as expected.
Note: Creating a sub-array each time will result in bad performance for large arrays. It's better to pass in the bounds of the array instead.
You can improve your algorithm as the others suggested, but let's first look at why it doesn't work:
You're getting stuck in a loop because if needle_element > array[mid], you're including element mid in the bisected array you search next. So if needle is not in the array, you'll eventually be searching an array of length one forever. Pass array[mid+1:] instead (it's legal even if mid+1 is not a valid index), and you'll eventually call your function with an array of length zero. So len(array) == 0 means "not found", not an error. Handle it appropriately.
This is a tail recursive solution, I think this is cleaner than copying partial arrays and then keeping track of the indexes for returning:
def binarySearch(elem, arr):
# return the index at which elem lies, or return false
# if elem is not found
# pre: array must be sorted
return binarySearchHelper(elem, arr, 0, len(arr) - 1)
def binarySearchHelper(elem, arr, start, end):
if start > end:
return False
mid = (start + end)//2
if arr[mid] == elem:
return mid
elif arr[mid] > elem:
# recurse to the left of mid
return binarySearchHelper(elem, arr, start, mid - 1)
else:
# recurse to the right of mid
return binarySearchHelper(elem, arr, mid + 1, end)
def binary_search(array, target):
low = 0
mid = len(array) / 2
upper = len(array)
if len(array) == 1:
if array[0] == target:
print target
return array[0]
else:
return False
if target == array[mid]:
print array[mid]
return mid
else:
if mid > low:
arrayl = array[0:mid]
binary_search(arrayl, target)
if upper > mid:
arrayu = array[mid:len(array)]
binary_search(arrayu, target)
if __name__ == "__main__":
a = [3,2,9,8,4,1,9,6,5,9,7]
binary_search(a,9)
Using Recursion:
def binarySearch(arr,item):
c = len(arr)//2
if item > arr[c]:
ans = binarySearch(arr[c+1:],item)
if ans:
return binarySearch(arr[c+1],item)+c+1
elif item < arr[c]:
return binarySearch(arr[:c],item)
else:
return c
binarySearch([1,5,8,10,20,50,60],10)
All the answers above are true , but I think it would help to share my code
def binary_search(number):
numbers_list = range(20, 100)
i = 0
j = len(numbers_list)
while i < j:
middle = int((i + j) / 2)
if number > numbers_list[middle]:
i = middle + 1
else:
j = middle
return 'the index is '+str(i)
If you're doing a binary search, I'm guessing the array is sorted. If that is true you should be able to compare the last element in the array to the needle_element. As octopus says, this can be done before the search begins.
You can just check to see that needle_element is in the bounds of the array before starting at all. This will make it more efficient also, since you won't have to do several steps to get to the end.
if needle_element < array[0] or needle_element > array[-1]:
# do something, raise error perhaps?
It returns the index of key in array by using recursive.
round() is a function convert float to integer and make code fast and goes to expected case[O(logn)].
A=[1,2,3,4,5,6,7,8,9,10]
low = 0
hi = len(A)
v=3
def BS(A,low,hi,v):
mid = round((hi+low)/2.0)
if v == mid:
print ("You have found dude!" + " " + "Index of v is ", A.index(v))
elif v < mid:
print ("Item is smaller than mid")
hi = mid-1
BS(A,low,hi,v)
else :
print ("Item is greater than mid")
low = mid + 1
BS(A,low,hi,v)
BS(A,low,hi,v)
Without the lower/upper indexes this should also do:
def exists_element(element, array):
if not array:
yield False
mid = len(array) // 2
if element == array[mid]:
yield True
elif element < array[mid]:
yield from exists_element(element, array[:mid])
else:
yield from exists_element(element, array[mid + 1:])
Returning a boolean if the value is in the list.
Capture the first and last index of the list, loop and divide the list capturing the mid value.
In each loop will do the same, then compare if value input is equal to mid value.
def binarySearch(array, value):
array = sorted(array)
first = 0
last = len(array) - 1
while first <= last:
midIndex = (first + last) // 2
midValue = array[midIndex]
if value == midValue:
return True
if value < midValue:
last = midIndex - 1
if value > midValue:
first = midIndex + 1
return False

Python function to split and add lists recursively

I'm having trouble writing a function in python that will take a list, split it into 2 equal sides and then recursively add each element in each half. In the end returning the sum of both halves.
def sumLists(aList):
half = len(aList)//2
leftHalf = aList[half:]
rightHalf = aList[:half]
if len(aList) == 1:
return aList[0]
else:
sumOfLeft = sumLists(leftHalf[1:])
resultLeft = leftHalf[0] + sumOfLeft
sumOfRight = sumLists(rightHalf[1:])
resultRight = rightHalf[0] + sumOfRight
return resultLeft + resultRight
Any tips are appreciated, thanks!
You're overcomplicating the else block. You don't need to call sumLists on leftHalf[1:] and rightHalf[1:] and manually add the first respective elements; it suffices to call sumLists on the complete lists.
This slicing is what's causing your RuntimeError. A leftHalf with length one will have a leftHalf[1:] of length zero. But your function recurses forever for lengths of length zero because you didn't write an if case for that scenario.
You could rewrite your else so that it doesn't require slicing:
def sumLists(aList):
half = len(aList)//2
leftHalf = aList[half:]
rightHalf = aList[:half]
if len(aList) == 1:
return aList[0]
else:
return sumLists(leftHalf) + sumLists(rightHalf)
... Or you could add a special case for empty lists:
def sumLists(aList):
half = len(aList)//2
leftHalf = aList[half:]
rightHalf = aList[:half]
if len(aList) == 0:
return 0
elif len(aList) == 1:
return aList[0]
else:
sumOfLeft = sumLists(leftHalf[1:])
resultLeft = leftHalf[0] + sumOfLeft
sumOfRight = sumLists(rightHalf[1:])
resultRight = rightHalf[0] + sumOfRight
return resultLeft + resultRight
Or both:
def sumLists(aList):
half = len(aList)//2
leftHalf = aList[half:]
rightHalf = aList[:half]
if len(aList) == 0:
return 0
if len(aList) == 1:
return aList[0]
else:
return sumLists(leftHalf) + sumLists(rightHalf)
I think aList[half:] is the right side, and aList[:half] is the left side, is it right?
the follow code will sum the list left and right side, hope this can solve your problem.
def sumlist(l):
if not l or not isinstance(l, list):
return 0 # or return other default value.
if len(l) == 1:
return l[0]
half = len(l) // 2
left = l[:half] # left
right = l[-half:] # right
return sumlist(left) + sumlist(right)
test:
l = [1,2,3,4,5,6,7,8,9]
result = sumlist(l)
print(result) # 40
Maybe I misread your question, but do you actually need to do both things in one function?
To sum a list recursively, you could define that the empty list (your base case) has the sum 0. Any non-empty list has the sum of the first element plus the sum of the remainder (the "tail") of the list:
def sumList(a):
return 0 if not a else a[0] + sumList(a[1:])
To split a list recursively, you could keep track of the "left" and "right" list. The left list starts out being your input, the right list is empty. You then recursively prepend the last element of the left list to the right list until the right list is no longer shorter than the left:
def splitList(a, b=None):
b = b or []
return (a, b) if len(a) <= len(b) else splitList(a[:-1], [a[-1]] + b)
I suspect that passing around slices of lists is very inefficient in Python, so it might be better to rather pass around indices, e.g.
def sumList(a, idx=None):
idx = idx or 0
return 0 if idx >= len(a) else a[idx] + sumList(a, idx+1)

Categories