Java代写:COMS227 StringList

Introduction

本次作业是要实现一个StringList,具体是要求练习掌握Interface的编程方法。程序的主干代码已经给出,按照文档补充缺失的部分即可。

Overview

This is a short set of problems using interfaces. You will implement a class called StringList that uses interfaces for performing various kinds of processing on streams of text, as well as some sample classes that implement those interfaces. In all, you’ll implement six classes:

StringList.java
NonCommentLineSelector.java
CommentRemover.java
LetterCollecter.java
LineNumberer.java
LocCounter.java

Note that the file StringListTest.java, found in the default package, will not compile until you have created stubs for the six required classes.
None of the code is very complex; the purpose is just to get you thinking about interfaces a bit.
A StringList (not surprisingly) represents a list of strings. (Most likely, in fact, you’ll use an instance variable of type ArrayList to store the strings themselves.) The interesting part is in the operations map, filter, and reduce. These are based on the four interfaces defined in the package api: Combiner, IntCombiner, Selector, and Transformation. They are all very simple and have only one method each. You can take a look at the javadoc or sample code for more details.

public StringList map(Transformation t)

Returns a new StringList obtained by invoking the given Transformation’s apply method to each string in this StringList. The apply method just takes a string and returns a (possibly different) string.

public StringList filter(Selector selector)

Returns a new StringList containing only the elements of this StringList for which the Selector’s select method returns true. The select method just takes a string and returns true or false.

public String reduce(Combiner combiner, String initialValue)

Returns a string resulting from a reduction operation using the given Combiner and the given initial value. A Combiner has one method, combine, that takes two strings and returns a string. The idea of a “reduction” is to initialize an accumulator variable with the given initial value, and then to iterate over the list, replacing the accumulator value with the result of combining it with the next item in the list.

public int reduce(IntCombiner combiner, int initialValue)

Returns an integer resulting from a reduction operation using the given IntCombiner and the given initial value. Similar to the reduce operation above, but the accumulated value is an int.

The meaning of the reduce operation may not be obvious. The next section of this document includes a detailed explanation and examples. You should also look at the example StringListTest.java. found in the default package of the sample code.
In addition to the StringList class, you’ll implement some examples of classes that implement the interfaces in the api package so you can try things out. These are:

public class NonCommentLineSelector implements Selector

The select method returns true if the given string does not have “//“ as its first nonwhitespace characters.

public class CommentRemover implements Transformation

The apply method returns a new string in which any text following “//“ is removed.

public class LineNumberer implements Transformation

The apply method returns a new string with a line number left-justified in the first five spaces at the beginning of each line. When the LineNumberer is first constructed, the line number starts at 1; thereafter the number increases by 1 in each call to apply.

Tip: to format a number num within a 5-space string, use String.format(“%-5d”, num)

public class LocCounter implements IntCombiner

Given an integer n and a string, the combine method returns n if the string is a comment line, a blank line, or a line whose only text, other than an end-of-line comment, is a single curly brace; otherwise the method returns n + 1. (Using a LocCounter in the reduce method has the general effect of counting “lines of code” that are actual program statements.)

public class LetterCollector implements Combiner

Given two strings first and second, appends onto first all characters in second that don’t already occur in first. (Using a LetterCollector in the reduce method returns a string in which each character occurring in the strings appears exactly once.) Not case sensitive.

Detailed example: the idea of map, filter, and reduce

Here is a related example using integer arrays to illustrate the main idea of the map, filter, and reduce operations. Suppose we have a loop that iterates over an int array and returns a new array, after applying some operation to each number. For example, here is a loop that multiplies each value in the array by 2:

1
2
3
4
5
6
7
public int[] multiplyEverythingByTwo(int[] arr) {
int[] result = new int[arr.length];
for (int i = 0; i < arr.length; i += 1) {
result[i] = 2 * arr[i];
}
return result;
}

And here is a loop that rounds each value to the nearest multiple of ten:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public int[] roundEverythingToNearestTen(int[] arr) {
int[] result = new int[arr.length];
for (int i = 0; i < arr.length; i += 1) {
int tens = arr[i] / 10;
int rem = arr[i] % 10;
if (rem < 5) {
result[i] = tens;
} else {
// round up
result[i] = tens + 1;
}
}
return result;
}

The basic structure of both methods is exactly the same: for each number in the array, get a new number by applying some transformation. It seems like we should not need two separate methods that are so similar. The method that iterates over the array does not care what the transformation is, it just needs to apply it to every element.
We can use an interface to represent the transformation:

1
2
3
interface IntTransformation {
int apply(int num);
}

Then a general method that applies the transformation to each element of the array is easy:

1
2
3
4
5
6
7
public int[] applyTransformation(int[] arr, IntTransformation t) {
int[] result = new int[arr.length];
for (int i = 0; i < arr.length; i += 1) {
result[i] = t.apply(arr[i]);
}
return result;
}

Now if we want to multiply each element by 2, we just define a class implementing the IntTransformation interface whose apply method does so:

1
2
3
4
5
6
class MultiplyByTwo implements IntTransformation {
@Override
public int apply(int num) {
return 2 * num;
}
}

Then call applyTransformation on an array myArray using a MultiplyByTwo object:

1
int[] answer = applyTransformation(myArray, new MultiplyByTwo());

An operation like the applyTransformation is referred to as a map operation. Some other common patterns are filter and reduce. A filter operation would be something like “return an array containing just the even numbers”. It is based on a method that takes an integer and returns a boolean - if the method returns true, the value is included in the result.
A reduce operation is a bit more interesting. To see what it means, think about at the operation of adding up the elements:

1
2
3
4
5
6
7
public int sum(int[] arr) {
int total = 0;
for (int i = 0; i < arr.length; i += 1) {
total = total + arr[i];
}
return total;
}

and then look at the similar code for finding the maximum:

1
2
3
4
5
6
7
8
9
public int findMax(int[] arr) {
int max = Integer.MIN_VALUE;
for (int i = 0; i < arr.length; i += 1) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}

What is happening in both cases is that we start with a specific initial value (like the total in the sum() method), and then go through the array, updating that value by combining it with the first element in the array, then the second element, and so on. In the case of the sum method, we “combine” two values using addition. In the case of the findMax method, we “combine” two values by taking the largest one.
As we saw for int-to-int transformations, we can define a simple interface representing the “combine” operation:

1
2
3
interface Combiner {
int combine(int first, int second);
}

Then the general method for performing any such operation is:

1
2
3
4
5
6
7
public int reduce(int[] arr, Combiner combiner, int initialValue) {
int result = initialValue;
for (int i = 0; i < arr.length; i += 1) {
result = combiner.combine(result, arr[i]);
}
return result;
}

This type of operation is often called “reduce,” since it take a collection of values and “reduces” them to a single value such as a sum or maximum. Note that we generally have to provide the desired default or initial value.

The SpecChecker

There is a simple SpecChecker that will check basic spec conformance of class and method declarations. A second SpecChecker will be posted by Tuesday, April 11. The second one will perform some functional tests and create a zip file.
In the meantime think about how you could test your code yourself. Take a look at StringListTest.java in the default package of the sample code for some ideas. Notice that it is easy to write simple test cases for a class such as (say) CommentRemover, even if you do not have StringList implemented. For example:

1
2
3
CommentRemover c = new CommentRemover();
String s = c.apply("before comment// after comment");
System.out.println(s); // expected "before comment"

Documentation and style

Since this is a miniassignment, the grading is automated and in most cases we will not be reading your code. Therefore, there are no specific documentation and style requirements.