rubylabs 0.8.0 → 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +27 -6
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/data/eliza/doctor.txt +1 -1
- data/data/tsp/ireland.txt +10 -10
- data/lib/bitlab.rb +170 -37
- data/lib/demos.rb +5 -5
- data/lib/rubylabs.rb +27 -2
- data/lib/sievelab.rb +6 -6
- metadata +3 -3
data/README.rdoc
CHANGED
@@ -1,9 +1,30 @@
|
|
1
|
-
|
1
|
+
== Description
|
2
2
|
|
3
|
-
|
4
|
-
<em>
|
5
|
-
|
3
|
+
RubyLabs is a collection of modules used for tutorial exercises in the textbook
|
4
|
+
<em>{Explorations in Computing: An Introduction to Computer Science}[http://www.cs.uoregon.edu]</em>.
|
5
|
+
There is one module for each chapter in the text:
|
6
6
|
|
7
|
-
|
7
|
+
<b>IntroLab[link:classes/RubyLabs/IntroLab.html]</b>:: A quick introduction to Ruby, with an exercise leading to the definition of a method to convert temperatures from Fahrenheit to Celsius.
|
8
|
+
<b>SieveLab[link:classes/RubyLabs/SieveLab.html]</b>:: A project on the Sieve of Eratosthenes, an algorithm for generating lists of prime numbers.
|
9
|
+
<b>IterationLab[link:classes/RubyLabs/IterationLab.html]</b>:: Simple iterative algorithms for searching and sorting.
|
10
|
+
<b>RecursionLab[link:classes/RubyLabs/RecursionLab.html]</b>:: More sophisticated algorithms, using a divide and conquer strategy.
|
11
|
+
<b>HashLab[link:classes/RubyLabs/HashLab.html]</b>:: Using a hash table to represent a word list, e.g. for a crossword puzzle dictionary.
|
12
|
+
<b>BitLab[link:classes/RubyLabs/BitLab.html]</b>:: Projects with binary encodings, including methods for simple error correction with parity bits and text compression with Huffman codes.
|
13
|
+
<b>MARSLab[link:classes/RubyLabs/MARSLab.html]</b>:: Using the game of Core War to explore the von Neumann architecture.
|
14
|
+
<b>RandomLab[link:classes/RubyLabs/RandomLab.html]</b>:: Creating lists of random numbers with a pseudo-random number generator.
|
15
|
+
<b>ElizaLab[link:classes/RubyLabs/ElizaLab.html]</b>:: An introduction to issues in natural language processing, using a version of ELIZA written in Ruby.
|
16
|
+
<b>SphereLab[link:classes/RubyLabs/SphereLab.html]</b>:: Introduction to modeling and simulation, culminating in an N-body simulation of the movement of planets in the Solar System.
|
17
|
+
<b>TSPLab[link:classes/RubyLabs/TSPLab.html]</b>:: Solving the Traveling Salesman Problem with a genetic algorithm.
|
18
|
+
|
19
|
+
The main *RubyLabs* module has some common methods (e.g. min and max) used throughout the book.
|
20
|
+
|
21
|
+
== Installing
|
22
|
+
|
23
|
+
== Examples
|
24
|
+
|
25
|
+
== Documentation
|
26
|
+
|
27
|
+
A lab manual is available from http://ix.cs.uoregon.edu/~conery/eic.
|
28
|
+
|
29
|
+
== Questions
|
8
30
|
|
9
|
-
Copyright (c) 2009 John S. Conery. See LICENSE for details.
|
data/Rakefile
CHANGED
@@ -6,7 +6,7 @@ begin
|
|
6
6
|
Jeweler::Tasks.new do |gem|
|
7
7
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
8
8
|
gem.name = "rubylabs"
|
9
|
-
gem.summary = %Q{Software and data for lab projects
|
9
|
+
gem.summary = %Q{Software and data for lab projects for Explorations in Computing.}
|
10
10
|
gem.description = %Q{A set of modules for interactive experiments in an introductory computer science class.}
|
11
11
|
gem.email = "conery@cs.uoregon.edu"
|
12
12
|
gem.homepage = "http://github.com/conery/rubylabs"
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.8.
|
1
|
+
0.8.1
|
data/data/eliza/doctor.txt
CHANGED
data/data/tsp/ireland.txt
CHANGED
@@ -8,16 +8,16 @@
|
|
8
8
|
|
9
9
|
:matrix driving distances (in kilometers) between cities
|
10
10
|
|
11
|
-
belfast cork
|
12
|
-
belfast dublin
|
13
|
-
belfast galway
|
14
|
-
belfast limerick
|
11
|
+
belfast cork 425
|
12
|
+
belfast dublin 167
|
13
|
+
belfast galway 306
|
14
|
+
belfast limerick 323
|
15
15
|
|
16
|
-
cork dublin
|
17
|
-
cork galway
|
18
|
-
cork limerick
|
16
|
+
cork dublin 257
|
17
|
+
cork galway 209
|
18
|
+
cork limerick 105
|
19
19
|
|
20
|
-
dublin galway
|
21
|
-
dublin limerick
|
20
|
+
dublin galway 219
|
21
|
+
dublin limerick 198
|
22
22
|
|
23
|
-
galway limerick
|
23
|
+
galway limerick 105
|
data/lib/bitlab.rb
CHANGED
@@ -10,7 +10,9 @@ module RubyLabs
|
|
10
10
|
|
11
11
|
module BitLab
|
12
12
|
|
13
|
-
QueueView = Struct.new(:queue)
|
13
|
+
QueueView = Struct.new(:queue, :options)
|
14
|
+
NodeView = Struct.new(:circle, :text, :ftext, :lseg, :rseg)
|
15
|
+
NodeCoords = Struct.new(:x, :y, :leftedge, :leftdepth, :rightedge, :rightdepth)
|
14
16
|
|
15
17
|
=begin rdoc
|
16
18
|
Make a unique binary code for each item in array +a+, returning a Hash that
|
@@ -163,7 +165,7 @@ module BitLab
|
|
163
165
|
while pq.length > 1
|
164
166
|
n1 = pq.shift
|
165
167
|
n2 = pq.shift
|
166
|
-
pq << Node.combine(
|
168
|
+
pq << Node.combine(n1, n2)
|
167
169
|
end
|
168
170
|
|
169
171
|
return pq[0]
|
@@ -286,7 +288,8 @@ module BitLab
|
|
286
288
|
are Nodes for the roots of subtrees.
|
287
289
|
|
288
290
|
Use +new+ to create a new leaf node. Call the class method +combine+ (an alternative
|
289
|
-
constructor) to make a new interior node from two existing nodes.
|
291
|
+
constructor) to make a new interior node from two existing nodes. If the tree is
|
292
|
+
being displayed on the RubyLabs canvas, make a graphic for the new interior node, too.
|
290
293
|
|
291
294
|
The +<+ method allows Nodes to be compared so they can be ordered in a priority
|
292
295
|
queue.
|
@@ -294,18 +297,32 @@ module BitLab
|
|
294
297
|
|
295
298
|
class Node
|
296
299
|
|
297
|
-
attr_accessor :freq, :char, :left, :right
|
298
|
-
|
300
|
+
attr_accessor :freq, :char, :left, :right, :drawing, :coords, :lfchain, :rfchain, :depth
|
301
|
+
|
299
302
|
def initialize(char,freq)
|
300
303
|
@char = char
|
301
304
|
@freq = freq
|
302
305
|
@left = @right = nil
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
306
|
+
@drawing = @coords = nil
|
307
|
+
@lfchain = @rfchain = self
|
308
|
+
@depth = 0
|
309
|
+
end
|
310
|
+
|
311
|
+
# todo -- need to follow chains to end when updating shallower tree
|
312
|
+
|
313
|
+
def Node.combine(leftdesc, rightdesc)
|
314
|
+
node = Node.new(nil, leftdesc.freq + rightdesc.freq)
|
315
|
+
node.left = leftdesc
|
316
|
+
node.right = rightdesc
|
317
|
+
node.depth = 1 + max(leftdesc.depth, rightdesc.depth)
|
318
|
+
node.lfchain = leftdesc
|
319
|
+
node.rfchain = rightdesc
|
320
|
+
if leftdesc.depth > rightdesc.depth
|
321
|
+
rightdesc.rfchain = leftdesc.rfchain
|
322
|
+
elsif leftdesc.depth < rightdesc.depth
|
323
|
+
leftdesc.lfchain = rightdesc.lfchain
|
324
|
+
end
|
325
|
+
draw_root(node, leftdesc, rightdesc) if (leftdesc.drawing && rightdesc.drawing)
|
309
326
|
return node
|
310
327
|
end
|
311
328
|
|
@@ -559,28 +576,74 @@ module BitLab
|
|
559
576
|
|
560
577
|
end # Message
|
561
578
|
|
579
|
+
def move_tree(tree, dx, dy)
|
580
|
+
tree.coords.x += dx
|
581
|
+
tree.coords.y += dy
|
582
|
+
Canvas.move(tree.drawing.circle, dx, dy)
|
583
|
+
Canvas.move(tree.drawing.text, dx, dy)
|
584
|
+
Canvas.move(tree.drawing.ftext, dx, dy) if tree.drawing.ftext
|
585
|
+
if tree.left
|
586
|
+
Canvas.move(tree.drawing.lseg, dx, dy)
|
587
|
+
move_tree(tree.left, dx, dy)
|
588
|
+
end
|
589
|
+
if tree.right
|
590
|
+
Canvas.move(tree.drawing.rseg, dx, dy)
|
591
|
+
move_tree(tree.right, dx, dy)
|
592
|
+
end
|
593
|
+
Canvas.sync
|
594
|
+
end
|
595
|
+
|
596
|
+
def draw_root(node, left, right)
|
597
|
+
opts = @@drawing.options
|
598
|
+
x = (left.coords.x + right.coords.x) / 2
|
599
|
+
y = left.coords.y - 2 * @@unit
|
600
|
+
draw_node(node, x, y)
|
601
|
+
node.drawing.lseg = Canvas.line(x, y, left.coords.x, left.coords.y).lower
|
602
|
+
node.drawing.rseg = Canvas.line(x, y, right.coords.x, right.coords.y).lower
|
603
|
+
# [left, right].each do |desc|
|
604
|
+
# desc.drawing.ftext.delete
|
605
|
+
# desc.drawing.ftext = nil
|
606
|
+
# end
|
607
|
+
end
|
608
|
+
|
609
|
+
def draw_node(node, x, y)
|
610
|
+
return nil unless @@drawing
|
611
|
+
opts = @@drawing.options
|
612
|
+
d = @@unit
|
613
|
+
circ = Canvas.circle( x, y, d / 2, :fill => opts[:nodefill] )
|
614
|
+
text = Canvas.text( node.char, x, y, :anchor => :center )
|
615
|
+
ftext = Canvas.text( node.freq.to_s, x, y-d, {:font => opts[:freqfont], :anchor => :center} )
|
616
|
+
node.drawing = NodeView.new(circ, text, ftext, nil, nil)
|
617
|
+
node.coords = NodeCoords.new(x, y, x, 0, x, 0)
|
618
|
+
end
|
619
|
+
|
562
620
|
=begin rdoc
|
563
621
|
Initialize the canvas with a drawing of a priority queue.
|
564
622
|
=end
|
565
623
|
|
566
|
-
=begin
|
567
|
-
TODO fill in this stub...
|
568
|
-
=end
|
569
|
-
|
570
624
|
def view_queue(pq, userOptions = {} )
|
571
|
-
options = @@
|
572
|
-
Canvas.init(
|
573
|
-
@@drawing = QueueView.new(pq)
|
625
|
+
options = @@queueViewOptions.merge(userOptions)
|
626
|
+
Canvas.init(options[:width], options[:height], "BitLab")
|
627
|
+
@@drawing = QueueView.new(pq, options)
|
628
|
+
options[:nodefill] = "lightgray"
|
629
|
+
options[:freqfont] = Canvas.font(:family => 'Helvetica', :size => 10)
|
574
630
|
pq.on_canvas = true
|
631
|
+
x = options[:qx]
|
632
|
+
pq.each_with_index do |node, i|
|
633
|
+
draw_node(node, x, options[:qy])
|
634
|
+
x += 3 * @@unit
|
635
|
+
end
|
575
636
|
Canvas.sync
|
576
637
|
return true
|
577
638
|
end
|
578
639
|
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
640
|
+
@@unit = 24 # pixels per "tree unit"
|
641
|
+
|
642
|
+
@@queueViewOptions = {
|
643
|
+
:width => 42 * @@unit,
|
644
|
+
:height => 15 * @@unit,
|
645
|
+
:qy => 50,
|
646
|
+
:qx => 50,
|
584
647
|
}
|
585
648
|
|
586
649
|
@@bitsDirectory = File.join(File.dirname(__FILE__), '..', 'data', 'huffman')
|
@@ -590,27 +653,83 @@ end # BitLab
|
|
590
653
|
end # RubyLabs
|
591
654
|
|
592
655
|
=begin rdoc
|
593
|
-
Add
|
594
|
-
|
656
|
+
Add an update method to the PriorityQueue so nodes are moved around on the screen when
|
657
|
+
they are removed from or added to the queue (if the queue is on the canvas).
|
658
|
+
Note: the << and shift methods call this method when @on_canvas is true....
|
595
659
|
=end
|
596
660
|
|
661
|
+
=begin
|
662
|
+
TODO make time to sleep a parameter
|
663
|
+
todo add comments, rdoc
|
664
|
+
todo distance in units (down, up, spacing) should also be params
|
665
|
+
todo clean up def of Node -- coords not being used, combine with graphics
|
666
|
+
=end
|
597
667
|
class PriorityQueue
|
598
668
|
|
599
669
|
attr_accessor :on_canvas
|
600
670
|
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
671
|
+
def update(op, node)
|
672
|
+
if op == :shift
|
673
|
+
move_tree(node, 0, 4 * @@unit) # move subtree rooted at node down 4 units
|
674
|
+
else
|
675
|
+
i = 0
|
676
|
+
dx = @@drawing.options[:qx] - left_edge(@q[0])
|
677
|
+
while @q[i] != node
|
678
|
+
move_tree(@q[i], dx, 0)
|
679
|
+
dx = 3 * @@unit - tree_sep(@q[i], node)
|
680
|
+
move_tree(node, dx, 0)
|
681
|
+
dx = 3 * @@unit - tree_sep(@q[i], @q[i+1])
|
682
|
+
i += 1
|
683
|
+
sleep(0.2)
|
684
|
+
end
|
685
|
+
move_tree(node, 0, -2 * @@unit)
|
686
|
+
if i < @q.length - 1
|
687
|
+
dx = 3 * @@unit - tree_sep(@q[i], @q[i+1])
|
688
|
+
i += 1
|
689
|
+
while i < @q.length
|
690
|
+
sleep(0.2)
|
691
|
+
move_tree(@q[i], dx, 0)
|
692
|
+
i += 1
|
693
|
+
end
|
694
|
+
end
|
695
|
+
end
|
607
696
|
end
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
697
|
+
|
698
|
+
def left_edge(tree)
|
699
|
+
x = tree.coords.x
|
700
|
+
while tree.lfchain != tree
|
701
|
+
tree = tree.lfchain
|
702
|
+
x = min(x, tree.coords.x)
|
703
|
+
end
|
704
|
+
return x
|
705
|
+
end
|
706
|
+
|
707
|
+
def right_edge(tree)
|
708
|
+
x = tree.coords.x
|
709
|
+
while tree.rfchain != tree
|
710
|
+
tree = tree.rfchain
|
711
|
+
x = max(x, tree.coords.x)
|
712
|
+
end
|
713
|
+
return x
|
714
|
+
end
|
715
|
+
|
716
|
+
def tree_sep(left, right)
|
717
|
+
res = right.coords.x - left.coords.x
|
718
|
+
while (left.rfchain != left && right.lfchain != right)
|
719
|
+
left = left.rfchain
|
720
|
+
right = right.lfchain
|
721
|
+
dist = right.coords.x - left.coords.x
|
722
|
+
res = dist if dist < res
|
723
|
+
end
|
724
|
+
# loop do
|
725
|
+
# puts "res = #{res}"
|
726
|
+
# break if left == left.rfchain || right == right.lfchain
|
727
|
+
# left = left.rfchain
|
728
|
+
# right = right.lfchain
|
729
|
+
# puts "down #{left.inspect} --- #{right.inspect}"
|
730
|
+
# dist = right.coords.x - left.coords.x
|
731
|
+
# res = dist if dist < res
|
732
|
+
# end
|
614
733
|
return res
|
615
734
|
end
|
616
735
|
|
@@ -649,3 +768,17 @@ class Fixnum
|
|
649
768
|
|
650
769
|
end
|
651
770
|
|
771
|
+
=begin
|
772
|
+
TODO delete this
|
773
|
+
=end
|
774
|
+
|
775
|
+
def test_view
|
776
|
+
vf = read_frequencies(:hafreq)
|
777
|
+
pq = init_queue(vf)
|
778
|
+
view_queue(pq)
|
779
|
+
Canvas.sync
|
780
|
+
return pq
|
781
|
+
end
|
782
|
+
|
783
|
+
|
784
|
+
|
data/lib/demos.rb
CHANGED
@@ -21,19 +21,19 @@ module Demos
|
|
21
21
|
end
|
22
22
|
|
23
23
|
=begin rdoc
|
24
|
-
This version of the Sieve of Eratosthenes iterates until the
|
24
|
+
This version of the Sieve of Eratosthenes iterates until the worksheet is
|
25
25
|
empty. Use it as a baseline for counting the number of iterations, to
|
26
26
|
compare it to the actual version that iterates until finding the first
|
27
27
|
prime greater than sqrt(n)
|
28
28
|
=end
|
29
29
|
|
30
30
|
def sieve(n)
|
31
|
-
|
31
|
+
worksheet = Array(2..n)
|
32
32
|
primes = []
|
33
33
|
|
34
|
-
while
|
35
|
-
primes <<
|
36
|
-
|
34
|
+
while worksheet.length > 0
|
35
|
+
primes << worksheet.first
|
36
|
+
worksheet.delete_if { |x| x % primes.last == 0 }
|
37
37
|
end
|
38
38
|
|
39
39
|
return primes
|
data/lib/rubylabs.rb
CHANGED
@@ -576,6 +576,7 @@ Call +a.random(:success)+ to get a value that is in the array +a+, or call
|
|
576
576
|
if RUBY_VERSION =~ %r{^1\.8} && RUBY_PLATFORM =~ %r{darwin} && caller[1].index("(irb)") == 0
|
577
577
|
sleep(0.1)
|
578
578
|
end
|
579
|
+
# @@tkroot.update :idletasks
|
579
580
|
end
|
580
581
|
|
581
582
|
=begin rdoc
|
@@ -585,13 +586,17 @@ Call +a.random(:success)+ to get a value that is in the array +a+, or call
|
|
585
586
|
|
586
587
|
def Canvas.text(s, x, y, opts = {})
|
587
588
|
return nil unless @@canvas
|
588
|
-
opts[:anchor] = :nw
|
589
|
+
opts[:anchor] = :nw unless opts.has_key?(:anchor)
|
589
590
|
opts[:text] = s
|
590
591
|
text = TkcText.new( @@canvas, x, y, opts)
|
591
592
|
@@objects << text
|
592
593
|
return text
|
593
594
|
end
|
594
595
|
|
596
|
+
def Canvas.font(options)
|
597
|
+
return TkFont.new(options)
|
598
|
+
end
|
599
|
+
|
595
600
|
=begin rdoc
|
596
601
|
Draw a line from (x0,y0) to (x1,y1)
|
597
602
|
=end
|
@@ -735,6 +740,10 @@ Call +a.random(:success)+ to get a value that is in the array +a+, or call
|
|
735
740
|
assignment via an index or any other array operation.
|
736
741
|
The +<<+ method checks to make sure an object is comparable (responds to <) before
|
737
742
|
adding it to the queue.
|
743
|
+
|
744
|
+
If a program that uses a priority queue adds an instance variable named @on_canvas
|
745
|
+
the shift and << methods will call a method named update so drawings that show
|
746
|
+
a queue can be updated.
|
738
747
|
=end
|
739
748
|
|
740
749
|
class PriorityQueue
|
@@ -751,9 +760,17 @@ Call +a.random(:success)+ to get a value that is in the array +a+, or call
|
|
751
760
|
i += 1
|
752
761
|
end
|
753
762
|
@q.insert(i, obj)
|
763
|
+
update(:insert, obj) if @on_canvas
|
764
|
+
return @q
|
765
|
+
end
|
766
|
+
|
767
|
+
def shift
|
768
|
+
res = @q.shift
|
769
|
+
update(:shift, res) if @on_canvas
|
770
|
+
return res
|
754
771
|
end
|
755
772
|
|
756
|
-
%w{
|
773
|
+
%w{length first last to_s inspect clear empty?}.each do |name|
|
757
774
|
eval "def #{name}() @q.#{name} end"
|
758
775
|
end
|
759
776
|
|
@@ -764,6 +781,14 @@ Call +a.random(:success)+ to get a value that is in the array +a+, or call
|
|
764
781
|
def collect(&f)
|
765
782
|
@q.map { |x| f.call(x) }
|
766
783
|
end
|
784
|
+
|
785
|
+
def each(&b)
|
786
|
+
@q.each &b
|
787
|
+
end
|
788
|
+
|
789
|
+
def each_with_index(&b)
|
790
|
+
@q.each_with_index &b
|
791
|
+
end
|
767
792
|
|
768
793
|
end # PriorityQueue
|
769
794
|
|
data/lib/sievelab.rb
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
Use the Sieve of Eratosthenes algorithm to generate a list of prime
|
7
7
|
numbers. The method is an introduction to iteration, using iterators
|
8
8
|
to make and filter lists of numbers, and a while loop to repeat the
|
9
|
-
filtering step until no more composite numbers are left in the
|
9
|
+
filtering step until no more composite numbers are left in the worksheet.
|
10
10
|
|
11
11
|
=end
|
12
12
|
|
@@ -21,15 +21,15 @@ module SieveLab
|
|
21
21
|
# :begin :sieve
|
22
22
|
def sieve(n)
|
23
23
|
return [] if n < 2
|
24
|
-
|
24
|
+
worksheet = Array(2..n)
|
25
25
|
primes = []
|
26
26
|
|
27
|
-
while
|
28
|
-
primes <<
|
29
|
-
|
27
|
+
while worksheet.first < sqrt(n)
|
28
|
+
primes << worksheet.first
|
29
|
+
worksheet.delete_if { |x| x % primes.last == 0 }
|
30
30
|
end
|
31
31
|
|
32
|
-
return primes +
|
32
|
+
return primes + worksheet
|
33
33
|
end
|
34
34
|
# :end :sieve
|
35
35
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubylabs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- conery
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-
|
12
|
+
date: 2010-08-08 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -114,7 +114,7 @@ rubyforge_project: rubylabs
|
|
114
114
|
rubygems_version: 1.3.5
|
115
115
|
signing_key:
|
116
116
|
specification_version: 3
|
117
|
-
summary: Software and data for lab projects
|
117
|
+
summary: Software and data for lab projects for Explorations in Computing.
|
118
118
|
test_files:
|
119
119
|
- test/bit_test.rb
|
120
120
|
- test/eliza_test.rb
|