dlinked 0.1.8 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CODE_OF_CONDUCT.md +74 -0
- data/CONTRIBUTING.md +88 -0
- data/README.md +88 -0
- data/benchmark.rb +60 -36
- data/dlinked.gemspec +1 -0
- data/lib/d_linked/cache_list.rb +122 -93
- data/lib/d_linked/list.rb +167 -146
- data/lib/d_linked/version.rb +1 -1
- data/lru_cache_example.rb +152 -0
- metadata +19 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8215705b10ca404e892cc7b1d6571d96ee66aa8521941f42d6e7051582d2372a
|
|
4
|
+
data.tar.gz: bff1d797d11300cdae48d6f7a3623e17bb7e996db7457b270884e5e9312192b6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 69e363bbc4d74898aa65673c0f46250633f9448d7c406ecfe089860a2005142905a170f0da5724d3a1766fe4ff70ddbbd2a77653be86b9fab08efd8b23a538a9
|
|
7
|
+
data.tar.gz: 129ab2caf81b5d8fb6d24c67ccad979fff1ce928811341f28104f5b0f851f1d05106783935d2e17bddd1bc9edf953f539332d4cc01131286495bd529a90dc65d
|
data/CODE_OF_CONDUCT.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
|
8
|
+
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
|
9
|
+
level of experience, education, socio-economic status, nationality, personal
|
|
10
|
+
appearance, race, religion, or sexual identity and orientation.
|
|
11
|
+
|
|
12
|
+
## Our Standards
|
|
13
|
+
|
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
|
15
|
+
include:
|
|
16
|
+
|
|
17
|
+
* Using welcoming and inclusive language
|
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
|
19
|
+
* Gracefully accepting constructive criticism
|
|
20
|
+
* Focusing on what is best for the community
|
|
21
|
+
* Showing empathy towards other community members
|
|
22
|
+
|
|
23
|
+
Examples of unacceptable behavior by participants include:
|
|
24
|
+
|
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
|
26
|
+
advances
|
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
|
28
|
+
* Public or private harassment
|
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
|
30
|
+
address, without explicit permission
|
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
|
32
|
+
professional setting
|
|
33
|
+
|
|
34
|
+
## Our Responsibilities
|
|
35
|
+
|
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
|
38
|
+
response to any instances of unacceptable behavior.
|
|
39
|
+
|
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
|
44
|
+
threatening, offensive, or harmful.
|
|
45
|
+
|
|
46
|
+
## Scope
|
|
47
|
+
|
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
|
49
|
+
when an individual is representing the project or its community. Examples of
|
|
50
|
+
representing a project or community include using an official project e-mail
|
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
|
53
|
+
further defined and clarified by project maintainers.
|
|
54
|
+
|
|
55
|
+
## Enforcement
|
|
56
|
+
|
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
58
|
+
reported by contacting the project team. All
|
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
|
63
|
+
|
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
|
66
|
+
members of the project's leadership.
|
|
67
|
+
|
|
68
|
+
## Attribution
|
|
69
|
+
|
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0,
|
|
71
|
+
available at [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0].
|
|
72
|
+
|
|
73
|
+
[homepage]: https://www.contributor-covenant.org
|
|
74
|
+
[v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html
|
data/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Contributing to DLinked
|
|
2
|
+
|
|
3
|
+
First off, thank you for considering contributing to DLinked! It's people like you that make open source such a great community.
|
|
4
|
+
|
|
5
|
+
We welcome any type of contribution, not just code. You can help with:
|
|
6
|
+
* **Reporting a bug**
|
|
7
|
+
* **Discussing the current state of the code**
|
|
8
|
+
* **Submitting a fix**
|
|
9
|
+
* **Proposing new features**
|
|
10
|
+
* **Becoming a maintainer**
|
|
11
|
+
|
|
12
|
+
## Getting Started
|
|
13
|
+
|
|
14
|
+
### Development Environment
|
|
15
|
+
|
|
16
|
+
To get started with the development environment, you'll need:
|
|
17
|
+
* Ruby (see `.ruby-version` for the required version)
|
|
18
|
+
* Bundler
|
|
19
|
+
|
|
20
|
+
1. **Fork the repository** on GitHub.
|
|
21
|
+
2. **Clone your fork** locally:
|
|
22
|
+
```bash
|
|
23
|
+
git clone https://github.com/your-username/dlinked.git
|
|
24
|
+
cd dlinked
|
|
25
|
+
```
|
|
26
|
+
3. **Install dependencies** using Bundler:
|
|
27
|
+
```bash
|
|
28
|
+
bundle install
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Running Tests
|
|
32
|
+
|
|
33
|
+
This project uses Minitest for testing. To run the full test suite, use the following command:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
bundle exec rake test
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
This will also run SimpleCov to generate a code coverage report in the `coverage/` directory. When submitting a pull request, please ensure that your changes are covered by tests and that you maintain or increase the test coverage.
|
|
40
|
+
|
|
41
|
+
### Code Style
|
|
42
|
+
|
|
43
|
+
This project uses RuboCop to enforce a consistent code style. To check your code for style violations, run:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
bundle exec rubocop
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Many style violations can be automatically fixed by running:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
bundle exec rubocop -A
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Please ensure your code follows the project's style guidelines before submitting a pull request.
|
|
56
|
+
|
|
57
|
+
## How to Contribute
|
|
58
|
+
|
|
59
|
+
### Reporting Bugs
|
|
60
|
+
|
|
61
|
+
If you find a bug, please open an issue on GitHub. Please include:
|
|
62
|
+
* A clear and descriptive title.
|
|
63
|
+
* A detailed description of the problem, including steps to reproduce it.
|
|
64
|
+
* The expected behavior and what actually happened.
|
|
65
|
+
* The version of DLinked and Ruby you are using.
|
|
66
|
+
|
|
67
|
+
### Submitting Changes (Pull Requests)
|
|
68
|
+
|
|
69
|
+
1. Create a new branch for your changes:
|
|
70
|
+
```bash
|
|
71
|
+
git checkout -b your-feature-branch-name
|
|
72
|
+
```
|
|
73
|
+
2. Make your changes, and add or update tests as needed.
|
|
74
|
+
3. Ensure the test suite passes (`bundle exec rake test`).
|
|
75
|
+
4. Ensure your code passes the RuboCop checks (`bundle exec rubocop`).
|
|
76
|
+
5. Commit your changes with a clear and descriptive commit message.
|
|
77
|
+
6. Push your branch to your fork on GitHub:
|
|
78
|
+
```bash
|
|
79
|
+
git push origin your-feature-branch-name
|
|
80
|
+
```
|
|
81
|
+
7. Open a pull request from your fork to the main DLinked repository.
|
|
82
|
+
8. In the pull request description, please explain the problem you are solving and the changes you have made.
|
|
83
|
+
|
|
84
|
+
## Code of Conduct
|
|
85
|
+
|
|
86
|
+
By participating in this project, you are expected to uphold our [Code of Conduct](CODE_OF_CONDUCT.md).
|
|
87
|
+
|
|
88
|
+
Thank you for your contribution!
|
data/README.md
CHANGED
|
@@ -28,9 +28,18 @@ gem install dlinked
|
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
30
|
bundle exec rake test
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Documentation
|
|
34
|
+
|
|
35
|
+
To generate the YARD documentation for this project, run:
|
|
31
36
|
|
|
37
|
+
```bash
|
|
38
|
+
bundle exec yard doc
|
|
32
39
|
```
|
|
33
40
|
|
|
41
|
+
This will create a `doc/` directory containing the full HTML documentation. You can view it by opening `doc/index.html` in your browser.
|
|
42
|
+
|
|
34
43
|
## Usage
|
|
35
44
|
### 1. Basic Initialization and O(1) Operations
|
|
36
45
|
Demonstrate creating the list, adding elements to both ends, and removing them quickly.
|
|
@@ -241,6 +250,57 @@ lru_list.clear
|
|
|
241
250
|
lru_list.size # => 0
|
|
242
251
|
|
|
243
252
|
```
|
|
253
|
+
|
|
254
|
+
### Real-World Example: A Complete LRU Cache
|
|
255
|
+
While `DLinked::CacheList` provides the low-level, high-performance key tracking, you can easily build a complete, practical `LRUCache` class around it.
|
|
256
|
+
|
|
257
|
+
The following example demonstrates how to combine `DLinked::CacheList` with a `Hash` for data storage to create a fully functional LRU cache.
|
|
258
|
+
|
|
259
|
+
```ruby
|
|
260
|
+
# A complete, working implementation of a Least Recently Used (LRU) Cache
|
|
261
|
+
# built on top of DLinked::CacheList.
|
|
262
|
+
class LRUCache
|
|
263
|
+
attr_reader :capacity, :size
|
|
264
|
+
|
|
265
|
+
def initialize(capacity)
|
|
266
|
+
raise ArgumentError, 'Capacity must be a positive integer' unless capacity.is_a?(Integer) && capacity > 0
|
|
267
|
+
|
|
268
|
+
@capacity = capacity
|
|
269
|
+
@size = 0
|
|
270
|
+
@list = DLinked::CacheList.new
|
|
271
|
+
@data = {}
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def get(key)
|
|
275
|
+
return nil unless @data.key?(key)
|
|
276
|
+
@list.move_to_head_by_key(key)
|
|
277
|
+
@data[key]
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def set(key, value)
|
|
281
|
+
if @data.key?(key)
|
|
282
|
+
@list.move_to_head_by_key(key)
|
|
283
|
+
else
|
|
284
|
+
@list.prepend_key(key, value)
|
|
285
|
+
@size += 1
|
|
286
|
+
evict if @size > @capacity
|
|
287
|
+
end
|
|
288
|
+
@data[key] = value
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
private
|
|
292
|
+
|
|
293
|
+
def evict
|
|
294
|
+
lru_key = @list.pop_key
|
|
295
|
+
return unless lru_key
|
|
296
|
+
@data.delete(lru_key)
|
|
297
|
+
@size -= 1
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
For a complete, runnable demonstration of this `LRUCache` class in action, see the [lru_cache_example.rb](./lru_cache_example.rb) file.
|
|
303
|
+
|
|
244
304
|
## ⚡ Performance Characteristics
|
|
245
305
|
This library is designed to offer the guaranteed performance benefits of a Doubly Linked List over a standard Ruby `Array` for certain operations.
|
|
246
306
|
|
|
@@ -254,6 +314,34 @@ This library is designed to offer the guaranteed performance benefits of a Doubl
|
|
|
254
314
|
|
|
255
315
|
|
|
256
316
|
|
|
317
|
+
## ⚡ Performance Benchmarks
|
|
318
|
+
|
|
319
|
+
To quantify the performance benefits of `DLinked::List` over Ruby's `Array`, a benchmark suite is available in `benchmark.rb`. It uses the `benchmark-ips` gem to compare the performance of single operations on a pre-filled data structure of 10,000 items.
|
|
320
|
+
|
|
321
|
+
The benchmark measures a single operation (e.g., `shift`) and immediately performs the inverse operation (e.g., `push`) to ensure the list size remains constant for every measurement. This provides a more accurate comparison of how each data structure handles these calls on a large collection.
|
|
322
|
+
|
|
323
|
+
You can run the benchmark yourself:
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
bundle install
|
|
327
|
+
bundle exec ruby benchmark.rb
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
**Results:**
|
|
331
|
+
|
|
332
|
+
The results below were generated on Ruby 3.1.4. They demonstrate that for a list of 10,000 items, the performance of Ruby's native, C-implemented `Array` is significantly faster than `DLinked::List`'s pure Ruby implementation, even for operations where the linked list has a better theoretical time complexity.
|
|
333
|
+
|
|
334
|
+
| Operation | Comparison | Analysis |
|
|
335
|
+
| :--- | :--- | :--- |
|
|
336
|
+
| `append` / `push` | `Array` is ~5.2x faster | `Array` is faster. This is expected as it's a highly optimized C implementation, while `DLinked::List` has the overhead of Ruby method calls and `Node` object allocation. |
|
|
337
|
+
| `prepend` / `unshift`| `Array` is ~5.1x faster | Surprisingly, `Array#unshift` is still faster at this scale. The cost of memory shifting in C for 10,000 items is lower than the overhead of `DLinked::List`'s Ruby implementation. |
|
|
338
|
+
| `pop` | `Array` is ~5.3x faster | Similar to `push`, the native `Array` implementation is faster for this O(1) operation. |
|
|
339
|
+
| `shift` | `Array` is ~5.4x faster | Like `unshift`, `Array#shift`'s O(n) operation in C is faster than `DLinked::List`'s O(1) operation in Ruby at this list size, due to the overhead of the Ruby implementation. |
|
|
340
|
+
|
|
341
|
+
**Conclusion:**
|
|
342
|
+
|
|
343
|
+
These benchmarks show that the raw speed of the underlying C implementation of `Array` outweighs the Big-O algorithmic advantages of a pure Ruby linked list for collections of this size. `DLinked::List` is better suited for educational purposes or for algorithms where the explicit node structure and pointer manipulation are more important than raw wall-clock performance against `Array`.
|
|
344
|
+
|
|
257
345
|
## 💾 Memory Usage
|
|
258
346
|
|
|
259
347
|
While memory usage is highly dependent on the objects stored, the overhead of the list structure itself is minimal and highly efficient:
|
data/benchmark.rb
CHANGED
|
@@ -1,54 +1,78 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
require 'benchmark'
|
|
3
|
+
require 'benchmark/ips'
|
|
4
|
+
require 'dlinked'
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
puts "Ruby version: #{RUBY_VERSION}"
|
|
7
|
+
puts "DLinked version: #{DLinked::VERSION}"
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
LIST_SIZE = 10_000
|
|
10
|
+
puts "\n--- Benchmarking single operations on a list of #{LIST_SIZE} items ---"
|
|
11
|
+
puts "A single operation is performed, and then undone to maintain list size."
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
# --- Setup ---
|
|
14
|
+
array = (0...LIST_SIZE).to_a
|
|
15
|
+
list = DLinked::List.new
|
|
16
|
+
array.each { |i| list.append(i) }
|
|
17
|
+
|
|
18
|
+
# --- Benchmark Suite ---
|
|
19
|
+
|
|
20
|
+
puts "\nAppend/Push at the end:"
|
|
21
|
+
Benchmark.ips do |x|
|
|
22
|
+
x.report("Array#push") do
|
|
23
|
+
array.push(0)
|
|
24
|
+
array.pop
|
|
18
25
|
end
|
|
19
|
-
end
|
|
20
26
|
|
|
21
|
-
|
|
27
|
+
x.report("DLinked::List#append") do
|
|
28
|
+
list.append(0)
|
|
29
|
+
list.pop
|
|
30
|
+
end
|
|
22
31
|
|
|
23
|
-
|
|
32
|
+
x.compare!
|
|
33
|
+
end
|
|
24
34
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
35
|
+
puts "\nPrepend/Unshift at the beginning:"
|
|
36
|
+
Benchmark.ips do |x|
|
|
37
|
+
x.report("Array#unshift") do
|
|
38
|
+
array.unshift(0)
|
|
39
|
+
array.shift
|
|
28
40
|
end
|
|
29
41
|
|
|
30
|
-
x.report(
|
|
31
|
-
|
|
42
|
+
x.report("DLinked::List#prepend") do
|
|
43
|
+
list.prepend(0)
|
|
44
|
+
list.shift
|
|
32
45
|
end
|
|
33
46
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
class_nodes = Array.new(1000) { |i| NodeClass.new(i, nil, nil) }
|
|
47
|
+
x.compare!
|
|
48
|
+
end
|
|
37
49
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
end
|
|
50
|
+
puts "\nPop from the end:"
|
|
51
|
+
Benchmark.ips do |x|
|
|
52
|
+
x.report("Array#pop") do
|
|
53
|
+
el = array.pop
|
|
54
|
+
array.push(el)
|
|
44
55
|
end
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
node.value = v + 1
|
|
50
|
-
end
|
|
56
|
+
|
|
57
|
+
x.report("DLinked::List#pop") do
|
|
58
|
+
el = list.pop
|
|
59
|
+
list.append(el)
|
|
51
60
|
end
|
|
61
|
+
|
|
62
|
+
x.compare!
|
|
52
63
|
end
|
|
53
64
|
|
|
54
|
-
puts "\
|
|
65
|
+
puts "\nShift from the beginning:"
|
|
66
|
+
Benchmark.ips do |x|
|
|
67
|
+
x.report("Array#shift") do
|
|
68
|
+
el = array.shift
|
|
69
|
+
array.push(el)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
x.report("DLinked::List#shift") do
|
|
73
|
+
el = list.shift
|
|
74
|
+
list.append(el)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
x.compare!
|
|
78
|
+
end
|
data/dlinked.gemspec
CHANGED
|
@@ -38,4 +38,5 @@ Gem::Specification.new do |spec|
|
|
|
38
38
|
spec.add_development_dependency "rubocop-minitest", "~> 0.16.0"
|
|
39
39
|
spec.add_development_dependency "simplecov", "~> 0.22"
|
|
40
40
|
spec.add_development_dependency "yard", "~> 0.9"
|
|
41
|
+
spec.add_development_dependency "benchmark-ips", "~> 2.8"
|
|
41
42
|
end
|