bc
"
and your projects can be combined to make this happen:
echo "for(i=1; i <= 1000; ++i) s(i*i)" | bc -l | scaler | GetYoFreqOn | ReturnZero | pp
First, echo "for(i=1; i <= 10000; ++i) s(i*i)" | bc -l
generates the sines of the squares of the first 10000
numbers.
Then scaler
scales the results so that instead
of falling in the range -1..1, as sine does, they are scaled
to fall in the range 0..100.
Then GetYoFreqOn
counts the number of values
falling in the bins [0,1),[1,2),...,[98,99),[99,100] and
prints out those 100 frequency counts. Then ReturnZero adds
adds on x-coordinates to the frequency values to provide
evenly spaced points. Finally pp
(or
epsplot
if you'd prefer a printable picture)
plots the points in a nice window on the screen.
Important Lesson By saying less, by providing a less elaborate interface, by making minimalist I/O requirements, we end up with programs that can be tied together in new and interesting ways!
For example, now we have a nice function plotter. To plot sin(x^2) in the range -2..2, we could enter:
echo "for(x=-2; x <= 2; x=x+0.05) s(x*x)" | bc -l | scaler | ReturnZero | ppTry it and see what happens.
pp
to connect the points it plots with
lines. If pp
asked the user whether he wanted
this option, and the user had to type some response, the
pipeline we set up would be messed up. Same thing for any of
the other pieces of our pipeline, which the user might like to
control similarly. Therefore, we are led inexorably to
command-line options as the way to allow the user to modify
the behaviour of programs --- like the -l
in the
call to wc -l
.
What we really want, then, is a -l
option for
pp
to tell it to draw lines between the points.
Every program you launch from the command line is given an array of each of the strings that was typed onto the command line in launching that program (called the argument vector) and an integer telling you how many elements that array has. the name of the program is the first of these. Here is a demonstration of how that information can be accessed:
/* A silly program demonstrating command line arguments */ #include <iostream> #include <string> using namespace std; int main(int argc, char **argv) { for(int i = 0; i < argc; ++i) { cout << "argv[" << i << "] = " << argv[i] << endl; } return 0; }Here are several runs of the above program, demonstrating what kind of output you get.
valiant[1] [~/courses/SI486AS05/classes/L03/]> g++ -o ex1 ex1.cpp valiant[140] [~/courses/SI486AS05/classes/L03/]> ./ex1 argv[0] = ./ex1 valiant[2] [~/courses/SI486AS05/classes/L03/]> ex1 "the end" argv[0] = ex1 argv[1] = the end valiant[3] [~/courses/SI486AS05/classes/L03/]> /home/wcbrown/courses/SI486AS05/classes/L03/ex1 -s 7 argv[0] = /home/wcbrown/courses/SI486AS05/classes/L03/ex1 argv[1] = -s argv[2] = 7It is important to note that the strings in
argv
are c-style strings, not C++ string objects. That means you
need to use strcmp to compare strings, strlen to get their
length, etc. It's easier to simply assigning the element of
argv
to a C++ string object, which then can be
manipulated a bit more easily.