diff --git a/book/stacks/implementation.md b/book/stacks/implementation.md index 7c313b9a..40aac8cc 100644 --- a/book/stacks/implementation.md +++ b/book/stacks/implementation.md @@ -5,58 +5,18 @@ collection: stacks position: 3 --- -Now that we have clearly defined the stack as an abstract data type we -will turn our attention to using Python to implement the stack. Recall -that when we give an abstract data type a physical implementation we -refer to the implementation as a data structure. - -You may be wondering if a Python `list` “is” a stack. It is more precise -to say that a Python `list` “may be used as a” stack. That is to say, -the implementation of `list` in Python provides methods that allow us to -achieve the behavior of the stack abstract data type, for instance -`.append()` allows us to push items to our stack. - -In practice “use a Python list as a stack” is precisely what you are -likely to do, in other words you will define something like: -`pancake_stack = []` then diligently use only the `append` and `pop` -methods as well as `len(pancake_stack)` for the size, and -`pancake_stack[-1]` to peek. Of course, a Python `list` permits much -more than the behavior of a stack, notably accessing an item by index, -and inserting and deleting items at any point by index. - -As such, we ought to communicate as clearly as possible our intention to -use this (concrete) data structure of a `list` as an abstract data type -stack. Sometimes we can achieve this simply by naming it a stack. Other -times, we may want to use a class to abstract away the implementation of -the stack as a list, and only provide the behaviors that we require of a -stack. - -Such an abstraction is also illustrative of the distinction between -concrete data structures and abstract data types, so we provide a -possible implementation of a stack class here: - + + -It is important to note that we could have chosen to implement the stack -using a list where the top is at the beginning instead of at the end. In -this case, instead of using `pop` and `append` as above, instead we -would pop from and insert into position 0 in the. Here is a possible -implementation of that approach: + + + + + -This ability to change the physical implementation of an abstract data -type while maintaining the logical characteristics is an example of -abstraction at work. However, even though the stack will work either -way, if we consider the performance of the two implementations, there is -definitely a difference. Recall that the `append` and `pop()` operations -were both $$O(1)$$. This means that the first implementation will -perform push and pop in constant time no matter how many items are on -the stack. The performance of the second implementation suffers in that -the `insert(0)` and `pop(0)` operations will both require $$O(n)$$ for a -stack of size n. Clearly, even though the implementations are logically -equivalent, they would have very different timings when performing -benchmark testing. - -Going forward, we will simply use Python `list`s directly as stacks, -being careful to only use the stack-like behavior of the `list`. + + + diff --git a/book/stacks/stack_left.js b/book/stacks/stack_left.js new file mode 100644 index 00000000..2911224c --- /dev/null +++ b/book/stacks/stack_left.js @@ -0,0 +1,51 @@ +/* +It is important to note that we could have chosen to implement the stack +using an array where the top is at the beginning instead of at the end. In +this case, instead of using `pop` and `push` as above, instead we +would pop from and insert into position 0. Here is a possible +implementation of that approach: +*/ + +class Stack { + constructor() { + this._items = []; + } + + is_empty() { + return this._items.length == 0; + } + + push(item) { + this._items.splice(0, 0, item); + } + + pop() { + return this._items.splice(0, 1)[0]; + } + + peek() { + return this._items[0]; + } + + size() { + return this._items.length; + } +} + +/* +This ability to change the physical implementation of an abstract data +type while maintaining the logical characteristics is an example of +abstraction at work. However, even though the stack will work either +way, if we consider the performance of the two implementations, there is +definitely a difference. Recall that the `push` and `pop()` operations +were both $$O(1)$$. This means that the first implementation will +perform push and pop in constant time no matter how many items are on +the stack. The performance of the second implementation suffers in that +all the `splice()` operations will require $$O(n)$$ for a +stack of size n. Clearly, even though the implementations are logically +equivalent, they would have very different timings when performing +benchmark testing. + +Going forward, we will simply use JavaScript's `array`s directly as stacks, +being careful to only use the stack-like behavior of the `array`. +*/ diff --git a/book/stacks/stack_left.py b/book/stacks/stack_left.py index 87371a8c..d7fab707 100644 --- a/book/stacks/stack_left.py +++ b/book/stacks/stack_left.py @@ -1,3 +1,11 @@ +""" +It is important to note that we could have chosen to implement the stack +using a list where the top is at the beginning instead of at the end. In +this case, instead of using `pop` and `append` as above, instead we +would pop from and insert into position 0. Here is a possible +implementation of that approach: +""" + class Stack: def __init__(self): @@ -17,3 +25,21 @@ def peek(self): def size(self): return len(self._items) + +""" +This ability to change the physical implementation of an abstract data +type while maintaining the logical characteristics is an example of +abstraction at work. However, even though the stack will work either +way, if we consider the performance of the two implementations, there is +definitely a difference. Recall that the `append` and `pop()` operations +were both $$O(1)$$. This means that the first implementation will +perform push and pop in constant time no matter how many items are on +the stack. The performance of the second implementation suffers in that +the `insert(0)` and `pop(0)` operations will both require $$O(n)$$ for a +stack of size n. Clearly, even though the implementations are logically +equivalent, they would have very different timings when performing +benchmark testing. + +Going forward, we will simply use Python `list`s directly as stacks, +being careful to only use the stack-like behavior of the `list`. +""" diff --git a/book/stacks/stack_right.js b/book/stacks/stack_right.js new file mode 100644 index 00000000..5210fad8 --- /dev/null +++ b/book/stacks/stack_right.js @@ -0,0 +1,57 @@ +/* +Now that we have clearly defined the stack as an abstract data type we +will turn our attention to using JavaScript to implement the stack. Recall +that when we give an abstract data type a physical implementation we +refer to the implementation as a data structure. + +You may be wondering if a JavaScript `array` “is” a stack. It is more precise +to say that a JavaScript `array` “may be used as a” stack. That is to say, +the implementation of `array` in JavaScript provides methods that allow us to +achieve the behavior of the stack abstract data type, for instance +`.push()` allows us to push items to our stack. + +In practice “use a JavaScript list as a stack” is precisely what you are +likely to do, in other words you will define something like: +`let pancake_stack = []` then diligently use only the `push` and `pop` +methods as well as `pancake_stack.length` for the size, and +`pancake_stack[pancake_stack.length - 1]` to peek. Of course, a JavaScript +`array` permits much more than the behavior of a stack, notably accessing an +item by index, and inserting and deleting items at any point by index. + +As such, we ought to communicate as clearly as possible our intention to +use this (concrete) data structure of a `array` as an abstract data type +stack. Sometimes we can achieve this simply by naming it a stack. Other +times, we may want to use a class to abstract away the implementation of +the stack as an array, and only provide the behaviors that we require of a +stack. + +Such an abstraction is also illustrative of the distinction between +concrete data structures and abstract data types, so we provide a +possible implementation of a stack class here: +*/ + +class Stack { + constructor() { + this._items = []; + } + + is_empty() { + return this._items.length == 0; + } + + push(item) { + this._items.push(item); + } + + pop() { + return this._items.pop(item); + } + + peek() { + return this._items[self._items.length - 1]; + } + + size() { + return this._items.length; + } +} diff --git a/book/stacks/stack_right.py b/book/stacks/stack_right.py index 69730285..84722a5c 100644 --- a/book/stacks/stack_right.py +++ b/book/stacks/stack_right.py @@ -1,3 +1,35 @@ +""" +Now that we have clearly defined the stack as an abstract data type we +will turn our attention to using Python to implement the stack. Recall +that when we give an abstract data type a physical implementation we +refer to the implementation as a data structure. + +You may be wondering if a Python `list` “is” a stack. It is more precise +to say that a Python `list` “may be used as a” stack. That is to say, +the implementation of `list` in Python provides methods that allow us to +achieve the behavior of the stack abstract data type, for instance +`.append()` allows us to push items to our stack. + +In practice “use a Python list as a stack” is precisely what you are +likely to do, in other words you will define something like: +`pancake_stack = []` then diligently use only the `append` and `pop` +methods as well as `len(pancake_stack)` for the size, and +`pancake_stack[-1]` to peek. Of course, a Python `list` permits much +more than the behavior of a stack, notably accessing an item by index, +and inserting and deleting items at any point by index. + +As such, we ought to communicate as clearly as possible our intention to +use this (concrete) data structure of a `list` as an abstract data type +stack. Sometimes we can achieve this simply by naming it a stack. Other +times, we may want to use a class to abstract away the implementation of +the stack as a list, and only provide the behaviors that we require of a +stack. + +Such an abstraction is also illustrative of the distinction between +concrete data structures and abstract data types, so we provide a +possible implementation of a stack class here: +""" + class Stack(object): def __init__(self):