Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

Assume the following abstract data types: Stack and Bag You are required to: a)

ID: 3870841 • Letter: A

Question

Assume the following abstract data types: Stack and Bag You are required to: a) Implement these abstract data types as two separate classes without inheritance between them b) Create a client class, MyClientTest1, and provide methods to test each one of the methods implemented by Stack and Bag c) Now, re-factor your program to use inheritance. Decide what class, e.g., Stack or Bag, should be the super-class. Provide a rationale of your design decision d) Create a client class, MyClientTest2, and provide methods to test each one of the methods implemented by Stack and Bag the Stack class State: a sequence of Objects s isEmpty() : boolean Precondition: true Postcondition: returns true if and only if s is empty take() : Object Precondition: ! isEmpty() Postcondition: result is the first item of s. The final value of s is the initial value with the first item removed. put( ob : Object ) Precondition: true Postcondition: The final value of s is its initial value with ob prepended. the Bag class State: a bag (i.e. multiset) of Objects b isEmpty() : boolean Precondition: true Postcondition: Returns true if and only if b is empty. take() : Object Precondition: ! isEmpty() Postcondition: result is an arbitrary member of b. The final value of b is the initial value with the result removed. put( ob : Object ) Precondition: true Postcondition: The final value of b is its initial value with ob added.

I need this code to be written in either c++ or java

Thank You

Explanation / Answer

An abstract data type is defined as a mathematical model of the data objects that make up a data type as well as the functions that operate on these objects.
There are no standard conventions for defining them. A broad division may be drawn between "imperative" and "functional" definition styles.
Imperative-style definition

In the philosophy of imperative programming languages, an abstract data structure is conceived as an entity that is mutable—meaning that it may be in different states at different times. Some operations may change the state of the ADT; therefore, the order in which operations are evaluated is important, and the same operation on the same entities may have different effects if executed at different times—just like the instructions of a computer, or the commands and procedures of an imperative language.
To underscore this view, it is customary to say that the operations are executed or applied, rather than evaluated. The imperative style is often used when describing abstract algorithms.
Abstract variable

Imperative-style definitions of ADT often depend on the concept of an abstract variable, which may be regarded as the simplest non-trivial ADT. An abstract variable V is a mutable entity that admits two operations:

    store(V, x) where x is a value of unspecified nature;
    fetch(V), that yields a value,

with the constraint that

    fetch(V) always returns the value x used in the most recent store(V, x) operation on the same variable V.

As in so many programming languages, the operation store(V, x) is often written V x (or some similar notation), and fetch(V) is implied whenever a variable V is used in a context where a value is required. Thus, for example, V V + 1 is commonly understood to be a shorthand for store(V,fetch(V) + 1).

In this definition, it is implicitly assumed that storing a value into a variable U has no effect on the state of a distinct variable V. To make this assumption explicit, one could add the constraint that

    if U and V are distinct variables, the sequence { store(U, x); store(V, y) } is equivalent to { store(V, y); store(U, x) }.

More generally, ADT definitions often assume that any operation that changes the state of one ADT instance has no effect on the state of any other instance (including other instances of the same ADT) — unless the ADT axioms imply that the two instances are connected (aliased) in that sense. For example, when extending the definition of abstract variable to include abstract records, the operation that selects a field from a record variable R must yield a variable V that is aliased to that part of R.

The definition of an abstract variable V may also restrict the stored values x to members of a specific set X, called the range or type of V. As in programming languages, such restrictions may simplify the description and analysis of algorithms, and improve their readability.

