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
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'dlinked'
|
|
4
|
+
|
|
5
|
+
# A complete, working implementation of a Least Recently Used (LRU) Cache
|
|
6
|
+
# built on top of DLinked::CacheList.
|
|
7
|
+
#
|
|
8
|
+
# An LRU Cache is a high-performance cache that evicts the least recently
|
|
9
|
+
# used item when it reaches its capacity. This example demonstrates how
|
|
10
|
+
# DLinked::CacheList's O(1) operations make it a perfect foundation for
|
|
11
|
+
# this data structure.
|
|
12
|
+
class LRUCache
|
|
13
|
+
# @!attribute [r] capacity
|
|
14
|
+
# @return [Integer] The maximum number of items the cache can hold.
|
|
15
|
+
# @!attribute [r] size
|
|
16
|
+
# @return [Integer] The current number of items in the cache.
|
|
17
|
+
attr_reader :capacity, :size
|
|
18
|
+
|
|
19
|
+
# Initializes a new LRU cache.
|
|
20
|
+
#
|
|
21
|
+
# @param capacity [Integer] The maximum number of items the cache can store.
|
|
22
|
+
# Must be a positive integer.
|
|
23
|
+
def initialize(capacity)
|
|
24
|
+
raise ArgumentError, 'Capacity must be a positive integer' unless capacity.is_a?(Integer) && capacity > 0
|
|
25
|
+
|
|
26
|
+
@capacity = capacity
|
|
27
|
+
@size = 0
|
|
28
|
+
@list = DLinked::CacheList.new
|
|
29
|
+
@data = {}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Retrieves the value for a given key from the cache.
|
|
33
|
+
#
|
|
34
|
+
# If the key exists, it is marked as "most recently used" by being moved
|
|
35
|
+
# to the head of the list. This is an O(1) operation.
|
|
36
|
+
#
|
|
37
|
+
# @param key [Object] The key to look up.
|
|
38
|
+
# @return [Object, nil] The cached value, or `nil` if the key is not found.
|
|
39
|
+
def get(key)
|
|
40
|
+
return nil unless @data.key?(key)
|
|
41
|
+
|
|
42
|
+
# "Touch" the item by moving it to the front of the list, marking it as
|
|
43
|
+
# the most recently used.
|
|
44
|
+
@list.move_to_head_by_key(key)
|
|
45
|
+
|
|
46
|
+
@data[key]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Adds or updates a key-value pair in the cache.
|
|
50
|
+
#
|
|
51
|
+
# - If the key already exists, its value is updated.
|
|
52
|
+
# - If the key is new, it is added to the cache.
|
|
53
|
+
#
|
|
54
|
+
# In both cases, the item is marked as "most recently used."
|
|
55
|
+
#
|
|
56
|
+
# If adding a new item causes the cache to exceed its capacity, the
|
|
57
|
+
# least recently used (LRU) item is automatically evicted.
|
|
58
|
+
# All operations here are O(1).
|
|
59
|
+
#
|
|
60
|
+
# @param key [Object] The key to set.
|
|
61
|
+
# @param value [Object] The value to store.
|
|
62
|
+
# @return [Object] The stored value.
|
|
63
|
+
def set(key, value)
|
|
64
|
+
if @data.key?(key)
|
|
65
|
+
# Key exists, just update the value and mark it as most recently used.
|
|
66
|
+
@list.move_to_head_by_key(key)
|
|
67
|
+
else
|
|
68
|
+
# Key is new. Add it to the front of the list.
|
|
69
|
+
# We store the key in the list, and the value in the hash.
|
|
70
|
+
@list.prepend_key(key, key)
|
|
71
|
+
@size += 1
|
|
72
|
+
|
|
73
|
+
# If we exceeded capacity, evict the least recently used item.
|
|
74
|
+
evict if @size > @capacity
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Store the actual data in our hash.
|
|
78
|
+
@data[key] = value
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Removes a key-value pair from the cache.
|
|
82
|
+
# This is an O(1) operation.
|
|
83
|
+
#
|
|
84
|
+
# @param key [Object] The key to remove.
|
|
85
|
+
# @return [Object, nil] The value of the removed item, or `nil` if the key was not found.
|
|
86
|
+
def delete(key)
|
|
87
|
+
return nil unless @data.key?(key)
|
|
88
|
+
|
|
89
|
+
@list.remove_by_key(key)
|
|
90
|
+
@size -= 1
|
|
91
|
+
@data.delete(key)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Provides a human-readable view of the cache's state, showing the
|
|
95
|
+
# order of items from most to least recently used.
|
|
96
|
+
#
|
|
97
|
+
# @return [String]
|
|
98
|
+
def to_s
|
|
99
|
+
# Iterate over the keys in the list (from MRU to LRU) and look up their values.
|
|
100
|
+
items = @list.map { |key| "#{key}:#{@data[key]}" }.join(', ')
|
|
101
|
+
"LRUCache (capacity: #{@capacity}, size: #{@size}) [MRU] #{items} [LRU]"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
private
|
|
105
|
+
|
|
106
|
+
# Evicts the least recently used (LRU) item from the cache.
|
|
107
|
+
# This is an O(1) operation.
|
|
108
|
+
def evict
|
|
109
|
+
# DLinked::CacheList automatically tracks the LRU item at the tail.
|
|
110
|
+
lru_key = @list.pop_key
|
|
111
|
+
return unless lru_key
|
|
112
|
+
|
|
113
|
+
@data.delete(lru_key)
|
|
114
|
+
@size -= 1
|
|
115
|
+
puts "Evicted: #{lru_key}"
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# --- DEMONSTRATION ---
|
|
120
|
+
if __FILE__ == $PROGRAM_NAME
|
|
121
|
+
puts "--- LRU Cache Demonstration (Capacity: 3) ---"
|
|
122
|
+
cache = LRUCache.new(3)
|
|
123
|
+
|
|
124
|
+
puts "\n1. Setting initial values: a=1, b=2, c=3"
|
|
125
|
+
cache.set(:a, 1)
|
|
126
|
+
cache.set(:b, 2)
|
|
127
|
+
cache.set(:c, 3)
|
|
128
|
+
puts cache # c should be MRU
|
|
129
|
+
|
|
130
|
+
puts "\n2. Accessing key 'a'"
|
|
131
|
+
cache.get(:a)
|
|
132
|
+
puts cache # a should now be MRU
|
|
133
|
+
|
|
134
|
+
puts "\n3. Adding a new item 'd=4' (should evict 'b')"
|
|
135
|
+
cache.set(:d, 4)
|
|
136
|
+
puts cache # d should be MRU, b should be gone
|
|
137
|
+
|
|
138
|
+
puts "\n4. Checking contents"
|
|
139
|
+
puts "cache.get(:a) -> #{cache.get(:a)}"
|
|
140
|
+
puts "cache.get(:b) -> #{cache.get(:b) || 'nil (evicted)'}"
|
|
141
|
+
puts "cache.get(:c) -> #{cache.get(:c)}"
|
|
142
|
+
puts "cache.get(:d) -> #{cache.get(:d)}"
|
|
143
|
+
|
|
144
|
+
puts "\n5. Deleting key 'c'"
|
|
145
|
+
cache.delete(:c)
|
|
146
|
+
puts cache
|
|
147
|
+
|
|
148
|
+
puts "\n6. Clearing the cache"
|
|
149
|
+
cache.set(:e, 5)
|
|
150
|
+
cache.set(:f, 6)
|
|
151
|
+
puts cache
|
|
152
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dlinked
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daniele Frisanco
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-01-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -108,6 +108,20 @@ dependencies:
|
|
|
108
108
|
- - "~>"
|
|
109
109
|
- !ruby/object:Gem::Version
|
|
110
110
|
version: '0.9'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: benchmark-ips
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - "~>"
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '2.8'
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - "~>"
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '2.8'
|
|
111
125
|
description: Provides a native Doubly Linked List data structure in Ruby, focusing
|
|
112
126
|
on O(1) performance for head/tail operations and standard Enumerable compatibility.
|
|
113
127
|
email:
|
|
@@ -120,6 +134,8 @@ files:
|
|
|
120
134
|
- ".gitignore"
|
|
121
135
|
- ".rubocop.yml"
|
|
122
136
|
- CHANGELOG.md
|
|
137
|
+
- CODE_OF_CONDUCT.md
|
|
138
|
+
- CONTRIBUTING.md
|
|
123
139
|
- Gemfile
|
|
124
140
|
- LICENSE.txt
|
|
125
141
|
- README.md
|
|
@@ -132,6 +148,7 @@ files:
|
|
|
132
148
|
- lib/d_linked/list/node.rb
|
|
133
149
|
- lib/d_linked/version.rb
|
|
134
150
|
- lib/dlinked.rb
|
|
151
|
+
- lru_cache_example.rb
|
|
135
152
|
homepage: https://github.com/danielefrisanco/dlinked
|
|
136
153
|
licenses:
|
|
137
154
|
- MIT
|