linked_list 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Benchmarks.txt +26 -0
- data/History.txt +4 -0
- data/Manifest.txt +14 -0
- data/README.rdoc +58 -0
- data/Rakefile +22 -0
- data/lib/linked_list.rb +204 -0
- data/lib/linked_list/node.rb +23 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/tasks/benchmark.rake +74 -0
- data/test/test_helper.rb +6 -0
- data/test/test_linked_list.rb +516 -0
- data/test/test_linked_list_node.rb +41 -0
- metadata +102 -0
data/Benchmarks.txt
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
(in /Users/francois/Documents/work/linked_list)
|
2
|
+
Loaded suite -e
|
3
|
+
Started
|
4
|
+
.................................................................................................
|
5
|
+
Finished in 0.015711 seconds.
|
6
|
+
|
7
|
+
97 tests, 97 assertions, 0 failures, 0 errors
|
8
|
+
Building a list of 1 million random strings
|
9
|
+
Took 4.389 seconds
|
10
|
+
Converting to an array
|
11
|
+
Took 1.686 seconds
|
12
|
+
user system total real
|
13
|
+
list#push/pop 0.350000 0.000000 0.350000 ( 0.364001)
|
14
|
+
array#push/pop 0.000000 0.000000 0.000000 ( 0.009894)
|
15
|
+
user system total real
|
16
|
+
list#last 0.000000 0.000000 0.000000 ( 0.000027)
|
17
|
+
array#last 0.000000 0.000000 0.000000 ( 0.000012)
|
18
|
+
user system total real
|
19
|
+
list#first 0.010000 0.000000 0.010000 ( 0.001188)
|
20
|
+
array#first 0.000000 0.000000 0.000000 ( 0.000449)
|
21
|
+
user system total real
|
22
|
+
list#reverse 7.540000 0.170000 7.710000 ( 7.806306)
|
23
|
+
array#reverse 0.010000 0.010000 0.020000 ( 0.008443)
|
24
|
+
user system total real
|
25
|
+
list#dup 23.080000 0.380000 23.460000 ( 24.039316)
|
26
|
+
array#dup 0.000000 0.000000 0.000000 ( 0.000017)
|
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
Benchmarks.txt
|
4
|
+
README.rdoc
|
5
|
+
Rakefile
|
6
|
+
lib/linked_list.rb
|
7
|
+
lib/linked_list/node.rb
|
8
|
+
script/console
|
9
|
+
script/destroy
|
10
|
+
script/generate
|
11
|
+
tasks/benchmark.rake
|
12
|
+
test/test_helper.rb
|
13
|
+
test/test_linked_list.rb
|
14
|
+
test/test_linked_list_node.rb
|
data/README.rdoc
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
= LinkedList
|
2
|
+
|
3
|
+
* http://linkedlist.rubyforge.org/
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
A simple linked list implementation that follows Ruby's conventions.
|
8
|
+
|
9
|
+
== FEATURES/PROBLEMS:
|
10
|
+
|
11
|
+
* FIXME: #dup is implemented by reversing the list twice. This should be pretty slow... Untested, not benchmarked.
|
12
|
+
* This implementation isn't thread safe.
|
13
|
+
|
14
|
+
== SYNOPSIS:
|
15
|
+
|
16
|
+
list = LinkedList.new
|
17
|
+
list << "a"
|
18
|
+
list.push "b"
|
19
|
+
list.unshift "c"
|
20
|
+
list.inspect
|
21
|
+
#=> ("c" ("b" ("a" nil)))
|
22
|
+
list.pop
|
23
|
+
#=> ("b" ("a" nil))
|
24
|
+
list.map {|v| v*2}
|
25
|
+
#=> ("bb" ("aa" nil))
|
26
|
+
|
27
|
+
== REQUIREMENTS:
|
28
|
+
|
29
|
+
* Ruby 1.8.6+
|
30
|
+
|
31
|
+
== INSTALL:
|
32
|
+
|
33
|
+
* sudo gem install linked_list
|
34
|
+
|
35
|
+
== LICENSE:
|
36
|
+
|
37
|
+
(The MIT License)
|
38
|
+
|
39
|
+
Copyright (c) 2008 François Beausoleil
|
40
|
+
|
41
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
42
|
+
a copy of this software and associated documentation files (the
|
43
|
+
'Software'), to deal in the Software without restriction, including
|
44
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
45
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
46
|
+
permit persons to whom the Software is furnished to do so, subject to
|
47
|
+
the following conditions:
|
48
|
+
|
49
|
+
The above copyright notice and this permission notice shall be
|
50
|
+
included in all copies or substantial portions of the Software.
|
51
|
+
|
52
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
53
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
54
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
55
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
56
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
57
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
58
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
%w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
|
2
|
+
require File.dirname(__FILE__) + '/lib/linked_list'
|
3
|
+
|
4
|
+
# Generate all the Rake tasks
|
5
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
6
|
+
$hoe = Hoe.new('linked_list', LinkedList::VERSION) do |p|
|
7
|
+
p.developer('François Beausoleil', 'francois@teksol.info')
|
8
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
9
|
+
p.rubyforge_name = "linkedlist"
|
10
|
+
p.extra_dev_deps = [
|
11
|
+
['newgem', ">= #{::Newgem::VERSION}"],
|
12
|
+
['francois-shoulda', ">= 2.0.5"]
|
13
|
+
]
|
14
|
+
|
15
|
+
p.clean_globs |= %w[**/.DS_Store tmp *.log]
|
16
|
+
path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
|
17
|
+
p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
|
18
|
+
p.rsync_args = '-av --delete --ignore-errors'
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'newgem/tasks' # load /tasks/*.rake
|
22
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
data/lib/linked_list.rb
ADDED
@@ -0,0 +1,204 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
require "linked_list/node"
|
5
|
+
|
6
|
+
# Linked list implementation.
|
7
|
+
# Linked lists have O(1) insertions and pops. Traversal is O(n).
|
8
|
+
#
|
9
|
+
# == Implementation Notes
|
10
|
+
# This implementation isn't thread safe. In fact, traversal is thread safe, but insertions and removals aren't.
|
11
|
+
#
|
12
|
+
# This implementation has problems with #dup, but beyond that, it looks fine. It needs to be battle tested though.
|
13
|
+
# As a performance optimization, #size is cached and maintained locally, instead of being recalculated everytime.
|
14
|
+
class LinkedList
|
15
|
+
VERSION = '1.0.0'
|
16
|
+
include Enumerable
|
17
|
+
|
18
|
+
attr_reader :cdr, :size
|
19
|
+
alias_method :length, :size
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
clear
|
23
|
+
end
|
24
|
+
|
25
|
+
# Two lists are equal if they have the same values in the same positions.
|
26
|
+
def ==(other)
|
27
|
+
cdr == other.cdr
|
28
|
+
end
|
29
|
+
|
30
|
+
# A list is empty if it doesn't have any nodes.
|
31
|
+
def empty?
|
32
|
+
cdr.nil?
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns a duplicate of this list, where the nodes are also copied.
|
36
|
+
# FIXME: Performance hog... The implementation sucks big time.
|
37
|
+
def dup
|
38
|
+
reverse.reverse
|
39
|
+
end
|
40
|
+
|
41
|
+
# Yields each value
|
42
|
+
def each
|
43
|
+
each_node do |node|
|
44
|
+
yield node.value
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Yields each LinkedList::Node (or subclass)
|
49
|
+
def each_node
|
50
|
+
node = cdr
|
51
|
+
while node
|
52
|
+
yield node
|
53
|
+
node = node.cdr
|
54
|
+
end
|
55
|
+
end
|
56
|
+
protected :each_node
|
57
|
+
|
58
|
+
# Returns an Array that has the same values as this LinkedList
|
59
|
+
def to_a
|
60
|
+
inject(Array.new) do |memo, value|
|
61
|
+
memo << value
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def at(index)
|
66
|
+
index = normalize_index(index)
|
67
|
+
each_with_index do |value, idx|
|
68
|
+
return value if index == idx
|
69
|
+
end
|
70
|
+
nil
|
71
|
+
end
|
72
|
+
|
73
|
+
def normalize_index(index)
|
74
|
+
index < 0 ? (length + index) : index
|
75
|
+
end
|
76
|
+
protected :normalize_index
|
77
|
+
|
78
|
+
def [](*args)
|
79
|
+
case args.length
|
80
|
+
when 1
|
81
|
+
case args.first
|
82
|
+
when Range
|
83
|
+
indexes = args.first
|
84
|
+
start, stop = normalize_index(indexes.first), normalize_index(indexes.exclude_end? ? indexes.last - 1 : indexes.last)
|
85
|
+
indexes = (start .. stop)
|
86
|
+
result = []
|
87
|
+
each_with_index do |value, idx|
|
88
|
+
next unless indexes.include?(idx)
|
89
|
+
result << value
|
90
|
+
end
|
91
|
+
result
|
92
|
+
else
|
93
|
+
at(args.first)
|
94
|
+
end
|
95
|
+
when 2
|
96
|
+
index = normalize_index(args.first)
|
97
|
+
return nil unless (0 .. length).include?(index)
|
98
|
+
count = args.last
|
99
|
+
self[index ... (index + count)]
|
100
|
+
else
|
101
|
+
raise ArgumentError, "Expected (index), (index, length) or (index0..index1), received #{args.inspect}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Returns a new instance of self with a reverse insertion order
|
106
|
+
def reverse
|
107
|
+
list = new_species
|
108
|
+
each do |value|
|
109
|
+
list << value
|
110
|
+
end
|
111
|
+
list
|
112
|
+
end
|
113
|
+
|
114
|
+
# Destructively maps this list's values to the return value of the block
|
115
|
+
def map!
|
116
|
+
each_node do |node|
|
117
|
+
node.value = yield(node.value)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Returns a new instance of self where the values have been replaced with the results of the block's
|
122
|
+
def map(&block)
|
123
|
+
list = dup
|
124
|
+
list.map!(&block)
|
125
|
+
list
|
126
|
+
end
|
127
|
+
|
128
|
+
# Pushes a new value at the head of this list
|
129
|
+
def <<(value)
|
130
|
+
@cdr = new_node(value, cdr)
|
131
|
+
@lcdr = @cdr if @lcdr.nil?
|
132
|
+
@size += 1
|
133
|
+
self
|
134
|
+
end
|
135
|
+
alias_method :push, :<<
|
136
|
+
alias_method :unshift, :<<
|
137
|
+
|
138
|
+
# Pops the head of the list, returning the value
|
139
|
+
def pop
|
140
|
+
return nil if empty?
|
141
|
+
@size -= 1
|
142
|
+
value = cdr.value
|
143
|
+
@cdr = cdr.cdr
|
144
|
+
@lcdr = nil if empty?
|
145
|
+
value
|
146
|
+
end
|
147
|
+
alias_method :shift, :pop
|
148
|
+
|
149
|
+
# Initializes this list to the empty state
|
150
|
+
def clear
|
151
|
+
@cdr, @lcdr, @size = nil, nil, 0
|
152
|
+
end
|
153
|
+
|
154
|
+
# Returns the first value of this list
|
155
|
+
def first
|
156
|
+
cdr.nil? ? nil : cdr.value
|
157
|
+
end
|
158
|
+
|
159
|
+
# Returns the last value of this list
|
160
|
+
def last
|
161
|
+
return nil if empty?
|
162
|
+
@lcdr.value
|
163
|
+
end
|
164
|
+
|
165
|
+
def concat(other)
|
166
|
+
result = new_species
|
167
|
+
other.reverse.each do |value|
|
168
|
+
result << value
|
169
|
+
end
|
170
|
+
self.reverse.each do |value|
|
171
|
+
result << value
|
172
|
+
end
|
173
|
+
result
|
174
|
+
end
|
175
|
+
alias_method :+, :concat
|
176
|
+
|
177
|
+
# Returns a new instance of the same class as self.
|
178
|
+
def new_species
|
179
|
+
self.class.new
|
180
|
+
end
|
181
|
+
protected :new_species
|
182
|
+
|
183
|
+
# Returns a new node instance.
|
184
|
+
def new_node(value, cdr)
|
185
|
+
node_class.new(value, cdr)
|
186
|
+
end
|
187
|
+
protected :new_node
|
188
|
+
|
189
|
+
# Returns the class of node to use. Defaults to LinkedList::Node.
|
190
|
+
def node_class
|
191
|
+
LinkedList::Node
|
192
|
+
end
|
193
|
+
protected :node_class
|
194
|
+
|
195
|
+
# Returns a nice looking view of this list.
|
196
|
+
# list = LinkedList.new
|
197
|
+
# list.push "a"
|
198
|
+
# list.push "b"
|
199
|
+
# list.inspect
|
200
|
+
# #=> ("b" ("a" nil))
|
201
|
+
def inspect
|
202
|
+
cdr.inspect
|
203
|
+
end
|
204
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class LinkedList
|
2
|
+
# Implements a single node of the linked list.
|
3
|
+
class Node
|
4
|
+
attr_accessor :cdr, :value
|
5
|
+
|
6
|
+
def initialize(value, cdr)
|
7
|
+
@cdr, @value = cdr, value
|
8
|
+
end
|
9
|
+
|
10
|
+
# Two nodes are equal if their values are equal and their cdr's are equal too.
|
11
|
+
def ==(other)
|
12
|
+
cdr == other.cdr && value == other.value
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns a nice-looking string.
|
16
|
+
# node = LinkedList::Node.new("a", nil)
|
17
|
+
# node.inspect
|
18
|
+
# #=> ("a" nil)
|
19
|
+
def inspect
|
20
|
+
"(#{value.inspect} #{cdr.inspect})"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/script/console
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# File: script/console
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
+
|
5
|
+
libs = " -r irb/completion"
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
+
libs << " -r #{File.dirname(__FILE__) + '/../lib/linked_list.rb'}"
|
9
|
+
puts "Loading linked_list gem"
|
10
|
+
exec "#{irb} #{libs} --simple-prompt"
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
@@ -0,0 +1,74 @@
|
|
1
|
+
namespace :test do
|
2
|
+
task :benchmark => %w(test) do
|
3
|
+
require "benchmark"
|
4
|
+
include Benchmark
|
5
|
+
$:.unshift File.dirname(__FILE__) + "/../lib"
|
6
|
+
require "linked_list"
|
7
|
+
|
8
|
+
puts "Building a list of 1 million random strings"
|
9
|
+
list = nil
|
10
|
+
time = realtime do
|
11
|
+
list = list_of_elements(1_000_000)
|
12
|
+
end
|
13
|
+
puts "Took %.3f seconds" % time
|
14
|
+
puts "Converting to an array"
|
15
|
+
array = nil
|
16
|
+
time = realtime do
|
17
|
+
array = list.to_a
|
18
|
+
end
|
19
|
+
puts "Took %.3f seconds" % time
|
20
|
+
|
21
|
+
bm(16) do |x|
|
22
|
+
x.report("list#push/pop") do
|
23
|
+
20_000.times do
|
24
|
+
list.push "a"
|
25
|
+
end
|
26
|
+
20_000.times do
|
27
|
+
list.pop
|
28
|
+
end
|
29
|
+
end
|
30
|
+
x.report("array#push/pop") do
|
31
|
+
20_000.times do
|
32
|
+
array.push "a"
|
33
|
+
end
|
34
|
+
20_000.times do
|
35
|
+
array.pop
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
bm(16) do |x|
|
41
|
+
x.report("list#last") { 20.times { list.last } }
|
42
|
+
x.report("array#last") { 20.times { array.last } }
|
43
|
+
end
|
44
|
+
|
45
|
+
bm(16) do |x|
|
46
|
+
x.report("list#first") { 2_000.times { list.first } }
|
47
|
+
x.report("array#first") { 2_000.times { array.first } }
|
48
|
+
end
|
49
|
+
|
50
|
+
bm(16) do |x|
|
51
|
+
x.report("list#reverse") { list.reverse }
|
52
|
+
x.report("array#reverse") { array.reverse }
|
53
|
+
end
|
54
|
+
|
55
|
+
bm(16) do |x|
|
56
|
+
x.report("list#dup") { list.dup }
|
57
|
+
x.report("array#dup") { array.dup }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def list_of_elements(count)
|
63
|
+
list = LinkedList.new
|
64
|
+
count.times do
|
65
|
+
list << nil
|
66
|
+
end
|
67
|
+
list
|
68
|
+
end
|
69
|
+
|
70
|
+
CHARS = ('a' .. 'z').to_a + ('A' .. 'Z').to_a + ('0' .. '9').to_a
|
71
|
+
LENGTH = CHARS.length
|
72
|
+
def random_string
|
73
|
+
(0..10).inject("") {|str, _| str << CHARS[rand(LENGTH)]}
|
74
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,516 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
class TestLinkedList < Test::Unit::TestCase
|
4
|
+
context "An empty LinkedList" do
|
5
|
+
setup do
|
6
|
+
@list = LinkedList.new
|
7
|
+
end
|
8
|
+
|
9
|
+
context "#push'ing a value of nil" do
|
10
|
+
setup do
|
11
|
+
@list.push nil
|
12
|
+
end
|
13
|
+
|
14
|
+
should "have a length of 1" do
|
15
|
+
assert_equal 1, @list.length
|
16
|
+
end
|
17
|
+
|
18
|
+
should "be equal to [nil]" do
|
19
|
+
assert_equal [nil], @list.to_a
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "#push'ing an element" do
|
24
|
+
setup do
|
25
|
+
@list.push "a"
|
26
|
+
end
|
27
|
+
|
28
|
+
should "be equal to ['a']" do
|
29
|
+
assert_equal %w(a), @list.to_a
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "calling #reverse" do
|
34
|
+
setup do
|
35
|
+
@other = @list.reverse
|
36
|
+
end
|
37
|
+
|
38
|
+
should "return another list instance" do
|
39
|
+
assert_not_same @list, @other
|
40
|
+
end
|
41
|
+
|
42
|
+
should "return an empty list" do
|
43
|
+
assert @other.empty?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "calling #to_a" do
|
48
|
+
setup do
|
49
|
+
@other = @list.to_a
|
50
|
+
end
|
51
|
+
|
52
|
+
should "be an Array" do
|
53
|
+
assert_kind_of Array, @other
|
54
|
+
end
|
55
|
+
|
56
|
+
should "be empty" do
|
57
|
+
assert @other.empty?
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
should "return nil on #at(0)" do
|
62
|
+
assert_nil @list.at(0)
|
63
|
+
end
|
64
|
+
|
65
|
+
should "return nil on #at(-1)" do
|
66
|
+
assert_nil @list.at(-1)
|
67
|
+
end
|
68
|
+
|
69
|
+
should "return nil on #[](1)" do
|
70
|
+
assert_nil @list[1]
|
71
|
+
end
|
72
|
+
|
73
|
+
should "return nil on #[](-1)" do
|
74
|
+
assert_nil @list[-1]
|
75
|
+
end
|
76
|
+
|
77
|
+
should "return nil on #[](0, 1)" do
|
78
|
+
assert_equal [], @list[0, 1]
|
79
|
+
end
|
80
|
+
|
81
|
+
should "return nil on #[](0..1)" do
|
82
|
+
assert_equal [], @list[0..1]
|
83
|
+
end
|
84
|
+
|
85
|
+
should "return nil on #[](0)" do
|
86
|
+
assert_nil @list[0]
|
87
|
+
end
|
88
|
+
|
89
|
+
should "return a new instance when calling #dup" do
|
90
|
+
assert_not_same @list, @list.dup
|
91
|
+
end
|
92
|
+
|
93
|
+
should "return nil for #first" do
|
94
|
+
assert_nil @list.first
|
95
|
+
end
|
96
|
+
|
97
|
+
should "return nil for #last" do
|
98
|
+
assert_nil @list.last
|
99
|
+
end
|
100
|
+
|
101
|
+
should "be empty" do
|
102
|
+
assert @list.empty?
|
103
|
+
end
|
104
|
+
|
105
|
+
should "have a length of 0" do
|
106
|
+
assert_equal 0, @list.length
|
107
|
+
end
|
108
|
+
|
109
|
+
should "have a size of 0" do
|
110
|
+
assert_equal 0, @list.size
|
111
|
+
end
|
112
|
+
|
113
|
+
should "never call the #each block" do
|
114
|
+
yielded = 0
|
115
|
+
@list.each do |value|
|
116
|
+
yielded += 1
|
117
|
+
end
|
118
|
+
assert yielded.zero?
|
119
|
+
end
|
120
|
+
|
121
|
+
context "calling #concat(['a'])" do
|
122
|
+
setup do
|
123
|
+
@other = @list.concat(%w(a))
|
124
|
+
end
|
125
|
+
|
126
|
+
should "return a new list" do
|
127
|
+
assert_not_same @other, @list
|
128
|
+
end
|
129
|
+
|
130
|
+
should "return a list with one more element" do
|
131
|
+
assert_equal @list.length + 1, @other.length
|
132
|
+
end
|
133
|
+
|
134
|
+
should "return a list equal to ('a' nil)" do
|
135
|
+
assert_equal LinkedList.new << "a", @other
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context "A linked list with one element" do
|
141
|
+
setup do
|
142
|
+
@list = LinkedList.new
|
143
|
+
@list << "a"
|
144
|
+
end
|
145
|
+
|
146
|
+
context "calling #pop" do
|
147
|
+
setup do
|
148
|
+
@element = @list.pop
|
149
|
+
end
|
150
|
+
|
151
|
+
should "be empty?" do
|
152
|
+
assert @list.empty?
|
153
|
+
end
|
154
|
+
|
155
|
+
should "return the element" do
|
156
|
+
assert_equal "a", @element
|
157
|
+
end
|
158
|
+
|
159
|
+
should "have a size of 0" do
|
160
|
+
assert @list.size.zero?
|
161
|
+
end
|
162
|
+
|
163
|
+
should "return nil from #last" do
|
164
|
+
assert_nil @list.last
|
165
|
+
end
|
166
|
+
|
167
|
+
should "return nil from #first" do
|
168
|
+
assert_nil @list.first
|
169
|
+
end
|
170
|
+
|
171
|
+
context "pushing 'b'" do
|
172
|
+
setup do
|
173
|
+
@list.push "b"
|
174
|
+
end
|
175
|
+
|
176
|
+
should "NOT be empty" do
|
177
|
+
assert !@list.empty?
|
178
|
+
end
|
179
|
+
|
180
|
+
should "be ['b']" do
|
181
|
+
assert_equal %w(b), @list.to_a
|
182
|
+
end
|
183
|
+
|
184
|
+
should "return 'b' from #last" do
|
185
|
+
assert_equal "b", @list.last
|
186
|
+
end
|
187
|
+
|
188
|
+
should "return 'b' from #first" do
|
189
|
+
assert_equal "b", @list.first
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
context "calling #to_a" do
|
195
|
+
setup do
|
196
|
+
@other = @list.to_a
|
197
|
+
end
|
198
|
+
|
199
|
+
should "be equal to ['a']" do
|
200
|
+
assert_equal %w(a), @other
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
context "calling #reverse" do
|
205
|
+
setup do
|
206
|
+
@other = @list.reverse
|
207
|
+
end
|
208
|
+
|
209
|
+
should "be equal to the initial list" do
|
210
|
+
assert_equal @list, @other
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
context "calling #dup" do
|
215
|
+
setup do
|
216
|
+
@other = @list.dup
|
217
|
+
end
|
218
|
+
|
219
|
+
should "be a new instance" do
|
220
|
+
assert_not_same @list, @other
|
221
|
+
end
|
222
|
+
|
223
|
+
should "have duplicated the nodes" do
|
224
|
+
@list.map! {|value| value*2}
|
225
|
+
assert_not_equal @list, @other
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
should "return 'a' for #at(-1)" do
|
230
|
+
assert_equal "a", @list.at(-1)
|
231
|
+
end
|
232
|
+
|
233
|
+
should "return 'a' for #at(0)" do
|
234
|
+
assert_equal "a", @list.at(0)
|
235
|
+
end
|
236
|
+
|
237
|
+
should "return nil for #at(2)" do
|
238
|
+
assert_nil @list.at(2)
|
239
|
+
end
|
240
|
+
|
241
|
+
should "return nil for #at(-2)" do
|
242
|
+
assert_nil @list.at(-2)
|
243
|
+
end
|
244
|
+
|
245
|
+
should "return nil for #at(1)" do
|
246
|
+
assert_nil @list.at(1)
|
247
|
+
end
|
248
|
+
|
249
|
+
should "return 'a' for #first" do
|
250
|
+
assert_equal "a", @list.first
|
251
|
+
end
|
252
|
+
|
253
|
+
should "return 'a' for #last" do
|
254
|
+
assert_equal "a", @list.last
|
255
|
+
end
|
256
|
+
|
257
|
+
should "NOT be empty?" do
|
258
|
+
assert !@list.empty?
|
259
|
+
end
|
260
|
+
|
261
|
+
should "have a size of 1" do
|
262
|
+
assert_equal 1, @list.size
|
263
|
+
end
|
264
|
+
|
265
|
+
should "yield the single element to the #each block" do
|
266
|
+
@list.each do |value|
|
267
|
+
assert_equal "a", value
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
should "yield once calling #each" do
|
272
|
+
yielded = 0
|
273
|
+
@list.each do |value|
|
274
|
+
yielded += 1
|
275
|
+
end
|
276
|
+
assert_equal 1, yielded
|
277
|
+
end
|
278
|
+
|
279
|
+
should "yield the value and the index when calling #each_with_index" do
|
280
|
+
yields = []
|
281
|
+
@list.each_with_index do |value, index|
|
282
|
+
yields << [value, index]
|
283
|
+
end
|
284
|
+
assert_equal [["a", 0]], yields
|
285
|
+
end
|
286
|
+
|
287
|
+
should "include?('a')" do
|
288
|
+
assert @list.include?("a")
|
289
|
+
end
|
290
|
+
|
291
|
+
should "NOT include?('b')" do
|
292
|
+
assert !@list.include?("b")
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
context "A list with 2 elements" do
|
297
|
+
setup do
|
298
|
+
@list = LinkedList.new
|
299
|
+
@list << "a"
|
300
|
+
@list << "b"
|
301
|
+
end
|
302
|
+
|
303
|
+
context "calling #to_a" do
|
304
|
+
setup do
|
305
|
+
@other = @list.to_a
|
306
|
+
end
|
307
|
+
|
308
|
+
should "be equal to ['b', 'a']" do
|
309
|
+
assert_equal %w(b a), @other
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
context "calling #reverse" do
|
314
|
+
setup do
|
315
|
+
@other = @list.reverse
|
316
|
+
end
|
317
|
+
|
318
|
+
should "return the elements in insertion order" do
|
319
|
+
assert_equal %w(a b), @other.to_a
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
context "cleared" do
|
324
|
+
setup do
|
325
|
+
@list.clear
|
326
|
+
end
|
327
|
+
|
328
|
+
should "be empty?" do
|
329
|
+
assert @list.empty?
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
context "#map" do
|
334
|
+
setup do
|
335
|
+
@other = @list.map {|f| f*2}
|
336
|
+
end
|
337
|
+
|
338
|
+
should "return a list of 2 elements" do
|
339
|
+
assert_equal 2, @other.length
|
340
|
+
end
|
341
|
+
|
342
|
+
should "include?('aa')" do
|
343
|
+
assert @other.include?("aa")
|
344
|
+
end
|
345
|
+
|
346
|
+
should "include?('bb')" do
|
347
|
+
assert @other.include?("bb")
|
348
|
+
end
|
349
|
+
|
350
|
+
should "NOT have reversed the list" do
|
351
|
+
assert_equal %w(bb aa), @other.to_a
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
context "calling #pop" do
|
356
|
+
setup do
|
357
|
+
@element = @list.pop
|
358
|
+
end
|
359
|
+
|
360
|
+
should "NOT be empty?" do
|
361
|
+
assert !@list.empty?
|
362
|
+
end
|
363
|
+
|
364
|
+
should "return the moast recently pushed element" do
|
365
|
+
assert_equal "b", @element
|
366
|
+
end
|
367
|
+
|
368
|
+
should "have a size of 1" do
|
369
|
+
assert_equal 1, @list.size
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
should "return 'b' for #at(0)" do
|
374
|
+
assert_equal "b", @list.at(0)
|
375
|
+
end
|
376
|
+
|
377
|
+
should "return 'a' for #at(1)" do
|
378
|
+
assert_equal "a", @list.at(1)
|
379
|
+
end
|
380
|
+
|
381
|
+
should "return 'a' for #at(-1)" do
|
382
|
+
assert_equal "a", @list.at(-1)
|
383
|
+
end
|
384
|
+
|
385
|
+
should "return 'b' for #at(-2)" do
|
386
|
+
assert_equal "b", @list.at(-2)
|
387
|
+
end
|
388
|
+
|
389
|
+
should "return 'b' for #first" do
|
390
|
+
assert_equal "b", @list.first
|
391
|
+
end
|
392
|
+
|
393
|
+
should "return 'a' for #last" do
|
394
|
+
assert_equal "a", @list.last
|
395
|
+
end
|
396
|
+
|
397
|
+
should "include?('b')" do
|
398
|
+
assert @list.include?("b")
|
399
|
+
end
|
400
|
+
|
401
|
+
should "have a length of 2" do
|
402
|
+
assert_equal 2, @list.length
|
403
|
+
end
|
404
|
+
|
405
|
+
should "yield the value and the index when calling #each_with_index" do
|
406
|
+
yields = []
|
407
|
+
@list.each_with_index do |value, index|
|
408
|
+
yields << [value, index]
|
409
|
+
end
|
410
|
+
assert_equal [["b", 0], ["a", 1]], yields
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
context "A list with 5 elements" do
|
415
|
+
setup do
|
416
|
+
@list = LinkedList.new
|
417
|
+
@list << "a" << "b" << "c" << "d" << "e"
|
418
|
+
end
|
419
|
+
|
420
|
+
should "have a length of 5" do
|
421
|
+
assert_equal 5, @list.length
|
422
|
+
end
|
423
|
+
|
424
|
+
should "return 'a' for #at(-1)" do
|
425
|
+
assert_equal "a", @list.at(-1)
|
426
|
+
end
|
427
|
+
|
428
|
+
should "return 'e' for #at(-5)" do
|
429
|
+
assert_equal "e", @list.at(-5)
|
430
|
+
end
|
431
|
+
|
432
|
+
should "return ['e', 'd', 'c', 'b', 'a'] for #[](0..10)" do
|
433
|
+
assert_equal %w(e d c b a), @list[0..10]
|
434
|
+
end
|
435
|
+
|
436
|
+
should "return ['e', 'd'] for #[](0..1)" do
|
437
|
+
assert_equal %w(e d), @list[0..1]
|
438
|
+
end
|
439
|
+
|
440
|
+
should "return ['e'] for #[](0..0)" do
|
441
|
+
assert_equal %w(e), @list[0..0]
|
442
|
+
end
|
443
|
+
|
444
|
+
should "return ['e', 'd', 'c'] for #[](0..2)" do
|
445
|
+
assert_equal %w(e d c), @list[0..2]
|
446
|
+
end
|
447
|
+
|
448
|
+
should "return ['e', 'd', 'c', 'b', 'a'] for #[](0, 10)" do
|
449
|
+
assert_equal %w(e d c b a), @list[0, 10]
|
450
|
+
end
|
451
|
+
|
452
|
+
should "return ['e'] for #[](0, 1)" do
|
453
|
+
assert_equal %w(e), @list[0, 1]
|
454
|
+
end
|
455
|
+
|
456
|
+
should "return ['e', 'd'] for #[](0, 2)" do
|
457
|
+
assert_equal %w(e d), @list[0, 2]
|
458
|
+
end
|
459
|
+
|
460
|
+
should "return nil for #[](-240)" do
|
461
|
+
assert_nil @list[-240]
|
462
|
+
end
|
463
|
+
|
464
|
+
should "return nil for #[](240)" do
|
465
|
+
assert_nil @list[240]
|
466
|
+
end
|
467
|
+
|
468
|
+
should "return nil for #[](-240, 100)" do
|
469
|
+
assert_nil @list[-240, 100]
|
470
|
+
end
|
471
|
+
|
472
|
+
should "return nil for #[](240, 100)" do
|
473
|
+
assert_nil @list[240, 100]
|
474
|
+
end
|
475
|
+
|
476
|
+
should "return ['b', 'a'] for #[](-2..-1)" do
|
477
|
+
assert_equal %w(b a), @list[-2..-1]
|
478
|
+
end
|
479
|
+
|
480
|
+
should "return ['c', 'b'] for #[](-3, 2)" do
|
481
|
+
assert_equal %w(c b), @list[-3, 2]
|
482
|
+
end
|
483
|
+
|
484
|
+
context "calling #concat(['x', 'y', 'z'])" do
|
485
|
+
setup do
|
486
|
+
@other = @list.concat(%w(x y z))
|
487
|
+
end
|
488
|
+
|
489
|
+
should "return a new list" do
|
490
|
+
assert_not_same @list, @other
|
491
|
+
end
|
492
|
+
|
493
|
+
should "return a list of 8 elements" do
|
494
|
+
assert_equal 8, @other.length
|
495
|
+
end
|
496
|
+
|
497
|
+
should "return a LinkedList" do
|
498
|
+
assert_kind_of LinkedList, @other
|
499
|
+
end
|
500
|
+
|
501
|
+
should "return ['e', 'd', 'c', 'b', 'a', 'x', 'y', 'z']" do
|
502
|
+
assert_equal %w(e d c b a x y z), @other.to_a
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
context "calling #+(('z' nil))" do
|
507
|
+
setup do
|
508
|
+
@other = @list + (LinkedList.new << "z")
|
509
|
+
end
|
510
|
+
|
511
|
+
should "return ['e', 'd', 'c', 'b', 'a', 'z']" do
|
512
|
+
assert_equal %w(e d c b a z), @other.to_a
|
513
|
+
end
|
514
|
+
end
|
515
|
+
end
|
516
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
class TestLinkedListNode < Test::Unit::TestCase
|
4
|
+
context "A node with the empty string as a value at the end of the chain" do
|
5
|
+
setup do
|
6
|
+
@node = LinkedList::Node.new("", nil)
|
7
|
+
end
|
8
|
+
|
9
|
+
should "be equal to itself" do
|
10
|
+
assert_equal @node, @node
|
11
|
+
end
|
12
|
+
|
13
|
+
should "be equal to another node that is empty" do
|
14
|
+
assert_equal @node, LinkedList::Node.new("", nil)
|
15
|
+
end
|
16
|
+
|
17
|
+
should "NOT be equal to another node that has a different value" do
|
18
|
+
assert_not_equal @node, LinkedList::Node.new("a", nil)
|
19
|
+
end
|
20
|
+
|
21
|
+
should "NOT be equal to another node that has a different cdr" do
|
22
|
+
assert_not_equal @node, LinkedList::Node.new("", LinkedList::Node.new(nil, nil))
|
23
|
+
end
|
24
|
+
|
25
|
+
should "be inspectable" do
|
26
|
+
assert_equal "(\"\" nil)", @node.inspect
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "A node with a value of 'a' and a successor of 'b'" do
|
31
|
+
setup do
|
32
|
+
@tail = LinkedList::Node.new("b", nil)
|
33
|
+
@head = LinkedList::Node.new("a", @tail)
|
34
|
+
end
|
35
|
+
|
36
|
+
should "be inspectable" do
|
37
|
+
assert_equal "(\"a\" (\"b\" nil))", @head.inspect
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: linked_list
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- "Fran\xC3\xA7ois Beausoleil"
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-11-12 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: newgem
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.0.7
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: francois-shoulda
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.0.5
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: hoe
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.8.0
|
44
|
+
version:
|
45
|
+
description: A simple linked list implementation that follows Ruby's conventions.
|
46
|
+
email:
|
47
|
+
- francois@teksol.info
|
48
|
+
executables: []
|
49
|
+
|
50
|
+
extensions: []
|
51
|
+
|
52
|
+
extra_rdoc_files:
|
53
|
+
- History.txt
|
54
|
+
- Manifest.txt
|
55
|
+
- Benchmarks.txt
|
56
|
+
- README.rdoc
|
57
|
+
files:
|
58
|
+
- History.txt
|
59
|
+
- Manifest.txt
|
60
|
+
- Benchmarks.txt
|
61
|
+
- README.rdoc
|
62
|
+
- Rakefile
|
63
|
+
- lib/linked_list.rb
|
64
|
+
- lib/linked_list/node.rb
|
65
|
+
- script/console
|
66
|
+
- script/destroy
|
67
|
+
- script/generate
|
68
|
+
- tasks/benchmark.rake
|
69
|
+
- test/test_helper.rb
|
70
|
+
- test/test_linked_list.rb
|
71
|
+
- test/test_linked_list_node.rb
|
72
|
+
has_rdoc: true
|
73
|
+
homepage: http://linkedlist.rubyforge.org/
|
74
|
+
post_install_message:
|
75
|
+
rdoc_options:
|
76
|
+
- --main
|
77
|
+
- README.rdoc
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: "0"
|
85
|
+
version:
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: "0"
|
91
|
+
version:
|
92
|
+
requirements: []
|
93
|
+
|
94
|
+
rubyforge_project: linkedlist
|
95
|
+
rubygems_version: 1.2.0
|
96
|
+
signing_key:
|
97
|
+
specification_version: 2
|
98
|
+
summary: A simple linked list implementation that follows Ruby's conventions.
|
99
|
+
test_files:
|
100
|
+
- test/test_helper.rb
|
101
|
+
- test/test_linked_list.rb
|
102
|
+
- test/test_linked_list_node.rb
|