dlinked 0.1.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 724380c942032cdc4308e6457a68996d0cffd35681a8a2f869e4f7e9f4938d59
4
+ data.tar.gz: 073d48b50858323574657b66386f14c3ec7137857a9dc16f3879579bcc09b002
5
+ SHA512:
6
+ metadata.gz: 260d3fefb326f3e41243b16875b38880d599706a3bcc497dd18583ddacf232700aa1e14e06033ad2bc21f01869c4c67df6fb29c5d5395a0e1fef4c26b591ab26
7
+ data.tar.gz: 79c0f06249f6c3df620248d4114ab3b18bdb3110c8c9e0284eee945362b5cc698c8848b5464e505ba4590ae64c93885086d6f350799f90f3ef27cd9a4d9f9ba6
@@ -0,0 +1,122 @@
1
+ # .github/workflows/release.yml
2
+
3
+ name: Publish Ruby Gem
4
+
5
+ # This workflow runs when:
6
+ on:
7
+ push:
8
+ branches:
9
+ - main # Run tests and build on every push to main
10
+ # Run the build and release steps ONLY when a new tag is pushed (e.g., v0.1.0)
11
+ release:
12
+ types: [published]
13
+
14
+ jobs:
15
+ test:
16
+ name: Run Tests & Build Gem
17
+ runs-on: ubuntu-latest
18
+
19
+ # Use a matrix to test across common Ruby versions (e.g., 3.0, 3.1, 3.2, 3.3)
20
+ strategy:
21
+ matrix:
22
+ ruby: ['3.1', '3.2', '3.3']
23
+
24
+ steps:
25
+ - name: Checkout Code
26
+ uses: actions/checkout@v4
27
+
28
+ - name: Set up Ruby
29
+ uses: ruby/setup-ruby@v1
30
+ with:
31
+ ruby-version: ${{ matrix.ruby }}
32
+ bundler-cache: true # Installs dependencies from Gemfile.lock
33
+
34
+ - name: Run Tests with SimpleCov
35
+ run: bundle exec rake test
36
+
37
+ - name: Upload Test Coverage Report (Artifact)
38
+ uses: actions/upload-artifact@v4
39
+ with:
40
+ name: coverage-report-${{ matrix.ruby }}
41
+ path: coverage/
42
+
43
+ # Only build the .gem file on the latest stable Ruby version
44
+ - name: Build Gem
45
+ if: matrix.ruby == '3.3'
46
+ run: bundle exec rake build
47
+
48
+ - name: Upload Gem Artifact
49
+ if: matrix.ruby == '3.3'
50
+ uses: actions/upload-artifact@v4
51
+ with:
52
+ name: dlinked-gem
53
+ path: pkg/
54
+ deploy_docs:
55
+ name: Deploy Documentation to GitHub Pages
56
+ runs-on: ubuntu-latest
57
+ needs: test # Ensure tests pass before deploying docs
58
+
59
+ # Run only when pushing to the main branch
60
+ if: github.ref == 'refs/heads/main'
61
+
62
+ permissions:
63
+ contents: write # Needed to push generated documentation to gh-pages branch
64
+
65
+ steps:
66
+ - name: Checkout Code
67
+ uses: actions/checkout@v4
68
+
69
+ - name: Set up Ruby
70
+ uses: ruby/setup-ruby@v1
71
+ with:
72
+ ruby-version: 3.3
73
+ bundler-cache: true
74
+
75
+ - name: Install YARD (if not in Gemfile)
76
+ run: gem install yard
77
+
78
+ - name: Generate YARD Documentation
79
+ run: yard doc
80
+
81
+ - name: Deploy to GitHub Pages
82
+ uses: peaceiris/actions-gh-pages@v3
83
+ with:
84
+ # This is the authentication token provided by GitHub
85
+ github_token: ${{ secrets.GITHUB_TOKEN }}
86
+ # The branch where YARD documentation will be pushed
87
+ publish_branch: gh-pages
88
+ # The directory to publish (generated by yard doc)
89
+ publish_dir: ./doc
90
+ publish:
91
+ name: Publish to RubyGems
92
+ runs-on: ubuntu-latest
93
+ needs: test
94
+
95
+ # This step only executes when a release is published (a tag is pushed)
96
+ if: github.event_name == 'release' && github.event.action == 'published'
97
+
98
+ steps:
99
+ - name: Checkout Code (Needed to access config)
100
+ uses: actions/checkout@v4
101
+
102
+ - name: Set up Ruby
103
+ uses: ruby/setup-ruby@v1
104
+ with:
105
+ ruby-version: 3.3 # Use the latest version for publishing
106
+ bundler-cache: false # We only need the runtime
107
+
108
+ - name: Download Gem Artifact
109
+ uses: actions/download-artifact@v4
110
+ with:
111
+ name: dlinked-gem
112
+ path: pkg/
113
+
114
+ - name: Set up RubyGems credentials
115
+ run: |
116
+ mkdir -p ~/.gem
117
+ echo ":rubygems_api_key: ${{ secrets.RUBYGEMS_API_KEY }}" > ~/.gem/credentials
118
+ chmod 0600 ~/.gem/credentials
119
+
120
+ - name: Push Gem to RubyGems.org
121
+ # The gem file is expected to be in the 'pkg/' directory
122
+ run: gem push pkg/*.gem
data/.gitignore ADDED
@@ -0,0 +1,83 @@
1
+
2
+ *.gem
3
+ *.rbc
4
+ /.config
5
+ /coverage/
6
+ /InstalledFiles
7
+ /pkg/
8
+ /spec/reports/
9
+ /spec/examples.txt
10
+ /test/tmp/
11
+ /test/version_tmp/
12
+ /tmp/
13
+
14
+ # Used by dotenv library to load environment variables.
15
+ .env
16
+
17
+ # Ignore Byebug command history file.
18
+ .byebug_history
19
+
20
+ ## Specific to RubyMotion:
21
+ .dat*
22
+ .repl_history
23
+ build/
24
+ *.bridgesupport
25
+ build-iPhoneOS/
26
+ build-iPhoneSimulator/
27
+
28
+ ## Specific to RubyMotion (use of CocoaPods):
29
+ #
30
+ # We recommend against adding the Pods directory to your .gitignore. However
31
+ # you should judge for yourself, the pros and cons are mentioned at:
32
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
33
+ #
34
+ # vendor/Pods/
35
+
36
+ ## Documentation cache and generated files:
37
+ /.yardoc/
38
+ /_yardoc/
39
+ /doc/
40
+ /rdoc/
41
+
42
+ ## Environment normalization:
43
+ /.bundle/
44
+ /vendor/bundle
45
+ /lib/bundler/man/
46
+
47
+ # for a library or gem, you might want to ignore these files since the code is
48
+ # intended to run in multiple environments; otherwise, check them in:
49
+ Gemfile.lock
50
+ .ruby-version
51
+ .ruby-gemset
52
+
53
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
54
+ .rvmrc
55
+
56
+ # Used by RuboCop. Remote config files pulled in from inherit_from directive.
57
+ .rubocop-https?--*
58
+
59
+ # IDE files
60
+ .idea/
61
+ .vscode/
62
+ *.swp
63
+ *.swo
64
+ *~
65
+
66
+ # OS files
67
+ .DS_Store
68
+ Thumbs.db
69
+ # --- Ruby Gem/Bundler Artifacts ---
70
+ # Ignores the compiled .gem file (which should be built on CI/release)
71
+ *.gem
72
+ # Ignores the cached extension files
73
+ /ext/**/tmp
74
+ /ext/**/objects
75
+ /ext/**/gems
76
+
77
+ # --- Test/Coverage Reports ---
78
+ # SimpleCov coverage output directory
79
+ /coverage
80
+
81
+ # --- Documentation Artifacts (YARD/RDoc) ---
82
+ # When you generate API documentation, it creates this folder
83
+ /doc
data/.rubocop.yml ADDED
@@ -0,0 +1,45 @@
1
+ # .rubocop.yml
2
+
3
+ # We'll use the default Rubocop configuration as a starting point.
4
+ inherit_gem:
5
+ rubocop-minitest:
6
+ - config/default.yml
7
+
8
+ require:
9
+ - rubocop-minitest # Good practice since you are using Minitest
10
+
11
+ AllCops:
12
+ # Exclude the gem build artifacts, configuration files, etc.
13
+ Exclude:
14
+ - 'dlinked-*.gem'
15
+ - 'vendor/**/*'
16
+ - 'bin/**/*'
17
+ - 'node_modules/**/*'
18
+ - 'pkg/**/*'
19
+ - 'Rakefile' # Often contains unique logic
20
+ - 'dlinked.gemspec'
21
+
22
+ # Target Ruby version is often set here
23
+ TargetRubyVersion: 3.1 # Set this to the version you are primarily developing on
24
+
25
+ # --- Specific Cops Configuration ---
26
+
27
+ Style/FrozenStringLiteralComment:
28
+ Enabled: true
29
+
30
+ Metrics/BlockLength:
31
+ # Allows longer blocks, often needed for test files
32
+ Exclude:
33
+ - 'test/**/*.rb'
34
+ - 'benchmark.rb'
35
+ Max: 100
36
+
37
+ Metrics/MethodLength:
38
+ # Your list.rb methods are complex due to assignment/slice logic
39
+ Exclude:
40
+ - 'lib/d_linked/list.rb'
41
+ Max: 20
42
+
43
+ # Allow documentation for public methods to be required
44
+ Documentation:
45
+ Enabled: false
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+
2
+ MIT License
3
+
4
+ Copyright (c) 2025 [Your Name]
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,221 @@
1
+ # DLinked
2
+
3
+ A fast, lightweight doubly linked list implementation for Ruby.
4
+
5
+ ## Features
6
+
7
+ - **Lightweight**: Minimal memory footprint using optimized node class
8
+ - **Fast**: O(1) operations for insertion/deletion at both ends
9
+ - **Ruby-native**: Includes Enumerable for full integration with Ruby
10
+ - **Bidirectional**: Iterate forward or backward efficiently
11
+
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ ```ruby
18
+ gem 'dlinked'
19
+ ```
20
+
21
+ Or install it yourself:
22
+
23
+ ```bash
24
+ gem install dlinked
25
+ ```
26
+
27
+ ## Test
28
+
29
+ ```bash
30
+ bundle exec ruby test/test_d_linked_list.rb
31
+
32
+ ```
33
+
34
+ ## Usage
35
+ ### 1. Basic Initialization and O(1) Operations
36
+ Demonstrate creating the list, adding elements to both ends, and removing them quickly.
37
+
38
+
39
+ ```ruby
40
+ require 'dlinked'
41
+
42
+ # 1. Initialization
43
+ list = DLinked::List.new
44
+ list.size # => 0
45
+
46
+ # 2. O(1) Prepend (Add to Head)
47
+ list.prepend(20).prepend(10) # Using method chaining
48
+ # The list now looks like: [10, 20]
49
+
50
+ # 3. O(1) Append (Add to Tail)
51
+ list << 30
52
+ list.append(40)
53
+ # The list now looks like: [10, 20, 30, 40]
54
+
55
+ # 4. O(1) Removal
56
+ list.shift # => 10 (Removes from head)
57
+ list.pop # => 40 (Removes from tail)
58
+ # The list now looks like: [20, 30]
59
+
60
+ list.to_a # => [20, 30]
61
+ ```
62
+ ### 2. Array-Like Access and Assignment
63
+ Show users how the list behaves like a Ruby Array, utilizing the [] and []= methods you implemented.
64
+
65
+
66
+ ```ruby
67
+ list = DLinked::List.new
68
+ list << 'A' << 'B' << 'C' << 'D'
69
+
70
+ # 1. Access by Index
71
+ list[2] # => 'C'
72
+ list[-1] # => 'D' (Accessing from the tail)
73
+
74
+ # 2. Element Assignment (O(n) but familiar)
75
+ list[1] = 'B_NEW'
76
+ list.to_a # => ["A", "B_NEW", "C", "D"]
77
+
78
+ # 3. Slice/Range Access
79
+ list[1, 2].to_a # => ["B_NEW", "C"] (start at 1, length 2)
80
+ list[0..2].to_a # => ["A", "B_NEW", "C"] (using a Range)
81
+
82
+ # 4. Slice Replacement (Deletion and Insertion)
83
+ list[1, 2] = ['X', 'Y', 'Z'] # Replace two elements with three new elements
84
+ list.to_a # => ["A", "X", "Y", "Z", "D"]
85
+ list.size # => 5
86
+ ```
87
+ ### 3. Enumerable and Iteration
88
+ Demonstrate how it works seamlessly with standard Ruby collection methods.
89
+
90
+
91
+ ```ruby
92
+ list = DLinked::List.new
93
+ list << 10 << 20 << 30 << 40
94
+
95
+ # Standard Enumerable methods work out of the box
96
+ list.map { |n| n * 2 } # => [20, 40, 60, 80]
97
+ list.select(&:even?) # => [10, 20, 30, 40]
98
+
99
+ # O(n) Insertion in the middle
100
+ list.insert(2, 25)
101
+ list.to_a # => [10, 20, 25, 30, 40]
102
+
103
+ # O(n) Deletion by value
104
+ list.delete(20)
105
+ list.to_a # => [10, 25, 30, 40]
106
+ ```
107
+
108
+
109
+ ### 4. Utility, Inspection, and Conversion Methods
110
+ This section covers the basic checks, conversions, and advanced destructive operations.
111
+
112
+ ```ruby
113
+
114
+ list = DLinked::List.new
115
+ list << 10 << 20 << 30
116
+
117
+ # --- Basic Inspection ---
118
+
119
+ list.size # => 3
120
+ list.length # => 3 (alias for size)
121
+ list.empty? # => false
122
+ DLinked::List.new.empty? # => true
123
+
124
+ list.first # => 10 (O(1))
125
+ list.last # => 30 (O(1))
126
+
127
+ list.to_a # => [10, 20, 30]
128
+ list.to_s # => "[10, 20, 30]"
129
+ list.inspect # => "[10, 20, 30]"
130
+
131
+ # --- Lookup ---
132
+
133
+ list.index(20) # => 1
134
+ list.index(99) # => nil (Value not found)
135
+ ```
136
+
137
+ ### 5. Advanced Insertion, Deletion, and Slicing
138
+ Demonstrate non-O(1) operations that are useful for list manipulation, including the destructive slice! method.
139
+
140
+ ```ruby
141
+
142
+
143
+ list = DLinked::List.new
144
+ list << 'A' << 'B' << 'C' << 'D' << 'E'
145
+
146
+ # --- O(n) Insertion ---
147
+
148
+ # Insert at the middle (index 2)
149
+ list.insert(2, 'Z')
150
+ list.to_a # => ["A", "B", "Z", "C", "D", "E"]
151
+ list.insert(0, 'Start') # Same as prepend (O(1))
152
+ list.to_a # => ["Start", "A", "B", "Z", "C", "D", "E"]
153
+
154
+ # --- Deletion by Value ---
155
+
156
+ list.delete('Z') # => "Z" (Returns the deleted value)
157
+ list.to_a # => ["Start", "A", "B", "C", "D", "E"]
158
+
159
+ # --- Destructive Slicing (slice!) ---
160
+
161
+ # Extract and remove a slice of 2 elements, starting at index 1
162
+ removed_slice = list.slice!(1, 2)
163
+ list.to_a # => ["Start", "C", "D", "E"]
164
+ removed_slice.to_a # => ["A", "B"]
165
+
166
+ # Remove one element using a range (index 2)
167
+ list.slice!(2..2)
168
+ list.to_a # => ["Start", "C", "E"]
169
+
170
+ list.size # => 3
171
+ ```
172
+
173
+ ### 6. Concatenation and Arithmetic
174
+ Demonstrate how lists can be combined.
175
+
176
+ ```ruby
177
+
178
+
179
+ list1 = DLinked::List.new << 1 << 2
180
+ list2 = DLinked::List.new << 3 << 4
181
+
182
+ # Non-destructive concatenation (returns a new list)
183
+ new_list = list1 + list2
184
+ new_list.to_a # => [1, 2, 3, 4]
185
+ list1.to_a # => [1, 2] (list1 is unchanged)
186
+
187
+ # Destructive concatenation (modifies list1)
188
+ list1.concat(list2)
189
+ list1.to_a # => [1, 2, 3, 4]
190
+ ```
191
+
192
+ ## ⚡ Performance Characteristics
193
+ This library is designed to offer the guaranteed performance benefits of a Doubly Linked List over a standard Ruby `Array` for certain operations.
194
+
195
+ | Operation | Method(s) | Complexity | Notes |
196
+ | - | - | - | - |
197
+ | End Insertion | append, prepend, push, unshift, << | $O(1)$ | Constant time, regardless of list size. |
198
+ | End Deletion | pop, shift | $O(1)$ | Constant time. |
199
+ | End Access | first, last | $O(1)$ | Constant time access to boundary values. |
200
+ | Middle Insertion/Deletion | insert, delete (by value), slice!, []= (slice) | $O(n)$ | Requires traversal to find the node. |
201
+ | Random Access/Search | [] (getter), index | $O(n)$ | Requires traversal; average time is $O(n/2)$. |
202
+
203
+
204
+
205
+ ## 💾 Memory Usage
206
+
207
+ While memory usage is highly dependent on the objects stored, the overhead of the list structure itself is minimal and highly efficient:
208
+
209
+ * **Node Overhead:** Each node in the list uses approximately 40 bytes. This includes the object header, and three necessary pointers:
210
+
211
+ 1. Pointer to the stored **value**
212
+
213
+ 2. Pointer to the **next** node
214
+
215
+ 3. Pointer to the **previous** node
216
+
217
+ * **Efficiency Advantage:** This structured overhead is often more memory-efficient than a large Ruby `Array` that requires constant reallocation and copying when its capacity is exceeded, especially if the `Array` is being modified frequently at the head.
218
+
219
+ ## License
220
+
221
+ MIT License. See LICENSE.txt for details.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ # Rakefile
2
+ require "bundler/gem_tasks"
3
+ require "rake/testtask"
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/test_*.rb"]
8
+ end
9
+
10
+ task default: :test
data/benchmark.rb ADDED
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ # benchmark.rb - Compare Struct vs Class performance
4
+ # Run with: ruby benchmark.rb
5
+ require 'benchmark'
6
+
7
+ # Struct version
8
+ NodeStruct = Struct.new(:value, :prev, :next)
9
+
10
+ # Class version
11
+ class NodeClass
12
+ attr_accessor :value, :prev, :next
13
+
14
+ def initialize(value, prev_node, next_node)
15
+ @value = value
16
+ @prev = prev_node
17
+ @next = next_node
18
+ end
19
+ end
20
+
21
+ N = 2_000_000
22
+
23
+ puts "Creating and accessing #{N} nodes:\n\n"
24
+
25
+ Benchmark.bm(20) do |x|
26
+ x.report('Class creation:') do
27
+ N.times { |i| NodeClass.new(i, nil, nil) }
28
+ end
29
+
30
+ x.report('Struct creation:') do
31
+ N.times { |i| NodeStruct.new(i, nil, nil) }
32
+ end
33
+
34
+ # Test access speed (the important part!)
35
+ struct_nodes = Array.new(1000) { |i| NodeStruct.new(i, nil, nil) }
36
+ class_nodes = Array.new(1000) { |i| NodeClass.new(i, nil, nil) }
37
+
38
+ x.report('Class access:') do
39
+ N.times do
40
+ node = class_nodes[rand(1000)]
41
+ v = node.value
42
+ node.value = v + 1
43
+ end
44
+ end
45
+ x.report('Struct access:') do
46
+ N.times do
47
+ node = struct_nodes[rand(1000)]
48
+ v = node.value
49
+ node.value = v + 1
50
+ end
51
+ end
52
+ end
53
+
54
+ puts "\nConclusion: Run this benchmark on your target Ruby version to decide!"
data/dlinked.gemspec ADDED
@@ -0,0 +1,37 @@
1
+
2
+ # frozen_string_literal: true
3
+ require_relative "lib/d_linked/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "dlinked"
7
+ spec.version = DLinked::VERSION
8
+ # spec.version = File.read(File.expand_path("lib/d_linked/version.rb")).scan(/VERSION = "([^"]+)"/).flatten.first
9
+ spec.authors = ["Daniele Frisanco"]
10
+ spec.email = ["daniele.frisanco@gmail.com"]
11
+
12
+ spec.summary = "A highly performant Doubly Linked List implementation for Ruby."
13
+ spec.description = "Provides a native Doubly Linked List data structure in Ruby, focusing on O(1) performance for head/tail operations and standard Enumerable compatibility."
14
+ spec.homepage = "https://github.com/danielefrisanco/dlinked"
15
+ spec.license = "MIT"
16
+
17
+ spec.required_ruby_version = ">= 2.7.0"
18
+
19
+ # --- Files to Include in the Gem ---
20
+
21
+ # Ensure all necessary files are included in the built gem
22
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
23
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|benchmark)/}) }
24
+ end
25
+
26
+ # Pin the main file that gets loaded when someone 'require's the gem
27
+ spec.require_paths = ["lib"]
28
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
29
+
30
+ spec.add_development_dependency "bundler", "~> 2.0"
31
+ spec.add_development_dependency "rake", "~> 13.0"
32
+ spec.add_development_dependency "minitest", "~> 5.0"
33
+ spec.add_development_dependency "rubocop", "~> 1.0"
34
+ spec.add_development_dependency "rubocop-minitest", "~> 0.16.0"
35
+ spec.add_development_dependency "simplecov", "~> 0.22"
36
+ spec.add_development_dependency "yard", "~> 0.9"
37
+ end
data/example.rb ADDED
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dlinked' # Add this line!
4
+
5
+ list = DLinked::List.new
6
+ list.append(2).append(3).prepend(1)
7
+ puts "After operations: #{list}"
8
+ puts "Size: #{list.size}"
9
+
10
+ # More examples...
11
+ list << 10 << 20 << 30
12
+ puts "List: #{list}"
13
+ puts "First: #{list.first}, Last: #{list.last}"
14
+
15
+ list.each { |v| puts " Value: #{v}" }
16
+ if __FILE__ == $PROGRAM_NAME
17
+ list = DLinked::List.new
18
+
19
+ # Test append and prepend
20
+ list.append(2).append(3).prepend(1)
21
+ puts "After operations: #{list}" # [1, 2, 3]
22
+ puts "Size: #{list.size}" # 3
23
+
24
+ # Test pop and shift
25
+ puts "Pop: #{list.pop}" # 3
26
+ puts "Shift: #{list.shift}" # 1
27
+ puts "After pop/shift: #{list}" # [2]
28
+
29
+ # Test iteration
30
+ list.append(4).append(6).prepend(0)
31
+ puts "\nForward iteration:"
32
+ list.each { |v| puts " #{v}" }
33
+
34
+ puts "\nReverse iteration:"
35
+ list.reverse_each { |v| puts " #{v}" }
36
+
37
+ # Test enumerable methods
38
+ puts "\nSquared values: #{list.map { |v| v * v }}"
39
+ puts "Sum: #{list.sum}"
40
+
41
+ # Test delete
42
+ list.delete(2)
43
+ puts "After deleting 2: #{list}"
44
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DLinked
4
+ class List
5
+ # The Node class represents a single element in the Doubly Linked List.
6
+ # It holds the element's value and pointers to the next and previous nodes.
7
+ # Note: If this class is defined outside of DLinked::List, adjust the scope accordingly.
8
+
9
+ # Represents a single element within the Doubly Linked List.
10
+ #
11
+ # Each node maintains three critical pieces of data:
12
+ # 1. The actual value stored by the user.
13
+ # 2. A pointer to the next node in the list.
14
+ # 3. A pointer to the previous node in the list.
15
+ #
16
+ # This structure is the foundation of the list's O(1) performance for boundary operations.
17
+ class Node
18
+ # @!attribute [rw] value
19
+ # @return [Object] The actual data stored by the user in this node.
20
+ attr_accessor :value
21
+
22
+ # @!attribute [rw] next
23
+ # @return [DLinked::List::Node, nil] A pointer to the subsequent node in the list, or nil if this is the tail.
24
+ attr_accessor :next
25
+
26
+ # @!attribute [rw] prev
27
+ # @return [DLinked::List::Node, nil] A pointer to the preceding node in the list, or nil if this is the head.
28
+ attr_accessor :prev
29
+
30
+ # Initializes a new Node instance.
31
+ #
32
+ # @param value [Object, nil] The value to store in the node.
33
+ # @param prev [DLinked::List::Node, nil] The node preceding this one.
34
+ # @param next_node [DLinked::List::Node, nil] The node succeeding this one.
35
+ def initialize(value = nil, prev = nil, next_node = nil)
36
+ @value = value
37
+ @prev = prev
38
+ @next = next_node
39
+ end
40
+ end
41
+ end
42
+ end