You should be able to complete this assignment using only the elements of Ruby presented on slides 1-114 and/or mentioned in this write-up. If you find yourself thinking that need to use other elements of the language, it probably indicates you don't realize the capabilities of what's been covered.
There will be one more programming assignment, a9
, using Ruby, that will be worth 100 points and due on December 7, the last
day of classes. Remember that the video assignment is also due on December 7.
You'll see that two of the programs, xfield.rb
and flash.rb
, process command line arguments. On those problems
you are not permitted to use any library code that parses/processes command line arguments, such as
OptionParser
.
That's for two
reasons: (1) the argument processing needed is a good exercise in plain old programming with Ruby, and (2) with things like
OptionParser
, the "cure" can be worse than the disease.
seqwords.rb
, longest.rb
, and jlongest.java
have some detailed restrictions
but note that the latter two are zero-point problems.
String
and Array
classesString
and Array
classes are rich. (Maybe too rich!)
Once you've read the assignment, browse the documentation for them to get
a feel for what they offer. Slide 8 shows the pattern used by URLs for Ruby class documentation.
We'll study inheritance in Ruby later but like Java, Ruby classes inherit methods from their superclasses. Additionally,
Ruby classes draw methods from "included" modules. When looking at
http://ruby-doc.org/core-2.7.0/Array.html
note the boxes for Parent,
Methods, and Included Modules in the left
sidebar. Array
's parent, Object
, isn't very interesting but the
Enumerable
module, which Array
include
s, has many useful methods.
Make an a8
symlink that references /cs/www/classes/cs372/fall22/a8
. Test using a8/tester
(or a8/t
).
All of the Ruby problems on this assignment are programs that can be run from the command line,
like the group.hs
and avg.hs
Haskell problems.
A strong convention in the UNIX world is that programs do not output a trailing blank line. Note the absence of trailing blank lines in these invocations:
$ date
Sun Nov 13 13:24:49 MST 2022
$ date | wc
1 6 29
$ (my prompt—Bash is waiting for the next command)
However, for ease of reading in this write-up, output from command-line examples is followed by a blank line, like this:
$ date Sun Nov 13 13:24:49 MST 2022 $ date | wc 1 6 29 $
On Wednesday, November 16 we'll use part of our class period to discuss your experiences, observations, loves, hates, etc. about Prolog. Being present for that discussion will earn you the five points for this problem.
We'll record attendance in some way for that discussion; there is nothing to turn in for this problem.
seqwords.rb
For this problem you are to write a Ruby program that reads a series of words from standard input, one per line, and then prints lines with the words sequenced according to a series of specifications, also one per line and read from standard input.
Don't overlook the restriction mentioned below.
Here is an example with four words and three sequencing specifications, which are simply integers, one per line.
$ cat a8/sw.1 one two three four . 1 2 3 . 3 2 1 1 2 3 . 4 1 $ ruby seqwords.rb < a8/sw.1 one two three three two one one two three four oneNote that lines containing only a period (
.
) end the word list and also separate specifications. For output, words are separated with a single blank. Here's another example:
$ cat a8/sw.2 tick . 1 . 1 1 $ ruby seqwords.rb < a8/sw.2 tick tick tickMake these assumptions for
seqwords.rb
:
Here is a key simplification: Assume that words are between 1 and 100,000 characters in length, inclusive. (Recall that 100_000
is a valid
Integer
literal in Ruby, just like in Java.) With that assumption you should be able to write code that's O(1) for
retrieving the Nth word.
If your code doesn't make some significant use of that 100,000-character limit for words, you'll be solving a harder problem than I intend.
Mail to 372f22
or see us during office hours if you don't see how to leverage that limit.
Restriction: The only types you may use are Integer
and String
along with the values nil
, true
, and false
(i.e., the values of the single-value classes NilClass
, TrueClass
, and FalseClass
).
In particular, you may NOT use Array
s.
As an aid to help ensure that your solution doesn't violate the restriction, the file
a8/check-seqwords.rb
has Ruby code that disables various common operations on arrays, as well as String#split
.
Your seqwords.rb
needs to explicitly load that file.
The first non-comment/blank line of your seqwords.rb
should be the following line, starting in column one:
load "a8/check-seqwords.rb"
If you don't have that line exactly right, you'll see a tester failure like this:
Error: No line with 'load "a8/check-seqwords.rb"' found in seqwords.rb
With that load
in place, let's imagine we've got a version of seqwords.rb
that
creates an array and then adds an element to it with Array
's <<
method—
definitely not permitted!
Here's what the failure will look like:
Test: 'ulimit -t 2; ruby seqwords.rb < a8/sw.1 | sed "s/ *$//"': FAILED Differences (expected/actual): *** a8/master/tester.out/seqwords.out.01 2016-02-22 20:20:03.000000000 -0700 --- tester.out/seqwords.out.01 2022-11-13 13:33:11.646952295 -0700 *************** *** 1,3 **** ! one two three ! three two one one two three ! four one --- 1,2 ---- ! (eval):1:in `<<': oops -- you shouldn't be using the class Array! (RuntimeError) ! from seqwords.rb:8:in `<main>'Note that
"...oops -- can't use '<<' for that type!"
.
Remember: Loading a8/check-seqwords.rb
will catch common violations of the
restrictions but it's not foolproof.
The "safe harbor" is to mail your code to 372f22
and ask for a manual inspection.
xfield.rb
xfield
.
xfield
was inspired by the UNIX cut(1)
command but its behavior differs in various ways.
Here's a man
-style description of xfield
. Detailed examples follow.
xfield [-dC] [-sSEPARATOR] [FIELDNUM|TEXT]...
xfield
extracts specified fields of data from each line of standard input and writes those fields to standard output.
Field numbers are one-based and may be negative to specify counting from the right.
If a field number is out of bounds, "<NONE>
" is output in place of actual data.
Unlike cut(1)
, xfield
allows fields to be specified in any order and appear more than once.
Fields are delimited by one or more spaces by default but an alternate character to delimit fields can be specified with
-dC
.
Tabs separate output fields by default but -sSEPARATOR
can be used to specify an alternate separator,
which must be at least one character in length.
There may be multiple -d
and -s
specifications, in any order, but they must appear
before any field number or text specifications.
If there are multiple specifications for either -d
or -s
, the last one of each "wins".
If a textual argument (not a number) falls between two field specifications (two numbers), that text is used instead of the separator.
Input lines are assumed to end with a newline. If there are no input lines, xfield
produces no output.
If no fields are specified, the message "xfield: no fields specified
" is printed, and xfield
calls exit(1)
to terminate execution. (exit
is a Kernel
method.)
xfield
is able to handle any number of input lines.
(Hint: Don't read all the input lines into memory and then process them—they might not fit!
Just fully process one line at a time.)
The behavior of xfield
is undefined in cases that are not specifically addressed by this write-up
or exercised with the tester. That means that any non-malicious behavior is ok—run-time errors, curious results,
etc., are not a surprise if the user misuses xfield
.
Some detailed examples of xfield
in operation follow. Here's the input file we'll work with first:
$ cat a8/xf.1 one 1 1.0 two 2 2.0 three 3 3.0 four 4 4.0 twenty 20 20.0The English text and the real numbers can be extracted like this:
$ ruby xfield.rb 1 3 < a8/xf.1 one 1.0 two 2.0 three 3.0 four 4.0 twenty 20.0
xfield
can be used to reorder fields:
$ ruby xfield.rb 3 2 1 < a8/xf.1 1.0 1 one 2.0 2 two 3.0 3 three 4.0 4 four 20.0 20 twenty
xfield
supports negative indexing, just like Ruby arrays:
$ ruby xfield.rb -1 1 < a8/xf.1 1.0 one 2.0 two 3.0 three 4.0 four 20.0 twenty $ ruby xfield.rb -1 1 2 -2 < a8/xf.1 1.0 one 1 1 2.0 two 2 2 3.0 three 3 3 4.0 four 4 4 20.0 twenty 20 20If a field reference is out of bounds, the string "
<NONE>
" is output:
$ ruby xfield.rb 1 10 2 < a8/xf.1 one <NONE> 1 two <NONE> 2 three <NONE> 3 four <NONE> 4 twenty <NONE> 20The
-s
flag specifies an output separator to use instead of tab.
$ ruby xfield.rb -s... 1 3 1 < a8/xf.1 one...1.0...one two...2.0...two three...3.0...three four...4.0...four twenty...20.0...twentyTo extract login ids and real names (and room/phone) from
a8/oldpasswd
, an excerpt from an ancient
/etc/passwd
, one might use -d:
to specify that a colon is the delimiter:
$ ruby xfield.rb -d: 1 5 < a8/oldpasswd wnj Bill Joy,457E,7780 dmr Dennis Ritchie ken Ken Thompson mike Mike Karels carl Carl Smith,508-21E,6258 joshua Josh GoldsteinNote that the
-s
and -d
options are single arguments—there's no space between
-s
or -d
and the following string.
Non-numeric arguments other than the -s
and -d
flags are considered to be text
to be included in each output line. If a textual argument (not a number) falls between two field
specifications (two numbers), that text is used instead of the separator:
$ ruby xfield.rb int= 2 ", real=" 3 ", english=" 1 < a8/xf.1 int=1, real=1.0, english=one int=2, real=2.0, english=two int=3, real=3.0, english=three int=4, real=4.0, english=four int=20, real=20.0, english=twentyNote the use of quotation marks to form an argument that contains blanks. The shell strips off the quotation marks so that the resulting arguments passed to the program do not have quotes. See the Implementation notes for
xfield
section below for more on this.
Here's that text-argument-overrides-separator rule again:
If a textual argument (not a number) falls between two field specifications (two numbers), that text is used instead of the separator.
Here are some more examples showing that rule in action:
$ cat a8/xf.2 one two three four $ ruby xfield.rb -s. 1 2 3 < a8/xf.2 one.two.three $ ruby xfield.rb -s. A 1 2 3 B C < a8/xf.2 Aone.two.threeBC $ ruby xfield.rb -s. A 1 B C 2 3 D < a8/xf.2 AoneBCtwo.threeD $ ruby xfield.rb -s. A 1 B C 2 D 3 E < a8/xf.2 AoneBCtwoDthreeEBelow are some cases that bring all the elements into play.
$ cat a8/xf.3 xxxxxxxAxxxxxxxxxxBxC DxExF xG1xG2 xxxxHIxxxJKxxxLMNxxxOPQRSx $ ruby xfield.rb -dx -s- 1 2 3 < a8/xf.3 A-B-C D-E-F G1-G2-<NONE> HI-JK-LMN $ ruby xfield.rb -dx -s- -1 ... -2 -3 @ < a8/xf.3 C...B-A@ F...E-D@ G2...G1-<NONE>@ OPQRS...LMN-JK@ $ ruby xfield.rb -s/ -de 1 2 < a8/xf.1 on/ 1 1.0 two 2 2.0/<NONE> thr/ 3 3.0 four 4 4.0/<NONE> tw/nty 20 20.0If there are no input lines, xfield produces no output:
$ ruby xfield.rb -s/ -d: 1 x 2 3 < /dev/null
xfield
gets
vs. STDIN.gets
gets
method does a little more than simply reading lines from standard input:
If command line arguments are specified, gets
will consider those arguments to be file names.
It will then try to open those files and read lines from each in turn.
That's really handy in some cases but it gets in the way for xfield
. To avoid this behavior,
don't use "line = gets
" to read lines. Instead, do this:
line = STDIN.getsThat limits
gets
to the contents of standard input.
String.split
I'm astounded by it but the fact is that split
behaves differently when the delimiter is a space:
>> " a b c ".split(" ") => ["a", "b", "c"] >> ".a..b..c.".split(".") => ["", "a", "", "b", "", "c"]
ARGV
,
an array. Here is a8/echo.rb
, a Ruby program that prints the command line arguments:
$ cat a8/echo.rb puts "#{ARGV.size} arguments:" for i in 0...ARGV.length do puts "argument #{i} is '#{ARGV[i]}'" endExecution:
$ ruby a8/echo.rb -s -s2 -abc x y 5 arguments: argument 0 is '-s' argument 1 is '-s2' argument 2 is '-abc' argument 3 is 'x' argument 4 is 'y'Unescaped quotes and backslashes specified on the command line are processed and fully consumed by the shell; the program,
a8/echo.rb
in this case, doesn't "see" them. Example:
$ ruby a8/echo.rb int= 2 ", real=" 3 ", english=" 5 arguments: argument 0 is 'int=' argument 1 is '2' argument 2 is ', real=' argument 3 is '3' argument 4 is ', english=' $ ruby a8/echo.rb " " ' x ' \ \y\ "" 4 arguments: argument 0 is ' ' argument 1 is ' x ' argument 2 is ' y ' argument 3 is ''The shell does provide some mechanisms to allow quotes and backslashes to be transmitted in arguments:
$ ruby a8/echo.rb '"' \\x\\ 2 arguments: argument 0 is '"' argument 1 is '\x\'Additionally, the shell consumes I/O redirections with
<
and >
—the program DOES NOT "see" those operators or their accompanying
filename argument.
Here's some evidence of that—notice that there's no trace of < lg.1
in the following output!
$ ruby a8/echo.rb 1 2 3 < lg.1 3 arguments: argument 0 is '1' argument 1 is '2' argument 2 is '3'Because redirections are consumed by the shell, we can even interleave them with program arguments! Example:
$ ruby > out a8/echo.rb 1 < lg.1 2 3 $ cat out 3 arguments: argument 0 is '1' argument 1 is '2' argument 2 is '3'Note that there is no trace of "
<
", ">
", "lg.1
", or "out
" in the output.
The above examples were produced with a UNIX shell; but you'll see similar behavior when working on the Windows command line, although backslashes are handled differently.
BOTTOM LINE: Don't add code to your solution that attempts to process those shell metacharacters—that's the job of the shell, not your program!
I've seen many students turn command line argument handling into an incredibly complicated mess. Don't do that!
Here's an easy way to process arguments in xfield
:
ARGV
.
"-s"
or "-d
", save the rest of the string for later use.
argument.to_i
produces something other than zero,
then add the value (as an integer) to an array that specifies what's to be printed for each line.
argument.to_i
produces zero, add argument
to that same array.
Note that Ruby's ARGV
is the counterpart to args
in a Java declaration like
void main(String args[])
, but unlike Java, the name ARGV
is fixed.
Remember that as stated in the Restrictions section at the top of the write-up, you
aren't allowed to use argument-parsing libraries like OptionParser
.
".",
the specification
1 3 x 4 x -2 1would be transformed into
1 . 3 x 4 x -2 . 1A hint in a hint: Think about representing the above specification with this Ruby array:
[0, ".", 2, "x", 3, "x", -2, ".", 0]Note the combination of
Integer
s and String
s.
Recall that xfield
's output fields are separated by a single tab. (Use "\t"
.)
I was going demonstrate that here using cat -A a8/xf.1
but I decided to
build my Ruby skills by seeing if I could demonstrate that using only ruby
's
command-line arguments.
After reading through the OPTIONS
in man ruby
and some experimentation, I came up with this:
$ ruby xfield.rb 3 1 2 < a8/xf.1 | ruby -n -e 'puts $_.inspect' "1.0\tone\t1\n" "2.0\ttwo\t2\n" "3.0\tthree\t3\n" "4.0\tfour\t4\n" "20.0\ttwenty\t20\n"
Here's the lesson in LHtLaL: I could have simply used what I knew (cat -A
)
to see those tabs but I chose to build my Ruby skills by
learning about -n
, -e
, and $_
.
(Now I should put that Ruby code into a Bash script, but I can't decide what to name it! rcatA
?)
flash.rb
For this problem you are write a Ruby program flash.rb
, a very simple flashcard-based study system.
Here's a sample input file for flash.rb
:
$ cat a8/units.fc mile : 5280 feet day : 86400 seconds acre : 43560 feet section : 640 acres furlong : 660 feet
Each line specifies two related pieces of information that the user wishes to learn. A colon separates the two pieces of
the pair. Whitespace around each piece is ignored. (Remove it with String#strip
.)
In its simplest mode of operation, flash.rb
selects one of the pairs at random, and prompts the user with the
left-hand piece. The user then enters their answer, which should match the right-hand piece, ignoring case.
If the user answers correctly, the pair is removed from the current run. If not, the pair is retained and tested again at a
random time later in the run, albeit possibly immediately. The process repeats until all pairs have been answered
correctly.
A user may quit at any time by responding with "/u
".
("Uncle!").
Entering "/u
" does not count as a guess.
When all pairs have been answered or the user quits, some simple statistics are shown, along with any pairs that were never answered.
Here's a run:
$ ruby flash.rb a8/units.fc section? 640 acres Correct! mile? 5280 feet Correct! acre? 43650 feet Nope! day? 86400 seconds Correct! furlong? 660 feet Correct! acre? 46350 feet Nope! acre? /u Done! 4 correct in 6 guesses = 66.7% Never answered: acre: 43560 feet
Here's the behavior if the user immediately does /u
:
$ ruby flash.rb a8/units.fc furlong? /u Done! No guesses! Never answered: mile: 5280 feet day: 86400 seconds acre: 43560 feet section: 640 acres furlong: 660 feet
The above case also demonstrates that never-answered pairs are shown in the order they appear in the input file.
flash.rb
supports a number of command-line arguments. Specifying -rt
("right") will cause the user
to be prompted with the right-hand values of the pairs:
$ ruby flash.rb -rt a8/units.fc 5280 feet? mile Correct! 660 feet? furlong Correct! 640 acres? /u Done! 2 correct in 2 guesses = 100.0% Never answered: day: 86400 seconds acre: 43560 feet section: 640 acres
The -mix
option causes a random selection of left and right prompts:
$ ruby flash.rb -mix a8/units.fc day? 86400 seconds Correct! 660 feet? furlong Correct! ...Behavior is undefined if both
-rt
and -mix
are specified.
As a development aid, the -a
option causes the answer to be shown with each prompt:
$ ruby flash.rb -a -mix a8/units.fc day? (psst...'86400 seconds') 86400 seconds Correct! 43560 feet? (psst...'acre') acre Correct!For learning purposes, if the user responds with "
//
", the answer is shown. The response
"//
" is counted as a guess.
$ ruby flash.rb a8/units.fc furlong? // Answer: 660 feet acre? // Answer: 43560 feet section? // Answer: 640 acres mile? /u Done! 0 correct in 3 guesses = 0.0% Never answered: mile: 5280 feet day: 86400 seconds acre: 43560 feet section: 640 acres furlong: 660 feetWhen learning a set of things, it can be useful to focus on a subset. The
-r
option limits
the pairs used to those in a specific range of line numbers. Let's work on United States presidents:
$ cat a8/presidents.fc 1:Washington 2:Adams 3:Jefferson 4:Madison 5:Monroe [...and forty-one more...]Let's focus on the tenth through the fifteenth presidents:
$ ruby flash.rb -r 10-15 -mix a8/presidents.fc Buchanan? 15 Correct! 10? Tyler Correct! 13? Fillmore Correct! Pierce? // Answer: 14 12? Taylor Correct! 11? Polk Correct! Pierce? 14 Correct! Done! 6 correct in 7 guesses = 85.7%
Testing a program that does random selections poses some challenges. The -n
option suppresses randomization
as follows:
-mix
is specified, prompts are alternated in a strict left-right-left-right pattern.
The following example uses a8/hexlets.fc
, limits choices to lines 2-5 (B-E
), specifies mixing
left and right prompts, and suppresses randomization. Study the example, after first looking at a8/hexlets.fc
.
$ ruby flash.rb -r 2-5 -mix -n a8/hexlets.fc B? 11 Correct! 12? B Nope! D? 13 Correct! 14? F Nope! C? 12 Correct! 14? F Nope! E? 14 Correct! Done! 4 correct in 7 guesses = 57.1%
-rt
: prompt with right-hand values
-mix
: prompt with a random mix of left- and right-hand values
-r FROM-THROUGH
: work with only the pairs found on line
FROM
through line THROUGH
.
-a
: Show the answer along with the prompt.
-n
: No random behavior.
-
), it is assumed to be the name of the file of pairs to
work with. The examples above use the suffix .fc
for the pair files but that suffix is not expected
nor assumed.
The behavior of flash.rb
is undefined in cases that are not specifically addressed by this write-up
or exercised with the tester. That means that any non-malicious behavior is ok—run-time errors, curious results,
etc., are not a surprise if the user misuses the program or tries to trip it up.
flash.rb
OptionParser
. You'll need to write some plain old
Ruby code to do that processing yourself.
Note that flash.rb
reads a file whose name is specified as a command-line argument. The open
method of Kernel
opens a file and returns an object whose gets
method
can be used to read lines. Example:
$ cat a8/fileio.rb f = open(ARGV[0]) # first command-line argument count = 0 while line = f.gets count += 1 end f.close puts "read #{count} lines" $ ruby a8/fileio.rb a8/presidents.fc read 45 linesAlternatively, you could use
f.readlines
to produce an array of all the lines in the file with a single call.
Here's a quick test in irb
:
>> open("a8/presidents.fc").readlines.size => 45
Here's a program that repeatedly prompts for a line and then prints the number of characters read:
$ cat a8/echoinput.rb while true do print("Line? ") line = gets break if not line puts "read #{line.size} characters" endUsage:
$ ruby a8/echoinput.rb Line? just read 5 characters Line? testing read 8 characters Line? this read 5 characters Line? ^DNote what we get when redirect those sames lines from a file:
$ cat a8/lines just testing this $ ruby a8/echoinput.rb < a8/lines Line? read 5 characters Line? read 8 characters Line? read 5 characters Line? $We see the output, but not the input. When trying to understand a diff, it's good to be able to see the input, too!
We can use STDIN
's tty?
method to see if we're reading from the keyboard, which years ago were
keyboards on
teletypes ("ttys").
The following Ruby invocations demonstrate that STDIN.tty?
returns true
iff standard input is
the keyboard:
$ ruby -e 'puts STDIN.tty?' true $ ruby -e 'puts STDIN.tty?' < /dev/null false $ echo testing | ruby -e 'puts STDIN.tty?' falseYou might try similar experiments with
STDOUT.tty?
.
Let's put STDIN.tty?
into use with echoinput2.rb
:
$ cat a8/echoinput2.rb while true do print("Line? ") line = gets break if not line # # If we're *not* reading input from the console, "echo" # the line we just read. # puts "\nInput: #{line.inspect}" if not STDIN.tty? puts "read #{line.size} characters" end $ ruby a8/echoinput2.rb < a8/lines Line? Input: "just\n" read 5 characters Line? Input: "testing\n" read 8 characters Line? Input: "this\n" read 5 characters Line? $You'll need to use
STDIN.tty?
in your flash.rb
to produce similar behavior when standard input
is redirected, as the tester will do. Example:
$ cat a8/f.pres.1 Washington 2 Jefferson // Monroe /u $ ruby flash.rb -mix -n -r 1-6 a8/presidents.fc < a8/f.pres.1 1? Response: 'Washington' Correct! Adams? Response: '2' Correct! 3? Response: 'Jefferson' Correct! Madison? Response: '//' Answer: 4 5? Response: 'Monroe' Correct! JQ Adams? Response: '/u' Done! 4 correct in 5 guesses = 80.0% Never answered: 4: Madison 6: JQ Adams $
Extra credit opportunity: For one point, create a flash.rb
input file for some topic that
you find interesting.
Name it flash-extra.txt
.
I'll post a compilation of them via Piazza, so don't submit anything you don't want everybody to see.
hudak.txt
In The Haskell School of Expression the late Paul Hudak wrote,
"The best news is that Haskell's type system will tell you if your program is well-typed before you run it. This is a big advantage because most programming errors are manifested as typing errors."Do you agree or disagree with his claim that "most programming errors are manifested as typing errors"? For this problem you are to present an argument based on your full experience as a programmer that either supports his claim or refutes it. (Do not argue both sides.) Take all your programming experience into account, not just 372.
IMPORTANT: By "typing errors" Hudak means type-consistency errors like take "testing" 5
in Haskell
or [1,2,3].join("-")
in Python, NOT typographical errors like a missing comma or mismatched parentheses!
As usual, I'm looking for quality, not quantity but as a ballpark figure I imagine 100-200 words, as reported by
"wc -w hudak.txt
", will be needed for a thoughtful answer.
As usual, the .txt
suffix on hudak.txt
indicates that I'm wanting a plain
text file, not a Word document, PDF, etc.
longest.rb
Note: In several past semesters I've started the first Ruby assignment with this problem but I've seen too many students sink too much time into it, some of them becoming extremely frustrated. I think it's a fun problem but I've eliminated the burden of it by making it worth zero points. Try it if you wish!
Write a Ruby program that reads lines from standard input and upon end of file writes the longest line to standard output. If there are ties for the longest line, longest writes out all the lines that tie. If there is no input, longest produces no output.
Don't overlook the restrictions mentioned below.
Here are some examples.
$ cat a8/lg.1 a test for the program here $ ruby longest.rb < a8/lg.1 the program $ cat a8/lg.2 xx a yy b zz $ ruby longest.rb < a8/lg.2 xx yy zzLet's use the null device and "
wc -c
" to demonstrate that if longest.rb
has no input,
there's no output:
$ ruby longest.rb < /dev/null | wc -c 0Let's use
longest.rb
and some grep
s to explore a list of words:
$ ruby longest.rb < /usr/share/dict/words electroencephalograph's $ grep ^q /usr/share/dict/words | ruby longest.rb quadrilateral's quadruplicate's quadruplicating qualification's quartermaster's questionnaire's $ grep ^w /usr/share/dict/words | ruby longest.rb whatchamacallit's wrongheadedness's $ grep ^wo /usr/share/dict/words | ruby longest.rb woolgathering's worthlessness'sRestrictions for
longest.rb
:
<
, ==
, !=
, <=>
,
between?
, eql?
, empty?
, and String.casecmp
may be used.
As a rule, any method that ends with ?
should be viewed with suspicion. Additionally, any kind of sorting
most likely involves comparisons.
case
statement may not be used.
Integer
, String,
along
with the values nil
, true
, and false
(i.e., the values of the single-value
classes NilClass
, TrueClass
, and FalseClass
). In particular, you may not use arrays.
Important: Regarding comparisons, you are permitted to employ the comparison that's implicit in control structures. For example, statements like
while x do ... # OK if f(x) then ... # OKare permitted.
Like for seqwords
, you'll need a line at the top of your longest.rb
that loads a checker
(lobotimizer?) that disables a number of prohibited operations:
load "a8/check-longest.rb"
jlongest.java
This problem is another zero-pointer. It is simply a Java version of longest.rb
, with an
analogous set of restrictions: no comparisons, no sorting, no arithmetic, only int
, boolean
,
and String
values, etc. You may use Scanner
to read lines.
(Use new Scanner(System.in)
to create an instance of Scanner
to read from standard input.)
If you've never used Java on the command line, note that javac
runs the Java compiler, producing a
.class
file. The .class
file can then be run with the java
command:
$ javac jlongest.java $ ls -l jlongest.class -rw-rw---- 1 whm whm 1161 Mar 3 05:10 jlongest.class $ java jlongest < a8/lg.1 the programCompilation and execution can be combined with Bash's
&&
operator:
$ javac jlongest.java && java jlongest < a8/lg.2 xx yy zzThe
&&
directs Bash to run "java jlongest
" iff compilation of jlongest.java
is
successful.
observations.txt
Submit a plain text file named observations.txt
with...
(a) (1 point extra credit) An estimate of how many hours it took you to complete this assignment. Put that estimate on a line by itself, like this:
Hours: 3.5There should be only one
"Hours:"
line in observations.txt
. (It's fine if you care to provide per-problem times, and that data is useful to us, but report it in some form of your own invention that doesn't contain the string "Hours:"
. Thanks!)
Feedback and comments about the assignment are welcome, too. Was it too long, too hard, too detailed? Speak up! I appreciate all feedback, favorable or not.
(b) (1-3 points extra credit) Cite an interesting course-related observation (or observations) that you made while working on the assignment. The observation should have at least a little bit of depth. Think of me thinking "Good!" as one point, "Excellent!" as two points, and "Wow!" as three points. I'm looking for quality, not quantity.
Use a8/turnin
to submit your work. You can run a8/turnin
as often as you want. We'll grade your final pre-deadline submission.
Remember a8/turnin
can also serve as a simple backup/snapshot facility. Don't hesitate to use it for such!
Use a8/turnin
to submit your work. Each run creates a time-stamped "tar file" in your current directory with a name like aN.YYYYMMDD.HHMMSS.tz
.
You can run a8/turnin
as often as you want. We'll grade your final pre-deadine submission.
a8/turnin -l
shows your submissions.
$ wc $(grep -v txt a8/delivs) # this command should work for you, too 19 47 295 seqwords.rb 60 150 1272 xfield.rb 114 291 2678 flash.rb 13 36 237 longest.rb 31 77 818 jlongest.java 237 601 5300 totalMy code has few comments.
Point values of problems correspond closely to the "assignment points" mentioned in the syllabus. For example, a 10-point problem would correspond to about 1% of your final grade in the course.
If you're worried about whether a solution meets the restrictions, mail it to 372f22
—we'll be happy to look it over. But don't wait too long; there may be a crunch at the end.
Feel free to use comments as you see fit, but no comments are required in your code.
In Ruby, #
is comment to end of line, unless in a string literal or regular expression.
There's no Ruby analog for /* ... */
in Java and {- ... -}
in Haskell but
you can comment out multiple lines by making them an embedded document—lines bracketed
with =begin/=end
starting in column 1. Example:
=begin Just some comments here. =endSilly-looking, huh? I agree! (It looks like a construction that escaped from the 1960s.)
Remember that late assignments are not accepted and that there are no late days; but if circumstances beyond your control interfere with your work on this assignment, there may be grounds for an extension. See the syllabus for details.
My estimate is that it will take a typical CS junior 5-7 hours to complete this assignment.
Our goal is that everybody gets 100% on this assignment AND gets it done in an amount of time that is reasonable for them.
Assignments are not take-home tests! We hope you'll make use of Piazza, email, Discord and office hours if problems arise. If you're getting toward twelve hours into this assignment and don't seem to be close to completing it, or you're simply worried about your "slope", email us! Give us a chance to speed you up!