Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 10 additions & 50 deletions book/stacks/implementation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

<!-- language python -->
<!-- literate stacks/stack_right.py -->
<!-- /language -->

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:
<!-- language javascript -->
<!-- literate stacks/stack_right.js -->
<!-- /language -->

<!-- language python -->
<!-- literate stacks/stack_left.py -->
<!-- /language -->

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`.
<!-- language javascript -->
<!-- literate stacks/stack_left.js -->
<!-- /language -->
51 changes: 51 additions & 0 deletions book/stacks/stack_left.js
Original file line number Diff line number Diff line change
@@ -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`.
*/
26 changes: 26 additions & 0 deletions book/stacks/stack_left.py
Original file line number Diff line number Diff line change
@@ -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):
Expand All @@ -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`.
"""
57 changes: 57 additions & 0 deletions book/stacks/stack_right.js
Original file line number Diff line number Diff line change
@@ -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;
}
}
32 changes: 32 additions & 0 deletions book/stacks/stack_right.py
Original file line number Diff line number Diff line change
@@ -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):
Expand Down