This shows you the differences between two versions of the page.
Next revision | Previous revision | ||
haas:fall2022:data:projects:dls0 [2017/10/24 11:50] – created - external edit 127.0.0.1 | haas:fall2022:data:projects:dls0 [2022/11/12 15:24] (current) – [RUBRIC] wedge | ||
---|---|---|---|
Line 4: | Line 4: | ||
</ | </ | ||
- | ======Project: DLS0====== | + | ======PROJECT: Lists - Doubly-Linked Stacks (DLS0)====== |
- | =====Errata===== | + | =====OBJECTIVE===== |
- | This section | + | Onto Stacks! Another very commonly used data structure, we will be building it atop our list. |
- | * __revision #__: < | + | Don't forget to contribute to project documentation! That helps to ensure everyone is invested in the project. |
- | =====Objective===== | + | =====OVERVIEW===== |
- | In this project, we resume our conceptual journey and explore another data structure: stacks. | + | Our stack will in many ways be a restricted-access list: access with the stack functions will only manipulate |
- | =====Background===== | + | |
- | A **stack** is considered one of the most important data structures, along with **queues** (next week's project) and trees. And it is largely because of how often we find them playing out in nature or our day-to-day lives. | + | |
- | The word " | + | Stacks are a First In, First Out structure (or Last In, Last Out), and understanding the value that provides |
- | * (generically): | + | =====UPGRADING===== |
- | * (computing): | + | To assist with consistency across all implementations, |
- | Additionally, when viewing it as a verb (an action), we also find some positive computing application (bolded) in a less reputable cardplaying usage: | + | Simply go into the project base directory, and run: |
- | * shuffle or **arrange** (a deck of cards) dishonestly **so as to gain** an unfair **advantage** | + | |
- | Or, to distill it out: | + | < |
+ | lab46:~/ | ||
+ | </ | ||
- | * arrange so as to gain advantage | + | =====EDIT===== |
+ | You will want to go [[/ | ||
- | Combining with our previous definitions, | + | * [[/ |
- | * a set of storage locations that are arranged in such a way so as to give us an advantage- the most recently stored item (the last to be placed onto the stack) is the first to be retrieved. | + | {{page> |
- | ====Lists and Nodes==== | + | =====SUBMISSION===== |
- | So, how does all this list and node stuff play into our stack implementation? | + | To be successful in this project, the following criteria (or their equivalent) must be met: |
- | Well, we're going to build the stack ON TOP OF lists (which are composed | + | * Project must be submit on time, by the deadline. |
+ | * Late submissions will lose 33% credit per day, with the submission window closing on the 3rd day following the deadline. | ||
+ | * All code must compile cleanly (no warnings or errors) | ||
+ | * Compile with the **-Wall** and **--std=gnu18** compiler flags | ||
+ | * all requested functionality | ||
+ | * Executed programs must display in a manner similar to provided output | ||
+ | * output | ||
+ | * Processing must be correct based on input given and output requested | ||
+ | * Output, if applicable, must be correct based on values input | ||
+ | * Code must be nicely and consistently indented | ||
+ | * Code must be consistently written, to strive for readability from having a consistent style throughout | ||
+ | * Code must be commented | ||
+ | * Any "to be implemented" | ||
+ | * these " | ||
+ | * Sufficient | ||
+ | * No global variables | ||
+ | * Track/ | ||
+ | * Submit | ||
- | Therefore, a stack is a data structure that stores its data in a list (which consists of nodes), and we apply various rules/ | + | ====Submit Tool Usage==== |
+ | Let' | ||
+ | submit, you would do the following: | ||
- | The concept of restricting access is a very important one- which we did with our list as well (limiting our access to the list through the use of **append()**, | + | < |
+ | lab46:~/ | ||
+ | </ | ||
- | * __error reduction__: | + | You should get some sort of confirmation indicating successful submission |
- | * __performance__: | + | if all went according |
+ | mismatches. | ||
- | ====conceptualizing a stack==== | + | =====RUBRIC===== |
- | It is common to think of a stack as a vertical object, much like a pile of papers that need to be processed (or a pile of anything we need to work with). | + | I' |
- | Although we've commonly viewed lists horizontally (from left to right), there is absolutely nothing requiring this positional orientation. | + | < |
- | + | 91:dls0:final tally of results | |
- | Similarly, stacks possess no mandatory orientation, | + | *:dls0:obtained project by the Sunday prior to duedate [6/6] |
- | + | *:dls0:clean compile, no compiler messages [13/13] | |
- | ====the stack==== | + | *:dls0:implementation passes unit tests [13/13] |
- | The stack data structure presents certain advantages that encourages its use in solving problems (why do we stack a bunch of papers all in the same place to create piles? Why is that more advantageous than giving each one its own unique desk space?), and we accomplish that by its compositional definition: | + | *:dls0:adequate modifications |
- | + | *: | |
- | * a stack has a **top**, basically a node pointer that constantly points to the top node in the stack (equivalent to the underlying list's last pointer). | + | *:dls0:code tracked in lab46 semester repo [7/7] |
- | * to put an item on the stack, we **push** it there. So one of the functions we'll be implementing is **push()**, which will take the node we wish to place on the given stack, and push will handle all the necessary coordination with its underlying list (i.e. it should call existing list functions to manipulate the list) | + | |
- | * to get an item off of the stack, we **pop** it. In our **pop()** function, we grab the **top** node off the stack (this also translates into a set of list-level transactions that our **pop()** function will handle for us). | + | |
- | + | ||
- | These qualities cause the stack to be described as a LIFO (or FILO) structure: | + | |
- | * **LIFO**: **L**ast **I**n **F**irst **O**ut | + | |
- | * **FILO**: **F**irst **I**n **L**ast **O**ut | + | |
- | + | ||
- | And that describes what is conceptually going on-- if we can ONLY access our data through one location (the top), the data most immediately available | + | |
- | + | ||
- | This concept is very important, and being aware of it can be of significant strategic importance when going about solving problems (and seeing its pattern proliferate in nature). | + | |
- | + | ||
- | With that said, the existence of **top**, along with the core **push()** and **pop()** functions defines the minimal necessary requiments to interface with a stack. Sometimes we'll see additional actions sneak in. While these may be commonly associated with stacks, they should not be confused as core requiments of a stack: | + | |
- | + | ||
- | * **peek**: the ability to gain access to the top node without removing it from the stack | + | |
- | * **is the stack empty?**: the ability to query the stack and determine if it is empty or non-empty (or perhaps if non-empty, how full is it?) | + | |
- | + | ||
- | While we may be implementing these supplemental functions, it should be noted that not only are they in no way necessary for using a stack, they could be detrimental (just as relying on counting can be a crutch). | + | |
- | + | ||
- | Their inclusion should ONLY be viewed as a means of convenience (in certain scenarios they may result in less code needing to be written), but NOT as something you should routinely make use of. | + | |
- | ====size can matter==== | + | |
- | + | ||
- | With a stack, there sometimes exists a need to cap its total size (especially in applications on the computer, we may have only allocated a fixed amount of space and cannot exceed it). For this reason, we will need to maintain a count of nodes in the stack (ie the underlying list). | + | |
- | + | ||
- | This is why **dll2** exists: to introduce **qty** back into the list struct. | + | |
- | + | ||
- | Additionally, | + | |
- | + | ||
- | It should also be pointed out that in other applications, | + | |
- | + | ||
- | ====stack error conditions==== | + | |
- | There are two very important operational error conditions a stack can experience: | + | |
- | + | ||
- | | + | |
- | * __stack **under**flow__: this is the situation where the stack is empty, yet we still try to pop a value from it. | + | |
- | + | ||
- | =====Project Overview===== | + | |
- | + | ||
- | For this project, we're going to be implementing the stack data structure atop of our recently re-implemented linked list (the doubly linked list). | + | |
- | + | ||
- | Should you be having any lingering issues with your doubly-linked list implementation, | + | |
- | ====inc/stack.h==== | + | |
- | To implement a stack, we'll be creating a new type of struct. Continuing our previous pattern, we'll isolate that specific information in its own header file: | + | |
- | + | ||
- | <code c> | + | |
- | #ifndef _STACK_H | + | |
- | #define _STACK_H | + | |
- | + | ||
- | ////////////////////////////////////////////////////////////////////// | + | |
- | // | + | |
- | // Stack relies on list (which relies on node) to work. | + | |
- | // See the layers? | + | |
- | // | + | |
- | #include " | + | |
- | + | ||
- | ////////////////////////////////////////////////////////////////////// | + | |
- | // | + | |
- | // Define the stack struct | + | |
- | // | + | |
- | struct stack { | + | |
- | Node *top; // pointer to top of stack | + | |
- | List *data; | + | |
- | ulli | + | |
- | }; | + | |
- | + | ||
- | code_t | + | |
- | code_t | + | |
- | code_t | + | |
- | + | ||
- | code_t | + | |
- | code_t | + | |
- | code_t | + | |
- | + | ||
- | code_t | + | |
- | + | ||
- | #endif | + | |
</ | </ | ||
- | As indicated, with stacks, suddenly a lot of the underlying details start to be abstracted away. And the total number | + | ===Pertaining |
- | For our stack implementation, | + | * each class member is to participate in the contribution of relevant information and formatting |
+ | * minimal member contributions consist of: | ||
+ | * near the class average edits (a value of at least four productive edits) | ||
+ | * near the average class content change average (a value of at least 256 bytes (absolute value of data content change)) | ||
+ | * near the class content contribution average (a value of at least 1kiB) | ||
+ | * no adding | ||
+ | * adding and formatting data in an organized fashion, aiming | ||
+ | * content contributions will be factored into a documentation coefficient, | ||
+ | * no contributions, | ||
+ | * less than minimum contributions is 0.75 | ||
+ | * met minimum contribution threshold is 1.00 | ||
- | This is necessary so that we can free up the return value of **push()** and **pop()** to be used for status (ie look out for stack overflows and underflows). | + | ===Additionally=== |
- | **peek()** and **isempty()** are being implemented as an exercise to aid in your understanding of stacks. Again, avoid their use except is a means of convenience (or to further optimize your code). The general rule of thumb is that the use of **peek()** and **isempty()** should result in shortening your code in a clear or clever way. | + | |
- | + | * Solutions | |
- | If you cannot think of how to solve a problem without the use of **peek()**/ | + | * Solutions not utilizing indentation |
- | + | * Solutions not organized | |
- | Also, while nothing is stopping you from doing so, the idea here is that things like **size** and the underlying list **qty** in stack transactions will **NOT** be accessed outside of the **push()** and **pop()** functions. Just like my warnings about using **qty** in your list solutions-- do not consider **size** as a variable for your general use (**push()** will probably be the only place it is used). | + | |
- | + | ||
- | In object-oriented programming, | + | |
- | ====inc/ | + | |
- | With stacks, the following new information has been added to **data.h**: | + | |
- | + | ||
- | <code c> | + | |
- | ////////////////////////////////////////////////////////////////////// | + | |
- | // | + | |
- | // Status codes for the doubly linked stack implementation | + | |
- | // | + | |
- | # | + | |
- | # | + | |
- | # | + | |
- | # | + | |
- | # | + | |
- | # | + | |
- | # | + | |
- | # | + | |
- | # | + | |
- | </ | + | |
- | + | ||
- | __**Technical note**__: Due to space constraints (there are 9 stack status codes), you'll notice **DLS_DEFAULT_FAIL** is not a unique number, but a combination | + | |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | ====stack library==== | + | |
- | In **src/ | + | |
- | + | ||
- | Figure out what is going on, the connections, | + | |
- | + | ||
- | Again, your stack is to utilize the stack for its underlying data storage operations. This is what the stack' | + | |
- | + | ||
- | ====stack operation status codes==== | + | |
- | You'll notice the presence of a set of stack-related # | + | |
- | + | ||
- | They are not exclusive- in some cases, multiple states can be applied. The intent is that you will OR together all pertinent states and return that from the function. | + | |
- | + | ||
- | * **DLS_SUCCESS** - everything went according | + | |
- | * **DLS_CREATE_FAIL** - memory allocation failed (considered in error) | + | |
- | * **DLS_NULL** - result is NULL (probably in error) | + | |
- | * **DLS_EMPTY** - result is an empty list/stack (may or may not be in error) | + | |
- | * **DLS_OVERFLOW** - operation exceeds allocated size of list (may be considered an error) | + | |
- | * **DLS_UNDERFLOW** - operation cannot proceed due to lack of data (may be considered an error) | + | |
- | * **DLS_DEFAULT_FAIL** - default state of unimplemented functions (default error) | + | |
- | * **DLS_ERROR** - some error occurred | + | |
- | * **DLS_INVALID** - invalid state (pointer to stack does not exist) | + | |
- | + | ||
- | For example, in the case of " | + | |
- | * DLS_ERROR (a problem has occurred) | + | |
- | * DLS_CREATE_FAIL (a problem has occurred when using malloc()) | + | |
- | * DLS_NULL (no memory allocated, so stack cannot be anything but NULL) | + | |
- | + | ||
- | ALL THREE states must be returned from the function in question should such an occurrence take place (in addition, various underlying list and node status codes may be present as well-- see the unit tests for more information). | + | |
- | ====Stack library unit tests==== | + | |
- | In **testing/ | + | |
- | + | ||
- | * **unit-mkstack.c** - unit test for **mkstack()** library function | + | |
- | * **unit-cpstack.c** - unit test for **cpstack()** library function | + | |
- | * **unit-rmstack.c** - unit test for **rmstack()** library function | + | |
- | * **unit-push.c** - unit test for **push()** library function | + | |
- | * **unit-pop.c** - unit test for **pop()** library function | + | |
- | * **unit-peek.c** - unit test for **peek()** library function | + | |
- | * **unit-isempty.c** - unit test for **isempty()** library function | + | |
- | + | ||
- | There are also corresponding **verify-FUNCTION.sh** scripts that will output | + | |
- | + | ||
- | These are complete runnable programs (when compiled, and linked against the stack library, which is all handled for you by the **Makefile** system in place). | + | |
- | + | ||
- | Of particular importance, I want you to take a close look at: | + | |
- | + | ||
- | * the source code to each of these unit tests | + | |
- | * the purpose of these programs is to validate the correct functionality of the respective library functions | + | |
- | * follow the logic | + | |
- | * make sure you understand what is going on | + | |
- | * ask questions to get clarification! | + | |
- | * the output from these programs once compiled | + | |
- | * analyze the output | + | |
- | * make sure you understand what is going on | + | |
- | * ask questions to get clarification! | + | |
- | + | ||
- | ====stack testing applications==== | + | |
- | + | ||
- | ===palindrome-stack=== | + | |
- | Now that we've completed our stack functionality, | + | |
- | + | ||
- | Our task (once again) | + | |
- | + | ||
- | This implementation will be considered an extra credit opportunity, | + | |
- | + | ||
- | It is also highly recommended to undertake as it will give you further experience working with these concepts. | + | |
- | + | ||
- | Note this is a DIFFERENT approach than you would have taken in the program with sll2 and dll1- you' | + | |
- | + | ||
- | =====Expected Results===== | + | |
- | To assist you in verifying | + | |
- | + | ||
- | ====stack library==== | + | |
- | Here is what you should get for stack: | + | |
- | + | ||
- | < | + | |
- | lab46: | + | |
- | ====================================================== | + | |
- | = Verifying Doubly-Linked Stack Functionality | + | |
- | ====================================================== | + | |
- | | + | |
- | [push] Total: | + | |
- | [pop] Total: | + | |
- | | + | |
- | [peek] Total: | + | |
- | | + | |
- | | + | |
- | ====================================================== | + | |
- | | + | |
- | ====================================================== | + | |
- | lab46: | + | |
- | </ | + | |
- | =====Submission===== | + | |
- | {{page> | + | |