Note that this definition does not imply anything about the result of evaluating fetch(V) when V is un-initialized, that is, before performing any store operation on V. An algorithm that does so is usually considered invalid, because its effect is not defined. (However, there are some important algorithms whose efficiency strongly depends on the assumption that such a fetch is legal, and returns some arbitrary value in the variable's range.[citation needed])
Instance creation

Some algorithms need to create new instances of some ADT (such as new variables, or new stacks). To describe such algorithms, one usually includes in the ADT definition a create() operation that yields an instance of the ADT, usually with axioms equivalent to

    the result of create() is distinct from any instance in use by the algorithm.

This axiom may be strengthened to exclude also partial aliasing with other instances. On the other hand, this axiom still allows implementations of create() to yield a previously created instance that has become inaccessible to the program.
Example: abstract stack (imperative)

As another example, an imperative-style definition of an abstract stack could specify that the state of a stack S can be modified only by the operations

    push(S, x), where x is some value of unspecified nature;
    pop(S), that yields a value as a result,

with the constraint that

    For any value x and any abstract variable V, the sequence of operations { push(S, x); V pop(S) } is equivalent to V x.

Since the assignment V x, by definition, cannot change the state of S, this condition implies that V pop(S) restores S to the state it had before the push(S, x). From this condition and from the properties of abstract variables, it follows, for example, that the sequence

    { push(S, x); push(S, y); U pop(S); push(S, z); V pop(S); W pop(S) }

where x, y, and z are any values, and U, V, W are pairwise distinct variables, is equivalent to

    { U y; V z; W x }

Here it is implicitly assumed that operations on a stack instance do not modify the state of any other ADT instance, including other stacks; that is,

    For any values x, y, and any distinct stacks S and T, the sequence { push(S, x); push(T, y) } is equivalent to { push(T, y); push(S, x) }.

An abstract stack definition usually includes also a Boolean-valued function empty(S) and a create() operation that returns a stack instance, with axioms equivalent to

    create() S for any stack S (a newly created stack is distinct from all previous stacks);
    empty(create()) (a newly created stack is empty);
    not empty(push(S, x)) (pushing something into a stack makes it non-empty).

Single-instance style

Sometimes an ADT is defined as if only one instance of it existed during the execution of the algorithm, and all operations were applied to that instance, which is not explicitly notated. For example, the abstract stack above could have been defined with operations push(x) and pop(), that operate on the only existing stack. ADT definitions in this style can be easily rewritten to admit multiple coexisting instances of the ADT, by adding an explicit instance parameter (like S in the previous example) to every operation that uses or modifies the implicit instance.

On the other hand, some ADTs cannot be meaningfully defined without assuming multiple instances. This is the case when a single operation takes two distinct instances of the ADT as parameters. For an example, consider augmenting the definition of the abstract stack with an operation compare(S, T) that checks whether the stacks S and T contain the same items in the same order.
Functional-style definition

Another way to define an ADT, closer to the spirit of functional programming, is to consider each state of the structure as a separate entity. In this view, any operation that modifies the ADT is modeled as a mathematical function that takes the old state as an argument, and returns the new state as part of the result. Unlike the imperative operations, these functions have no side effects. Therefore, the order in which they are evaluated is immaterial, and the same operation applied to the same arguments (including the same input states) will always return the same results (and output states).

In the functional view, in particular, there is no way (or need) to define an "abstract variable" with the semantics of imperative variables (namely, with fetch and store operations). Instead of storing values into variables, one passes them as arguments to functions.
Example: abstract stack (functional)

For example, a complete functional-style definition of an abstract stack could use the three operations:

    push: takes a stack state and an arbitrary value, returns a stack state;
    top: takes a stack state, returns a value;
    pop: takes a stack state, returns a stack state.

In a functional-style definition there is no need for a create operation. Indeed, there is no notion of "stack instance". The stack states can be thought of as being potential states of a single stack structure, and two stack states that contain the same values in the same order are considered to be identical states. This view actually mirrors the behavior of some concrete implementations, such as linked lists with hash cons.

Instead of create(), a functional-style definition of an abstract stack may assume the existence of a special stack state, the empty stack, designated by a special symbol like or "()"; or define a bottom() operation that takes no arguments and returns this special stack state. Note that the axioms imply that

    push(, x) .

In a functional-style definition of a stack one does not need an empty predicate: instead, one can test whether a stack is empty by testing whether it is equal to .

Note that these axioms do not define the effect of top(s) or pop(s), unless s is a stack state returned by a push. Since push leaves the stack non-empty, those two operations are undefined (hence invalid) when s = . On the other hand, the axioms (and the lack of side effects) imply that push(s, x) = push(t, y) if and only if x = y and s = t.

As in some other branches of mathematics, it is customary to assume also that the stack states are only those whose existence can be proved from the axioms in a finite number of steps. In the abstract stack example above, this rule means that every stack is a finite sequence of values, that becomes the empty stack () after a finite number of pops. By themselves, the axioms above do not exclude the existence of infinite stacks (that can be poped forever, each time yielding a different state) or circular stacks (that return to the same state after a finite number of pops). In particular, they do not exclude states s such that pop(s) = s or push(s, x) = s for some x. However, since one cannot obtain such stack states with the given operations, they are assumed "not to exist".
Whether to include complexity

Aside from the behavior in terms of axioms, it is also possible to include, in the definition of an ADT operation, their algorithmic complexity. Alexander Stepanov, designer of the C++ Standard Template Library, included complexity guarantees in the STL specification, arguing:

    The reason for introducing the notion of abstract data types was to allow interchangeable software modules. You cannot have interchangeable modules unless these modules share similar complexity behavior. If I replace one module with another module with the same functional behavior but with different complexity tradeoffs, the user of this code will be unpleasantly surprised. I could tell him anything I like about data abstraction, and he still would not want to use the code. Complexity assertions have to be part of the interface.
    — Alexander Stepanov[6]

Advantages of abstract data typing
   This section needs additional citations for verification. Please help improve this article by adding citations to reliable sources. Unsourced material may be challenged and removed. (May 2011) (Learn how and when to remove this template message)
Encapsulation
Abstraction provides a promise that any implementation of the ADT has certain properties and abilities; knowing these is all that is required to make use of an ADT obj

Code that uses an ADT object will not need to be edited if the implementation of the ADT is changed. Since any changes to the implementation must still comply with the interface, and since code using an ADT object may only refer to properties and abilities specified in the interface, changes may be made to the implementation without requiring any changes in code where the ADT is used.
Flexibility

Different implementations of the ADT, having all the same properties and abilities, are equivalent and may be used somewhat interchangeably in code that uses the ADT. This gives a great deal of flexibility when using ADT objects in different situations. For example, different implementations of the ADT may be more efficient in different situations; it is possible to use each in the situation where they are preferable, thus increasing overall efficiency.
Typical operations

Some operations that are often specified for ADTs (possibly under other names) are

    compare(s, t), that tests whether two instances' states are equivalent in some sense;
    hash(s), that computes some standard hash function from the instance's state;
    print(s) or show(s), that produces a human-readable representation of the instance's state.

In imperative-style ADT definitions, one often finds also

    create(), that yields a new instance of the ADT;
    initialize(s), that prepares a newly created instance s for further operations, or resets it to some "initial state";
    copy(s, t), that puts instance s in a state equivalent to that of t;
    clone(t), that performs s create(), copy(s, t), and returns s;

    free(s) or destroy(s), that reclaims the memory and other resources used by s.

The free operation is not normally relevant or meaningful, since ADTs are theoretical entities that do not "use memory". However, it may be necessary when one needs to analyze the storage used by an algorithm that uses the ADT. In that case one needs additional axioms that specify how much memory each ADT instance uses, as a function of its state, and how much of it is returned to the pool by free.

Hire Me For All Your Tutoring Needs
Integrity-first tutoring: clear explanations, guidance, and feedback.
Drop an Email at
drjack9650@gmail.com
Chat Now And Get Quote