rubylabs 0.8.0 → 0.8.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.
data/README.rdoc CHANGED
@@ -1,9 +1,30 @@
1
- = rubylabs
1
+ == Description
2
2
 
3
- The modules in this Gem have methods and data for lab projects in the textbook
4
- <em>The Science of Computing: A Problem Solving Approach</em>. There is one module
5
- for each chapter, plus a set of common methods shared by more than one module.
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
- == Copyright
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 in the Science of Computing text.}
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.0
1
+ 0.8.1
@@ -57,7 +57,7 @@ remember 5
57
57
 
58
58
  if 3
59
59
  /if (.*)/
60
- "Do you think its likely that $1?"
60
+ "Do you think it's likely that $1?"
61
61
  "Do you wish that $1?"
62
62
  "What do you think about $1?"
63
63
  "Really, if $1?"
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 422
12
- belfast dublin 157
13
- belfast galway 331
14
- belfast limerick 364
11
+ belfast cork 425
12
+ belfast dublin 167
13
+ belfast galway 306
14
+ belfast limerick 323
15
15
 
16
- cork dublin 254
17
- cork galway 200
18
- cork limerick 100
16
+ cork dublin 257
17
+ cork galway 209
18
+ cork limerick 105
19
19
 
20
- dublin galway 214
21
- dublin limerick 100
20
+ dublin galway 219
21
+ dublin limerick 198
22
22
 
23
- galway limerick 101
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( n1, n2)
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
- end
304
-
305
- def Node.combine(n1,n2)
306
- node = Node.new(nil, n1.freq + n2.freq)
307
- node.left = n1
308
- node.right = n2
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 = @@queueOptions.merge(userOptions)
572
- Canvas.init(300, 500, "BitLab")
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
- def redraw_queue(pq)
580
- puts "redraw: #{pq.inspect}"
581
- end
582
-
583
- @@queueOptions = {
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 hooks to the PriorityQueue's shift and << methods so they check to see if the
594
- queue is on the canvas, and if so, update the drawing when an item is removed or added.
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
- alias_method :pqappend, :<<
602
-
603
- def <<(obj)
604
- res = pqappend(obj)
605
- redraw_queue(self) if on_canvas
606
- return res
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
- alias_method :pqshift, :shift
610
-
611
- def shift
612
- res = pqshift
613
- redraw_queue(self) if on_canvas
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 worklist is
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
- worklist = Array(2..n)
31
+ worksheet = Array(2..n)
32
32
  primes = []
33
33
 
34
- while worklist.length > 0
35
- primes << worklist.first
36
- worklist.delete_if { |x| x % primes.last == 0 }
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{shift length first last to_s inspect clear empty?}.each do |name|
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 worklist.
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
- worklist = Array(2..n)
24
+ worksheet = Array(2..n)
25
25
  primes = []
26
26
 
27
- while worklist.first < sqrt(n)
28
- primes << worklist.first
29
- worklist.delete_if { |x| x % primes.last == 0 }
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 + worklist
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.0
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-05-17 00:00:00 -07:00
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 in the Science of Computing text.
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