/** An instance of this class maintains a list of words that it was "told" to ** remember. Once this list includes as many words as the object's capacity, ** remembering a new word results in the "oldest" word being "forgotten". ** ** The underlying data representation is an array of Strings. ** ** @author R. McCloskey ** @version 10-30-2009 */ public class ShortTermMemoryViaAry { // symbolic constants // ------------------------------------------------------------- private static final int DEFAULT_CAPACITY = 1; private static final String emptyString = ""; private static final char SPACE = ' '; // instance variables // -------------------------------------------------------------- private int capacity; // max # of words that can be remembered private int numWordsInMem; // # of words currently in memory private String[] words; // holds (references to) all the remembered // words // constructors // ------------------------------------------------------------ /** Initializes a new object to have the capacity to remember a ** number of words equal to the default capacity. ** (Initially there are no words in its memory, of course.) */ public ShortTermMemoryViaAry() { this(DEFAULT_CAPACITY); // call the other constructor } /** Initializes a new object to have the capacity to remember the ** specified number of words. (Initially there are no words in its ** memory, of course.) */ public ShortTermMemoryViaAry(int capacity) { this.capacity = capacity; // need to use 'this' here because the arg's // name is same as instance var's name numWordsInMem = 0; int aryLen = Math.max(1, capacity+1); // compute initial array length words = new String[aryLen]; // actually, we could just as well begin } // with a smaller array, as it grows when // needed to accommodate newly-added // words (see insertNewestWord()) // observers // ------------------------------------------------------------------- /** Returns the maximum number of words that can be in memory. */ public int capacityOf() { return capacity; } /** Returns the number of words currently in memory. */ public int numWordsInMemory() { return numWordsInMem; } /** Returns true iff the specified string is among the words currently ** in memory. */ public boolean isInMemory(String s) { return locOf(s) != numWordsInMem; } /* Returns a String object containing the k-th word in memory, where ** counting begins at zero with the oldest word. If k fails to satisfy ** 0 <= k < numWordsInMemory(), the empty string is returned. */ public String getWord(int k) { String result; if (0 <= k && k < numWordsInMem) { result = words[k]; } else { result = emptyString; } return result; } /** Returns a String containing all the words in memory (from least to ** most recently remembered), each one separated from the next by a ** single space. */ public String getAll() { String result; if (numWordsInMem == 0) { result = emptyString; } else { result = words[0]; int i = 1; while (i != numWordsInMemory()) { result = result + SPACE + words[i]; i = i+1; } } return result; } // mutators // --------------------------------------------------------------- /** If the specified string (newWord) is not a valid word, there is ** no effect. Nor is there any effect if the specified string is a ** word already among those remembered. ** Otherwise, the specified string becomes the "most recently" ** remembered word, and, if memory is already at its capacity, the ** oldest remembered word is "forgotten". */ public void remember(String newWord) { if (!isValidWord(newWord)) { // do nothing, as newWord includes a space } else if (isInMemory(newWord)) { // do nothing, as newWord is already in memory } else if (capacityOf() == 0) { // this else-if clause is not // do nothing, as capacity is zero // necessary, but speeds things } // up if capacity is zero else { rememberAux(newWord); // put new word into memory and remove } // oldest one if capacity is exceeded } /* Auxiliary to remember(), this method inserts a new word and then, ** if capacity is exceeded, removes the oldest word. */ private void rememberAux(String newWord) { insertNewestWord(newWord); if (numWordsInMemory() > capacityOf()) { removeOldestWord(); } } /** If the specified string (newWord) is not a valid word, there is ** no effect. Otherwise, the specified word becomes the most recently ** remembered word. If the specified word had already been among those ** remembered, its "older" copy is removed (and hence the oldest word ** in memory is not forgotten). If the specified word had not been ** among those already remembered and memory is already at its capacity, ** the oldest remembered word is forgotten. */ public void rememberReplace(String newWord) { if (!isValidWord(newWord)) { // do nothing, as newWord is not a valid word } else { int loc = locOf(newWord); if (loc != numWordsInMem) { // If newWord is already in memory, removeWord(loc); // remove it. } rememberAux(newWord); // remember newWord } } /** Changes the object's capacity to that specified, or to zero if the ** number specified is negative. ** If the new capacity is exceeded by the number of words in memory, ** as many "old" words are forgotten as is necessary to make the number ** of words in memory equal to the new capacity. */ public void setCapacity(int newCapacity) { capacity = Math.max(newCapacity,0); while (numWordsInMemory() > capacity) { removeOldestWord(); } } // private methods // ---------------------------------------------------------------- /* Reports whether the given string is a valid word, which simply ** requires that it not include any spaces. */ private boolean isValidWord(String s) { return s.indexOf(SPACE, 0) == -1; } /* Returns the location, within words[], at which there is a reference ** to the word s. (As a precondition, s must be a valid word.) ** If s is not a word in memory, the value returned is numWordsInMemory(). */ private int locOf(String s) { int i = 0; while (i != numWordsInMem && !s.equals(words[i])) { i = i+1; } return i; } /* Appends the specified word onto the "end" of memory. */ private void insertNewestWord(String newWord) { insertWord(numWordsInMem, newWord); } /* Inserts the specified word at the specified location in memory, ** so that any words currently at that location or "to the right" of ** of it are shifted one position "to the right". ** (Note: Given that words are always put at the end of memory (and ** thus calls to this method always pass numWordsInMem as the first ** argument), this method has greater capability than needed. However, ** this extra capability would be useful if it were ever decided to modify ** the class by putting words in order from newest to oldest, or to allow ** words to be inserted in the "middle" of memory. End of note.) ** ** As a pre-condition, 0 <= loc <= numWordsInMemory() */ private void insertWord(int loc, String s) { if (numWordsInMem == words.length) { // if words[] is "full", create a new array that is twice as long, // copy the elements of words[] into it, and then make words refer // to the new array String[] newWords = new String[2 * words.length]; System.arraycopy(words, 0, newWords, 0, numWordsInMem); words = newWords; } // shift the words in words[loc..numWordsInMem-1] to the "right" // one place so as to make room for the new word int i = numWordsInMem; while (i != loc) { words[i] = words[i-1]; // shift element i-1 into location i i = i-1; } // Now put new word into the specified location words[loc] = s; numWordsInMem = numWordsInMem + 1; } /* Chops off the oldest word in memory (which is that referred to by ** the zero-th element of words[]). */ private void removeOldestWord() { removeWord(0); } /* Removes element k of words[] by shifting all subsequent elements ** one place "to the left". As a precondition, must have ** 0 <= k < numWordsInMemory(). */ private void removeWord(int k) { int i = k; while (i != numWordsInMem - 1) { words[i] = words[i+1]; // shift element i+1 into location i i = i+1; } numWordsInMem = numWordsInMem - 1; } }