14 August 2011

Lesson 3: Input and output

This lesson covers:

  • Comments
  • Multi-statement lines and multi-line statements
  • Input with the read function
  • Output with the print statement

You should read Lesson 1 and Lesson 2 before this one.

This is my least favorite lesson. Nothing in this lesson makes it possible to solve new problems. It's just stuff to make your programs easier to read. The problems we solve in this lesson will be kind of lame. Let's get this over with.

Comments

Comments are simply text that bc ignores. The purpose of comments is to tell people reading your program what it does, how it does it, and how they can use it.

There are two ways to make a comment in bc. The way I prefer uses a pound sign #, which you may call a number sign, hash, or octothorpe. Everything on a line after a # is a comment, which will be ignored when your program is run.

You can also start a comment with slash-star /* and end it with star-slash */. You might prefer this way if you have comments that go on for more than one line. If I want a multi-line comment, I'll just put a # at the beginning of each line, but you feel free to use /* and */.

Commenting is also used to temporarily disable some of your program. If you want to see how your program would run without a certain line, you can "comment it out" by putting a # at the beginning of the line, and bc will just skip that line.

Here's a comic that uses the fact that comments in C++ start with //. The idea is that you can ignore someone by putting // in front of them. Get it?

Comment example

Anyway, here's an example of a program with comments:

fib.bc
# Find the nth Fibonacci number
n = 12
r = (1 + sqrt(5)) / 2  # The golden ratio
(r^n - (1 - r)^n) / sqrt(5)  # Binet's formula
halt

Writing good comments is a skill that you'll pick up over time as you write and read programs. One common rookie mistake is over-commenting. You should assume that people reading your program understand how bc works, so don't explain obvious things.

fib.bc
# Find the nth Fibonacci number
# This file is called fib.bc, because fib is short
#   for Fibonacci. That's because we're finding the
#   nth Fibonacci number.

# Beginning of program

# n is a variable that represents n
n = 12  # Assign 12 to n
# sqrt(5) means take the square root of 5
r = (1 + sqrt(5)) / 2  # The golden ratio
# About to output the nth Fibonacci number
# sqrt(5) means take the square root of 5
(r^n - (1 - r)^n) / sqrt(5)  # Binet's formula
# Done outputting the nth Fibonacci number
halt  # halt means to exit bc

# End of program
# Thank you

So I won't give you any rules for when to comment. Just use your judgment. But one thing I'll always do is start each program with a comment that says what the program does.

Short statements

Normally every statement is exactly one line long, and every line has exactly one statement on it. Occasionally, putting more than one short statement on a single line makes your program more readable. You can do this by separating statements with a semicolon ;:

day1 = 21 ; month1 = 12 ; year1 = 1872
day2 = 21 ; month2 = 11 ; year2 = 1955
day3 = 1  ; month3 = 1  ; year3 = 802701

That's a little clearer than having each statement on its own line, and it's interpreted the same way by bc:

day1 = 21
month1 = 12
year1 = 1872
day2 = 21
month2 = 11
year2 = 1955
day3 = 1
month3 = 1
year3 = 802701

If you like you can also put a ; at the end of every statement. It doesn't do anything. Some people do this because it's required in some languages, but it's optional in bc, so either way is fine.

Long statements

If a statement is really long, you might want to break it up into more than one line. This can be done by ending every line that doesn't end the statement with a backslash \. If you do this, the \ has to be the very last character on the line: you can't even put spaces or comments after it:

tedious_sum = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + \
              9 + 10 + 11 + 12 + 13 + 14 + 15 \
              + 16 + 17 + 18 + 19 + 20 + 21 + \
              22 + 23 + 24 + 25

Smooth programming rarely requires statements so long that they don't fit on a single line. I'm never going to use this feature in this tutorial.

The read function

The read function is how a bc program gets input from the user (the person running the program). When bc gets to read(), it will stop and wait for the user to enter a number. Let's solve a problem using the read function.

plates.bc
# Count the number of possible number plates
#   given the number of letters and digits
nletters = read()
ndigits = read()
26^nletters * 10^ndigits
halt

This program tells you how many different number plates (like on a car) you can make, if each plate has a certain number of letters and digits on it (and they appear in the same order, and you can tell the letter O from the number 0, etc.). If there are 3 letters and 2 digits, there are 1,757,600 possible plates. If we run the program and enter 3 and 2, it should give us this answer:

