To achieve this level of control, we first need some way for our Ruby script to handle command-line arguments provided to it. Second we need to be able to construct and run commands from within Ruby just as if we had typed them at the command line.
For example, I am working on the analysis of what are known as k-SAT landscapes: the idea is to construct a boolean function, such as x_1 & x_2 | x_1 & x_3, which contains a given number, m, of clauses, each clause with k terms. The clauses are constructed from n different variables, and there should be N of the landscapes for any given combination of n, m and k. I have a C program which will take n, m, and k as input arguments and print the landscape to standard output. I want to automate the construction of the N landscapes, and capture the output into files named in the format: 'results-n-m-k-i.txt', where i will range from 0 to N-1. This is ideal for automation, as the construction of any landscape can take up to a day.
Basic Techniques
When you start a Ruby program, using: 'ruby program.rb X ...' you can provide command-line arguments as 'X ...'. These arguments are placed into the array ARGV, as strings. (Warning for C programmers: ARGV[0] is the first argument, not the name of the program!) It is a simple matter to read each item out of ARGV, convert to a suitable number format, and place the result into an internal variable: to convert the first parameter to an integer and store in 'x', write: x = ARGV[0].to_i
Ruby strings make it easy to interpolate values from variables using the #{} syntax, which interprets anything inside the brackets and puts the result into the string. For example:
5.times do |i|
puts "The value is #{i}"
end
Will print:
The value is 0
The value is 1
The value is 2
The value is 3
The value is 4
Finally, to call an external program, and pass arguments to it, Ruby provides convenient backticks, ` `. Anything between the backticks will first be interpreted as a string (so variable values can be added in, as above) and then passed to the shell for execution, just as if you had typed the command within the backticks on the command line. Thus, `wc #{filename}` will call the word-count program on the file named within the 'filename' variable.
My final code is below. Notice the check for the number of input arguments, and output of a help message if there are not enough; this is always useful, as I very quickly forget what those input arguments should be. The script can be run easily using:
ruby create-samples.rb 20 50 3 20
# Automate the construction of landscapes
#
# ruby create-samples.rb n m k N
#
# where n = number of variables
# m = number of clauses
# k = number of variables per clause
# N = number of formulae
#
# Results for formulae constructed with names: results-n-m-k-i.txt
# where i runs from 0 to N-1
if ARGV.length != 4
puts <<-HELPMESSAGE
Automate the construction of landscapes
Copyright (c) Peter Lane, 2008.
ruby create-samples.rb n m k N
where n = number of variables
m = number of clauses
k = number of variables per clause
N = number of formulae
Results for formulae constructed with names: results-n-m-k-i.txt
where i runs from 0 to N-1
HELPMESSAGE
else
n = ARGV[0].to_i
m = ARGV[1].to_i
k = ARGV[2].to_i
N = ARGV[3].to_i
N.times do |i|
puts "Creating file #{i} for n=#{n}, m=#{m}, k=#{k}"
`../makels #{n} #{m} #{k} > results-#{n}-#{m}-#{k}-#{i}.txt`
sleep 1.0 # wait at least a second, to allow for C's randomisation!
end
end

