In: Computer Science
Implement a Bag ADT using Dynamic Array structure as underlying data storage for Bag ADT.
RESTRICTIONS: Not allowed to use ANY built-in Python data structures and their methods. You must solve by importing the DynamicArray class and using class methods to write solution. Also not allowed to directly access any variables of the DynamicArray class (like self.size, self.capacity and self.data in part 1). All work must be done by only using class methods.
Below is the Bag ADT starter code and the Dynamic Array program. Basic testing below shows how the code might be used.
Bag Methods to implement:
add (self, value: object) -> None: This method adds a new element to the bag.remove()
remove (self, value: object) -> bool: This method removes any one element from the bag that matches the provided “value” object. Method returns True if some object was actually removed from the bag. Otherwise it returns False.
count (self, value: object) -> int: This method counts the number of elements in the bag that match the provided “value” object.
clear (self) -> None: This method clears the content of the bag.
size (self) -> int: This method returns the number of elements currently in the bag.
equal (self, second_bag: object) -> bool: This method compares the content of the bag with the content of the second bag provided by the user. Method returns True if the bags are equal (have the same number of elements and contain the same elements without regards to the order of elements). Otherwise it returns False. Empty bag is only considered equal to another empty bag. This method should not change the contents of either bag.
#Dynamic Array Code
class DynamicArrayException(Exception): """ Custom exception class to be used by Dynamic Array DO NOT CHANGE THIS METHOD IN ANY WAY """ pass class DynamicArray: def __init__(self, start_array=None): """ Initialize new dynamic array DO NOT CHANGE THIS METHOD IN ANY WAY """ self.size = 0 self.capacity = 4 self.data = [None] * self.capacity # populate dynamic array with initial values (if provided) # before using this feature, implement append() method if start_array is not None: for value in start_array: self.append(value) def __str__(self) -> str: """ Return content of dynamic array in human-readable form DO NOT CHANGE THIS METHOD IN ANY WAY """ out = "DYN_ARR Size/Cap: " out += str(self.size) + "/"+ str(self.capacity) out += " " + str(self.data[:self.size]) return out def resize(self, new_capacity: int) -> None: if (new_capacity == 0) or (new_capacity < self.size): return new_data = [None] * new_capacity for i in range(self.size): new_data[i] = self.data[i] self.capacity = new_capacity self.data = new_data return def append(self, value: object) -> None: if self.is_full(): self.resize(self.capacity * 2) self.data[self.size] = value self.size += 1 return def insert_at_index(self, index: int, value: object) -> None: if (index < 0) or (index > self.size): raise DynamicArrayException if self.is_full(): self.resize(self.size * 2) for current_index in range(self.size, index, -1): self.data[current_index] = self.data[current_index - 1] self.data[index] = value self.size += 1 return def get_at_index(self, index: int) -> object: if (index < 0) or (index > self.size - 1): raise DynamicArrayException return self.data[index] def remove_at_index(self, index: int) -> None: if (index < 0) or (index > self.size - 1): raise DynamicArrayException if (self.size < (0.25 * self.capacity)) and self.capacity > 10: if self.size < 5: self.resize(10) else: self.resize(self.size * 2) for i in range(index, self.size): if i == (self.size - 1): self.data[i] = None else: self.data[i] = self.data[i + 1] self.size -= 1 return def is_empty(self) -> bool: return self.size == 0 def length(self) -> int: return self.size def slice(self, start_index: int, quantity: int) -> object: if (start_index < 0) or (start_index > self.size - 1): raise DynamicArrayException if (quantity < 0) or (quantity > (self.size - start_index)): raise DynamicArrayException new_array = DynamicArray() for i in range(start_index, (start_index + quantity)): new_array.append(self.data[i]) return new_array def reverse(self) -> None: if self.size <= 1: return front_index = 0 back_index = self.size - 1 while front_index < (self.size / 2): front_val = self.get_at_index(front_index) back_val = self.get_at_index(back_index) self.remove_at_index(front_index) self.insert_at_index(front_index, back_val) self.remove_at_index(back_index) if back_index == self.size: self.append(front_val) else: self.insert_at_index(back_index, front_val) front_index += 1 back_index -= 1 return def sort(self) -> None: if self.size <= 1: return current_index = 1 while current_index < self.size: comparison_index = current_index - 1 sorting = True while sorting: current_val = self.get_at_index(current_index) if current_val < self.get_at_index(comparison_index): if comparison_index == 0: self.remove_at_index(current_index) self.insert_at_index(0, current_val) sorting = False else: comparison_index -= 1 else: self.remove_at_index(current_index) self.insert_at_index(comparison_index + 1, current_val) sorting = False current_index += 1 return def merge(self, another_list: object) -> None: # Iterate the given list values and append for i in range(another_list.length()): self.append(another_list.get_at_index(i)) return def is_full(self) -> bool: if self.size == self.capacity: return True return False
#Bag Starter Code
from dynamic_array import * class Bag: def __init__(self, start_bag=None): """ Init new bag based on Dynamic Array DO NOT CHANGE THIS METHOD IN ANY WAY """ self.da = DynamicArray() # populate bag with initial values (if provided) # before using this feature, implement add() method if start_bag is not None: for value in start_bag: self.add(value) def __str__(self) -> str: """ Return content of bag in human-readable form DO NOT CHANGE THIS METHOD IN ANY WAY """ out = "BAG: " + str(self.da.size) + " elements. " out += str(self.da.data[:self.da.size]) return out def add(self, value: object) -> None: """ TODO: Write this implementation """ return def remove(self, value: object) -> bool: """ TODO: Write this implementation """ return False def count(self, value: object) -> int: """ TODO: Write this implementation """ return 0 def clear(self) -> None: """ TODO: Write this implementation """ return def size(self) -> int: """ TODO: Write this implementation """ return 0 def equal(self, second_bag: object) -> bool: """ TODO: Write this implementation """ return False
##Basic Testing
if __name__ == "__main__": pass
##Add Example Test:
bag = Bag()
print(bag)
values = [10, 20, 30, 10, 20, 30]
for value in values:
bag.add(value)
print(bag)
#Output:
#BAG: 0 elements. []
#BAG: 6 elements. [10, 20, 30, 10, 20, 30]
#########
##Remove Example Test:
bag = Bag([1, 2, 3, 1, 2, 3, 1, 2, 3])
print(bag)
print(bag.remove(7), bag)
print(bag.remove(3), bag)
print(bag.remove(3), bag)
print(bag.remove(3), bag)
print(bag.remove(3), bag)
#Output:
#BAG: 9 elements. [1, 2, 3, 1, 2, 3, 1, 2, 3]
#False BAG: 9 elements. [1, 2, 3, 1, 2, 3, 1, 2, 3]
#True BAG: 8 elements. [1, 2, 1, 2, 3, 1, 2, 3]
#True BAG: 7 elements. [1, 2, 1, 2, 1, 2, 3]
#True BAG: 6 elements. [1, 2, 1, 2, 1, 2]
#False BAG: 6 elements. [1, 2, 1, 2, 1, 2]
#########
#Count Example Testing:
bag = Bag([1, 2, 3, 1, 2, 2])
print(bag, bag.count(1), bag.count(2), bag.count(3), bag.count(4))
#Output:
#BAG: 6 elements. [1, 2, 3, 1, 2, 2] 2 3 1 0
#########
#Clear Example Testing:
bag = Bag([1, 2, 3, 1, 2, 3])
print(bag)
bag.clear()
print(bag)
#Output:
#BAG: 6 elements. [1, 2, 3, 1, 2, 3]
#BAG: 0 elements. []
#########
#Size Example Testing:
bag = Bag([10, 20, 30, 40])
print(bag.size(), bag.remove(30), bag.size())
bag.clear()
print(bag.size())
#Output:
#4 True 3
#0
#########
#Equal Example Testing:
bag1 = Bag([1, 2, 3, 4, 5, 6])
bag2 = Bag([6, 5, 4, 3, 2, 1])
bag3 = Bag([1, 2, 3, 4, 5])
bag_empty = Bag()
print(bag1, bag2, bag3, bag_empty, sep="\n")
print(bag1.equal(bag2), bag2.equal(bag1))
print(bag1.equal(bag3), bag3.equal(bag1))
print(bag2.equal(bag3), bag3.equal(bag2))
print(bag1.equal(bag_empty), bag_empty.equal(bag1))
print(bag_empty.equal(bag_empty))
print(bag1, bag2, bag3, bag_empty, sep="\n")
#Output:
#BAG: 6 elements. [1, 2, 3, 4, 5, 6]
#BAG: 6 elements. [6, 5, 4, 3, 2, 1]
#BAG: 5 elements. [1, 2, 3, 4, 5]
#BAG: 0 elements. []
#True True
#False False
#False False
#False False
#True
#BAG: 6 elements. [1, 2, 3, 4, 5, 6]
#BAG: 6 elements. [6, 5, 4, 3, 2, 1]
#BAG: 5 elements. [1, 2, 3, 4, 5]
#BAG: 0 elements. []
Each method implementation has been explained below.
We can use the append method of the dynamic array to add elements(values) to our bag.
def add(self, value: object) -> None:
"""
uses append() method of dynamic
array
"""
self.da.append(value)
return
This method removes the first occurence of the value from the bag. To implement this we iterate through the dynamic array and check if the element at each index is same as the value to be removed. Once we get the index, we can remove the element by calling remove_at_index(i) method of the dynamic array and return True immediately.
def remove(self, value: object) -> bool:
"""
gets the index of the element and
removes it
using remove_at_index(i)
"""
for i in
range(self.da.length()):
if
self.da.get_at_index(i) == value:
self.da.remove_at_index(i)
return True
return False
Here, again we iterate through the dynamic array. We initialize a variable n which keeps track of the count. Each time, we find our value in the dynamic array we increment n. Finally we return n, once the entire dynamic array has been checked.
def count(self, value: object) -> int:
"""
returns the count of element in the
bag
"""
n = 0
for i in
range(self.da.length()):
if
self.da.get_at_index(i) == value:
n += 1
return n
We simply remove all the elements from the dynamic array by calling remove_at_index(i). Note, that we iterate the dynamic array from the end to start, so that indices of other elements do not change when we remove an element from the dynamic array.
def clear(self) -> None:
"""
clears all elements from the
bag
"""
for i in range(self.da.length()-1,
-1, -1):
self.da.remove_at_index(i)
This is simply the length of the dynamic array.
def size(self) -> int:
"""
returns the size of the bag
"""
return self.da.length()
Here, we first compare if the sizes of the two bag's are equal. If the sizes are equal, we check if the count of each element in our bag and second_bag are equal.
def equal(self, second_bag: object) ->
bool:
"""
compares two bags and returns True
if
they have the
same size and
they have the
same elements
"""
if self.da.length() !=
second_bag.da.length():
return False
for i in
range(self.da.length()):
element =
self.da.get_at_index(i)
if
(self.count(element) != second_bag.count(element)):
return False
return True
Screenshot of the code (included for readability)
Output