christopher:~/smooth$ bc -q plates.bc
3
2
1757600
christopher:~/smooth$ 

Problem 1: Number Plates

Which option will give you more possible plates? Using 5 letters and 1 digit, or using 0 letters and 8 digits? Solve this problem without changing the text of the program, just by running it twice and inputting different numbers.

Click to reveal solution
Click to hide solution

5 letters and 1 digit give you more possible plates:

christopher:~/smooth$ bc -q plates.bc
5
1
118813760
christopher:~/smooth$ bc -q plates.bc
0
8
100000000
christopher:~/smooth$ 

The print statement

So far we've seen that when you enter an expression that's not an assignment, its value gets printed. The print statement is another way to print a value. It can also be used to print text, by placing double-quote marks " at the beginning and end of the text:

christopher:~/smooth$ bc -q
print "hello world"
hello world

You'd think that printing stuff out should be the simplest thing in the world, right? Well here's where it gets complicated. When you use a print statement, there's no newline at the end. (A newline is what you get when you press Enter.) If you want a newline, there are two ways to do it. The bad way is just to put an Enter inside your quotes:

print "hello world
"

But don't do that. The preferred way is with backslash-n \n:

print "hello world\n"

If you need to print out a double-quote mark, use backslash-q \q, and to make a backslash, use double-backslash \\\\:

print "    \qhello world\q\n\\\\o/\n"
    "hello world"
\o/

If you really feel like it, you can print text without print. Just put it on a line in ", but \n, \q, and \\\\ won't work:

"hello world\n"
hello world\n

I'll always use print when printing text.

Multiple print statements

You can use print statements to give context to the values that you print out. Just printing 1757600 is not as useful as printing The number of possible plates is: 1757600. The following in a program:

x = 4
y = 3
print "(x, y) = ("
print x
print ", "
print y
print ")\n"

will print:

(x, y) = (4, 3)

When you have consecutive print statements like that, you can combine them into one, by separating the things you're printing with commas. You can print out both text and expressions using the same print statement when you do this, that's fine.

print "(x, y) = (", x, ", ", y, ")\n"

In this example it can be confusing which commas are inside the quotes and which ones are outside, but that's the way you have to do it.

Triangular Tower

Say you've got a tower 4 levels high. The top level is 1 block, then 2, then 3, then 4. How many blocks make up the tower? It's 1 + 2 + 3 + 4 = 10. Now what if the tower is 100 blocks high? Then it's 1 + 2 + ... + 99 + 100 blocks.

So what's the sum of the numbers from 1 to 100? It's said that the mathematician Carl Gauss figured out how to get the answer quickly when he was seven years old. But then again, a lot of things get said about Gauss. (Like, Gauss can recite all the digits of π... backward.) Anyway, the technique he would have used basically comes down to this. A tower n levels high has this many blocks:

$$1 + 2 + ... + (n-1) + n = {n(n+1)}/2$$

If we used this formula with n = 100, we would see that the tower needs 5,050 blocks.

Problem 2: Program for the triangular tower

Let's bring it all together. Write a program that uses the read function and the print statement to input the number of levels in the triangular tower from the user, and output the number of blocks in the tower. Your program should also contain at least one comment. Here's what it should look like when you run the program:

christopher:~/smooth$ bc -q tower.bc
How many levels in the tower?
36
1 + 2 + ... + 35 + 36 = 666
so there will be 666 blocks in the tower.
Click to reveal completed program
Click to hide completed program
tower.bc
# Find the number of blocks in a triangular tower

print "How many levels in the tower?\n"
n = read()
b = n * (n+1) / 2  # number of blocks

print "1 + 2 + ... + ", n-1, " + ", n, " = ", b, "\n"
print "so there will be ", b, " blocks in the tower.\n"

halt

Next time

Sometimes in your programs, you want to sometimes do one thing and sometimes do another. Next time we'll see how to make decisions like this.

1 comment:

  1. Your program for Zeller's Congruence is nice, and it works for the limited test suite of three dates in your example. But three dates is not enough, is it? Your program doesn't do very well for days in that messy Month, February, which has a varying number of days. You need to begin the year in March, I think. But that's just a suggestion. Otherwise, your program's good, and even the slight error is more useful than someone else's b.s., in my view. J. Wagner, <gnujohn"at"gmail"dot"com.

    ReplyDelete