CSC151.01 2009F Functional Problem Solving : Labs
Primary: [Front Door] [Schedule] - [Academic Honesty] [Instructions]
Current: [Outline] [EBoard] [Reading] [Lab] - [Assignment]
Groupings: [Assignments] [EBoards] [Examples] [Exams] [Handouts] [Labs] [Outlines] [Projects] [Readings]
References: [A-Z] [By Topic] - [Scheme Report (R5RS)] [R6RS] [TSPL4]
Related Courses: [CSC151.02 2009F (Weinman)] [CSC151.02 2009S (Davis)] [CSC151 2008S (Rebelsky)]
Misc: [SamR] [MediaScript] [GIMP]
Summary: In this laboratory, we explore file creation, input, and output in Scheme.
a. Scan through this lab to determine what kinds of tasks you'll need to complete.
b. You should also scan through the reading on files in Scheme.
c. Open the reference on files in Scheme in a separate window.
d. Start the GIMP and MediaScript.
e. Add the procedures from the reading (reprinted at the end of this lab) to your definitions window.
As you may recall from the reading, we have prepared two simple files
for simple exploration of input and output,
/home/rebelsky/glimmer/samples/hi.txt
and
/home/rebelsky/glimmer/samples/sample.txt
.
a. Using a sequence of commands in the interaction window,
read all the characters from hi.txt
. For example,
>
(define source (open-input-file "/home/rebelsky/glimmer/samples/hi.txt"))
>
(read-char source)
#\H
...>
(read-char source)
#<eof>
>
(close-input-port source)
b. Using a sequence of commands in the interaction window,
read all the characters from sample.txt
.
c. Using a sequence of commands in the interaction window,
read all the values from sample.txt
(use
read
rather than read-char
).
d. Using a sequence of commands in the interaction window,
read all the values from hi.txt
.
The file /home/rebelsky/glimmer/samples/scheme-values.txt
contains the following:
23 A "A" #\A (1 2 3)
a. Use read-char
to confirm that it has that form.
>
(define inport (open-input-file "/home/rebelsky/glimmer/samples/scheme-values.txt"))
>
(read-char inport)
#\2
>
(read-char inport)
#\3
>
(read-char inport)
#\newline
>
(read-char inport)
#\A
...>
(close-input-port inport)
b. What values do you expect repeated calls to read
to extract from that file?
c. Check your answer experimentally.
>
(define inport (open-input-file "/home/rebelsky/glimmer/samples/scheme-values.txt"))
>
(read inport)
...>
(close-input-port inport)
The file /home/rebelsky/glimmer/samples/numbers.txt
contains five hundred and twenty-eight natural numbers.
a. Use sum-of-file
from the reading to determine their sum.
b. How would you quickly determine if your attempt to sum those numbers was correct?
Citation: That file was copied from a similar file produced by Mr. Stone.
Using sum-of-file
(and its helpers) as a
pattern, write a Scheme procedure, (file-size
"file-name")
that takes as argument a string that names a file
and returns the number of characters in that file (that is, the number
of times that read-char
can be called to read a
character from the file without returning the end-of-file object).
Find out what happens if sum-of-file
or
file-size
is given a string that does not name
any existing file.
In the interactions pane, write a series of expressions that will
create a file, my-info
, with the following lines
(substituting your own name and major).
Name: _last_, _first_ Major: _major-or-undeclared_
The Scheme standard says that if you try to open an output port to a file that already exists, “the effect is unspecified”, i.e., anything might happen. Hence, designers of a particular implementation of Scheme are free to do what they choose.
Find out through experimentation what MediaScript does in this situation.
Write a Scheme procedure, (dump-info file-name last-name
first-name major)
that, given four strings as parameters,
writes the following to the file named by file-name
,
Name: last-name, first-name Major: major
Write a procedure, (
, that takes the name
of a file as a parameter and returns a list of all the lines
of the file (with each line represented as a string). You can
certainly use the read-lines
filename
)read-line
procedure from
the reading as a helper for read-lines
.
You might use sum-of-file
as a pattern for
read-lines
.
Write a procedure, (
, that takes
the name of a file as a parameter and displays the contents of the
file with each line preceded by its line number. You may find it
useful to call upon the previous procedure to do the reading.
display-file
filename
)
Use the store-divisors
procedure from the reading
to draw up a list of the divisors of 120, storing them in a file named
divisors-of-120
. Examine the file afterwards
and confirm that the answer is correct.
By the way, don't give this procedure an extremely large number as argument -- it's much too slow. There are more efficient ways to find divisors!
Write a Scheme procedure that takes as arguments two file names (an input file and an output file), counts the number of occurrences of each vowel in the input file, and writes the result to the output file. Note that the output file should have the following form (with numbers in place of the number signs):
a: ### e: ### i: ### o: ### u: ###
If you have time or inclination, extend your procedure to deal with all 127 ASCII characters.
;;; Procedure: ;;; read-line ;;; Parameters: ;;; source, an input port ;;; Purpose: ;;; Read one line of input from a source and return that line ;;; as a string. ;;; Produces: ;;; line, a string ;;; Preconditions: ;;; The source is open for reading. [Unverified] ;;; Postconditions: ;;; Has read one line of characters from the source (thereby affecting ;;; future calls to read-char and peek-char). ;;; line represents the characters in the file from the "current" point ;;; at the time read-line was called until the first end-of-line ;;; or end-of-file character. ;;; line does not contain a newline. (define read-line (lambda (source) ; Read all the characters remaining on the line and ; then convert them to a string. (list->string (read-line-of-chars source)))) ;;; Procedure: ;;; read-line-of-chars ;;; Parameters: ;;; source, an input port ;;; Purpose: ;;; Read one line of input from a source and return that line ;;; as a list of characters. ;;; Produces: ;;; chars, a list of characters. ;;; Preconditions: ;;; The source is open for reading. [Unverified] ;;; Postconditions: ;;; Has read characters from the source (thereby affecting ;;; future calls to read-char and peek-char). ;;; chars represents the characters in the file from the ;;; "current" point at the time read-line was called ;;; until the first end-of-line or end-of-file character. ;;; chars does not contain a newline. (define read-line-of-chars (lambda (source) ; If we're at the end of the line or the end of the file, ; then there are no more characters, so return the empty list. (cond ; If we're at the end of the file, there are no more characters, ; so return the empty list. ((eof-object? (peek-char source)) null) ; If we're at the end of the line, we're done with the line ; skip over the end-of-line character and return the empty list. ((char=? (peek-char source) #\newline) (read-char source) null) ; Otherwise, read the current character, read the remaining ; characters, and join them together. (else (cons (read-char source) (read-line-of-chars source)))))) ;;; Procedure: ;;; first-line ;;; Parameters: ;;; file-name, a string that names a file. ;;; Purpose: ;;; Reads and displays the first line of the file. ;;; Produces: ;;; Absolutely nothing. ;;; Preconditions: ;;; There is a file by the given name. ;;; It is possible to write to the standard output port. ;;; Postconditions: ;;; Does not affect the file. ;;; The first line of the named file has been written to ;;; the standard output. (define first-line (lambda (file-name) (let ((source (open-input-file file-name))) (display "The first line of '") (display file-name) (display "' is") (newline) (display (read-line source)) (newline) (close-input-port source)))) ;;; Procedure: ;;; sum-of-file ;;; Parameters: ;;; file-name, a string that names a file. ;;; Purpose: ;;; Sums the values in the given file. ;;; Produces: ;;; sum, a number. ;;; Preconditions: ;;; file-name names a file. [Unverified] ;;; That file contains only numbers. [Unverified] ;;; Postconditions: ;;; Returns a number. ;;; That number is the sum of all the numbers in the file. ;;; Does not affect the file. (define sum-of-file (lambda (file-name) (let kernel ((source (open-input-file file-name))) ; named let (let ((nextval (read source))) (cond ; Are we at the end of the file? ; Then stop and return 0 for "no numbers read". ; Here, we're taking advantage of 0 being the arithmetic identity. ((eof-object? nextval) (close-input-port source) 0) ; Have we just read a number? ; If so, add it to the sum of the remaining numbers. ((number? nextval) (+ nextval (kernel source))) ; Hmmm ... not a number. Skip it. (else (kernel source))))))) ;;; Procedure: ;;; integer-store-divisors ;;; Parameters: ;;; dividend, a natural number ;;; file-name, a string that names a file ;;; Purpose: ;;; Compute all the divisors of dividend and store them ;;; to the named file. ;;; Produces: ;;; [None; Called for the side effect of creating a file] ;;; Preconditions: ;;; It must be possible to open the desired output file. ;;; dividend must be a non-negative, exact, integer- ;;; Postconditions: ;;; The file with name file-name now contains many integers. ;;; All the values in that file evenly divide dividend. (define integer-store-divisors (lambda (dividend file-name) (integer-store-divisors-kernel dividend (open-output-file file-name) 1))) ;;; Helper: ;;; integer-store-divisors-kernel ;;; Parameters: ;;; dividend, the number we're working with ;;; target, an output port ;;; trial-divisor, the smallest divisor we should try ;;; Purpose: ;;; Stores all divisors of dividend that are at least as ;;; large as trial-divisor to target. ;;; Produces: ;;; Nothing. ;;; Preconditions: ;;; It is possible to write to the target port. ;;; Both trial-divisor and dividend are natural numbers. ;;; Postconditions: ;;; All divisors of dividend that are at least as large as ;;; trial-divisor have been added to target. ;;; target is still open for writing (define integer-store-divisors-kernel (lambda (dividend target trial-divisor) ; We only continue to work when the trial-divisor is not ; larger than the dividend. Note that I'm using cond because ; cond permits multiple operations when the test succeeds. (cond ((<= trial-divisor dividend) ; Okay, does the current trial-divisor evenly divide ; dividend? (cond ((zero? (remainder dividend trial-divisor)) ; It does! Write it to the file (write trial-divisor target) (newline target))) ; Continue with any other potential divisors (integer-store-divisors-kernel dividend target (+ 1 trial-divisor))) ; If the trial divisor is bigger than the dividend, then we're ; done, so close the port and stop. (else (close-output-port target)))))
Primary: [Front Door] [Schedule] - [Academic Honesty] [Instructions]
Current: [Outline] [EBoard] [Reading] [Lab] - [Assignment]
Groupings: [Assignments] [EBoards] [Examples] [Exams] [Handouts] [Labs] [Outlines] [Projects] [Readings]
References: [A-Z] [By Topic] - [Scheme Report (R5RS)] [R6RS] [TSPL4]
Related Courses: [CSC151.02 2009F (Weinman)] [CSC151.02 2009S (Davis)] [CSC151 2008S (Rebelsky)]
Misc: [SamR] [MediaScript] [GIMP]
Copyright (c) 2007-9 Janet Davis, Matthew Kluber, Samuel A. Rebelsky, and Jerod Weinman. (Selected materials copyright by John David Stone and Henry Walker and used by permission.)
This material is based upon work partially supported by the National Science Foundation under Grant No. CCLI-0633090. Any opinions, findings, and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the National Science Foundation.
This work is licensed under a Creative Commons
Attribution-NonCommercial 2.5 License. To view a copy of this
license, visit http://creativecommons.org/licenses/by-nc/2.5/
or send a letter to Creative Commons, 543 Howard Street, 5th Floor,
San Francisco, California, 94105, USA.