teaching machines

Flipping my classroom with Ruby

August 11, 2012 by . Filed under code, public, teaching.

I’m trying something funny in my mobile software development class. I scheduled it in the computer lab, not a lecture hall, and students are going to have to teach each other some things they prepared before we meet. Here’s what I’m telling them:

No good classroom management technique is complete without some supporting scripts. I’ve been meaning to learn Ruby for some time now. Every time I have a script to write, I suppress the temptation to code it up quickly in a language I know. Instead, I scrounge the Internet for the first Ruby construct that meets my purposes. For randomly pairing up students from groups A and B, I wrote this script:

#!/usr/bin/env ruby

# ----------------------------------------------------------------------------
# FILE:   pair.rb
# AUTHOR: Chris Johnson
# DATE:   Aug 11 2012
#
# A script for randomly pairing up students. Before class, students have been
# split up in two groups, A and B. Groups are formed such that each group
# contains a student from A and a student from B. In the event that the groups
# have different sizes, the number of groups formed is min(|A|, |B|). The
# leftover students, as well as any students who did not do the work required
# by the A and B treatments, are distributed evenly amongst these groups.
#
# Output is written to stdout and the file pair.log.
#
# Usage: pair.rb assignments.csv
#
# The first line of the CSV file is headers, and two columns are present. The
# first contains the student's name, and the second is one of 'a', 'b', 'x',
# and ''. Use 'x' to mark a student as present (needing a group) but neither A
# or B, and '' to mark a student absent. For example:
#
# name,group
# P. Pared,a
# A. Straight,b
# S. Pants,b
# T. Book,a
# Z. Brain,b
# D. Zeased,
# N. Competent,x
#
# A possible arrangement of the students is:
#
# T. Book, A. Straight, S. Pants
# P. Pared, Z. Brain, N. Competent
# ---------------------------------------------------------------------------- 

require 'rubygems'
require 'fastercsv'

file = ARGV[0]

as = Array.new
bs = Array.new
xs = Array.new
absents = Array.new
groups = Array.new

# Drop each student into one of the four groups.
FasterCSV.foreach(file, :headers => :first_row) do |line|
  if not line[1]
    absents << line[0]
  elsif line[1] == "x"
    xs << line[0]
  elsif line[1] == "a"
    as << line[0]
  else line[1] == "b"
    bs << line[0]
  end
end

# Mix 'em up so we get random pairings.
as.shuffle!
bs.shuffle!
xs.shuffle!

# Put an A with a B as long as we have one of each.
while as.length > 0 and bs.length > 0
  groups << "%s, %s" % [as.pop, bs.pop]
end

i = 0

# Distribute out the remaining As in the groups we've already made.
while as.length > 0
  groups[i] = "%s, %s" % [groups[i], as.pop]
  i = (i + 1) % groups.length
end

# Distribute out the remaining Bs in the groups we've already made.
while bs.length > 0
  groups[i] = "%s, %s" % [groups[i], bs.pop]
  i = (i + 1) % groups.length
end

# Distribute out the students who didn't do their work.
while xs.length > 0
  groups[i] = "%s, %s" % [groups[i], xs.pop]
  i = (i + 1) % groups.length
end

# Show the groups.
puts groups

logFile = File.new("pair.log", "w")
logFile.puts groups
logFile.close

If I’m violating the Ruby way somewhere, I’d welcome your input.

Revision – August 21, 2012

Based on the comments, I’ve revised my script:

#!/usr/bin/env ruby

# ---------------------------------------------------------------------------- 
# FILE:   pair.rb                                                              
# AUTHOR: Chris Johnson                                                        
# DATE:   Aug 11 2012                                                          
#                                                                              
# A script for randomly pairing up students. Before class, students have been
# split up in two groups, A and B. Groups are formed such that each group
# contains a student from A and a student from B. In the event that the groups
# have different sizes, the number of groups formed is min(|A|, |B|). The
# leftover students, as well as any students who did not do the work required
# by the A and B treatments, are distributed evenly amongst these groups.
#
# Output is written to stdout and the file pair.log.
#
# Usage: pair.rb assignments.csv
#
# The first line of the CSV file is headers, and two columns are present. The
# first contains the student's name, and the second is one of 'a', 'b', 'x',
# and ''. Use 'x' to mark a student as present (needing a group) but neither A
# or B, and '' to mark a student absent. For example:
#
# name,group
# P. Pared,a
# A. Straight,b
# S. Pants,b
# T. Book,a
# Z. Brain,b
# D. Zeased,
# N. Competent,x
#
# A possible arrangement of the students is:
#
# T. Book, A. Straight, S. Pants
# P. Pared, Z. Brain, N. Competent
# ---------------------------------------------------------------------------- 

require 'rubygems'
require 'fastercsv'

class Pairer
  @lastFilledAt = 0

  def initialize(file)
    lists = {
      'a' => Array.new,
      'b' => Array.new,
      'x' => Array.new,
      '' => Array.new
    }

    # Drop each student into one of the four groups.
    FasterCSV.foreach(file, :headers => :first_row) do |line|
      name = line[0]
      group = line[1]
      lists[group] << name
    end

    # Mix 'em up so we get random pairings.
    lists['a'].shuffle!
    lists['b'].shuffle!
    lists['x'].shuffle!

    # Put an A with a B as long as we have one of each.
    @groups = Array.new
    while lists['a'].length > 0 and lists['b'].length > 0
      @groups << "#{lists['a'].pop}, #{lists['b'].pop}"
    end

    # Distribute out the remaining As, Bs, and Xs in the groups we've already
    # made.
    @lastFilledAt = 0
    distribute(lists['a'])
    distribute(lists['b'])
    distribute(lists['x'])
  end

  def distribute(list)
    while list.length > 0
      @groups[@lastFilledAt] = "#{@groups[@lastFilledAt]}, #{list.pop}"
      @lastFilledAt = (@lastFilledAt + 1) % @groups.length
    end
  end

  def log
    # Show the groups. 
    puts @groups

    logFile = File.new("pair.log", "w")
    logFile.puts @groups
    logFile.close
  end
end

pairer = Pairer.new(ARGV[0])
pairer.log