/** 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 a string containing all the ** words in memory, from oldest to newest, each preceded and followed ** by a single space. ** ** @author R. McCloskey ** @version 10-28-2009 (major improvement to the 10-09-2009 version) */ public class ShortTermMemory { // 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 memory; // holds all the remembered words, each one // preceded and followed by a single space, // in order from least to most recent private StringUtility strUtil; // for utility methods on String objects // 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 ShortTermMemory() { 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 ShortTermMemory(int capacity) { this.capacity = capacity; // need to use 'this' here because the arg's // name is same as instance var's name numWordsInMem = 0; memory = emptyString + SPACE; // initial value (a single space) is vital! strUtil = new StringUtility(); } // 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) != 0; } /* 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 < numWordsInMemory()) { int begLoc = locOfKthWord(k); int endLoc = strUtil.positionOf(memory, SPACE, begLoc); result = memory.substring(begLoc, endLoc); } 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 (numWordsInMemory() == 0) { result = emptyString; } else { // use substring() to exclude the leading & trailing space from result result = memory.substring(1, memory.length()-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(), it 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 begLoc = locOf(newWord); if (begLoc != 0) { // If newWord is already in memory, removeWord(begLoc); // 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 strUtil.positionOf(s, SPACE, 0) == s.length(); /// an alternative method body is as follows: //return s.indexOf(SPACE, 0) == -1; } /* Returns the location, within memory, at which the word s begins. ** (As a precondition, s must be a valid word.) ** If s is not a word in memory, the value returned is 0. (Note that ** a word cannot begin there because it is necessarily a space.) */ private int locOf(String s) { return memory.indexOf(SPACE + s + SPACE) + 1; } /* Returns the location, within memory, at which the k-th word begins. ** k must satisfy 0 <= k < numWordsInMemory(). */ private int locOfKthWord(int k) { int loc = 1; // location at which 0-th word begins int i = 0; // counts words as we pass by them // loop invariant: the i-th word begins at location loc while (i != k) { loc = strUtil.positionOf(memory, SPACE, loc) + 1; i = i + 1; } return loc; } /** Appends the specified word onto the end of memory. */ private void insertNewestWord(String newWord) { insertWord(memory.length(), newWord); } /* Inserts the specified word at the specified location within memory. ** (Note: Given that words are always put at the end of memory (and ** thus calls to this method always pass memory.length() 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 precondition, 0 < begLoc <= memory.length() */ private void insertWord(int begLoc, String s) { memory = memory.substring(0, begLoc) + s + SPACE + memory.substring(begLoc); numWordsInMem = numWordsInMem + 1; /// debugging code: //System.out.println("After inserting word at location " + begLoc + // ", memory = $" + memory + "$"); } /* Chops off the oldest word in memory (which begins at location 1). */ private void removeOldestWord() { removeWord(1); } /* Removes from memory the word that begins at the specified location. ** As a precondition, there must actually be a word that begins there, ** which is to say that memory.charAt(begLoc) is not a space and ** memory.charAt(begLoc-1) is a space. */ private void removeWord(int begLoc) { int endLoc = strUtil.positionOf(memory, SPACE, begLoc); memory = memory.substring(0, begLoc) + memory.substring(endLoc+1); numWordsInMem = numWordsInMem - 1; /// debugging code: //System.out.println("After removing word at location " + begLoc + // ", memory = $" + memory + "$"); } }