4: Strings

We start to delve into algorithms and basic AI with our first CollegeBoard-issued lab. We will learn about String manipulation so we can have a conversation with our computer.

Learning Targets

  • I can use common String methods to modify a string.

  • I can evaluate the contents of a String.

  • I can use a Scanner to take inputs.

  • I can isolate an object in the user's input and use it in Magpie's response.

Starting the Magpie Lab

What's a magpie?

Check out the chatbot in Activity 1:

Now let's set up our project in VS Code:

Bring up the command menu with CMD+Shift+P

Name the project Magpie.

GitHub Setup

Open the GitHub Desktop app and Add a Local Repository. Don't create a new repo because the app forces the creation of a new folder. If you use the "create a repository" link it will use the existing folder made by VS Code.

Add a local repo and then click the "create a repository" and this avoids making a new folder

Use the Java git ignore settings. Once it's up, push the repo. Then select the Repository menu and View on GitHub. Send me the link in Slack.

Activity 2 Starter Code

Now we're going to add a few new classes to the project.

Magpie.java

MagpieRunner.java
Magpie.java
MagpieRunner.java
import java.util.Scanner;
/**
* A simple class to run the Magpie class.
* @author Laurie White
* @version April 2012
*/
public class MagpieRunner
{
/**
* Create a Magpie, give it user input, and print its replies.
*/
public static void main(String[] args)
{
Magpie maggie = new Magpie();
System.out.println (maggie.getGreeting());
Scanner in = new Scanner (System.in);
String statement = in.nextLine();
while (!statement.equals("Bye"))
{
System.out.println (maggie.getResponse(statement));
statement = in.nextLine();
}
}
}
Magpie.java
/**
* A program to carry on conversations with a human user.
* This is the initial version that:
* <ul><li>
* Uses indexOf to find strings
* </li><li>
* Handles responding to simple words and phrases
* </li></ul>
* This version uses a nested if to handle default responses.
* @author Laurie White
* @version April 2012
*/
public class Magpie
{
/**
* Get a default greeting
* @return a greeting
*/
public String getGreeting()
{
return "Hello, let's talk.";
}
/**
* Gives a response to a user statement
*
* @param statement
* the user statement
* @return a response based on the rules given
*/
public String getResponse(String statement)
{
String response = "";
if (statement.indexOf("no") >= 0)
{
response = "Why so negative?";
}
else if (statement.indexOf("mother") >= 0
|| statement.indexOf("father") >= 0
|| statement.indexOf("sister") >= 0
|| statement.indexOf("brother") >= 0)
{
response = "Tell me more about your family.";
}
else
{
response = getRandomResponse();
}
return response;
}
/**
* Pick a default response to use if nothing else fits.
* @return a non-committal string
*/
private String getRandomResponse()
{
final int NUMBER_OF_RESPONSES = 4;
double r = Math.random();
int whichResponse = (int)(r * NUMBER_OF_RESPONSES);
String response = "";
if (whichResponse == 0)
{
response = "Interesting, tell me more.";
}
else if (whichResponse == 1)
{
response = "Hmmm.";
}
else if (whichResponse == 2)
{
response = "Do you really think so?";
}
else if (whichResponse == 3)
{
response = "You don't say.";
}
return response;
}
}

Now write a commit message to bookmark these changes and push the new version to GitHub. Then following along with the exercises in Activity 2.

Strings

Check out the official documentation.

Strings are a special data type in Java. They're not primitives but they behave a little differently than most instantiated objects. In many ways, they're an array of primitive char objects. The biggest difference is that Strings also have many helpful methods to change their formatting, to search and examine the Strings, and lots of other helpful tools.

Note: This is not how you should normally create Strings
Dropping in variables like this is called string interpolation. Use that term to impress your friends.

Activity 3: Better method

StringExplorer

Create a new class in our project called StringExplorer and drop this code in:

StringExplorer.java
StringExplorer.java
/**
* A program to allow students to try out different
* String methods.
* @author Laurie White
* @version April 2012
*/
public class StringExplorer
{
public static void main(String[] args)
{
String sample = "The quick brown fox jumped over the lazy dog.";
// Demonstrate the indexOf method.
int position = sample.indexOf("quick");
System.out.println ("sample.indexOf(\"quick\") = " + position);
// Demonstrate the toLowerCase method.
String lowerCase = sample.toLowerCase();
System.out.println ("sample.toLowerCase() = " + lowerCase);
System.out.println ("After toLowerCase(), sample = " + sample);
// Try other methods here:
}
}

Around line 16, let's add some more:

int notFoundPsn = sample.indexOf("slow"); System.out.println("sample.indexOf(\"slow\") = " + notFoundPsn);

Magpie's current structure and use of .indexOf("something") >= 0 is full of logic errors. It's caps sensitive, it can't tell if you've entered no response at all, and it sees the word "no" inside of "know". Let's do better.

Let's drop these two methods into your Magpie class (they're overloaded):

/**
* Search for one word in phrase. The search is not case
* sensitive. This method will check that the given goal
* is not a substring of a longer string (so, for
* example, "I know" does not contain "no").
*
* @param statement the string to search
* @param goal the string to search for
* @param startPos the character of the string to begin the search at
* @return the index of the first occurrence of goal in
* statement or -1 if it's not found
*/
private int findKeyword(String statement, String goal,
int startPos)
{
String phrase = statement.trim().toLowerCase();
goal = goal.toLowerCase();
// The only change to incorporate the startPos is in
// the line below
int psn = phrase.indexOf(goal, startPos);
// Refinement--make sure the goal isn't part of a
// word
while (psn >= 0)
{
// Find the string of length 1 before and after
// the word
String before = " ", after = " ";
if (psn > 0)
{
before = phrase.substring(psn - 1, psn);
}
if (psn + goal.length() < phrase.length())
{
after = phrase.substring(
psn + goal.length(),
psn + goal.length() + 1);
}
// If before and after aren't letters, we've
// found the word
if (((before.compareTo("a") < 0) || (before
.compareTo("z") > 0)) // before is not a
// letter
&& ((after.compareTo("a") < 0) || (after
.compareTo("z") > 0)))
{
return psn;
}
// The last position didn't work, so let's find
// the next, if there is one.
psn = phrase.indexOf(goal, psn + 1);
}
return -1;
}
/**
* Search for one word in phrase. The search is not case
* sensitive. This method will check that the given goal
* is not a substring of a longer string (so, for
* example, "I know" does not contain "no"). The search
* begins at the beginning of the string.
*
* @param statement
* the string to search
* @param goal
* the string to search for
* @return the index of the first occurrence of goal in
* statement or -1 if it's not found
*/
private int findKeyword(String statement, String goal)
{
return findKeyword(statement, goal, 0);
}

Now we've got to update our getResponse method to use this instead of indexOf.

Let's use this chart to walk through what's happening:

Activity 4: Slice and dice

Let's use parts of the user's message in our response. Read the details in the student guide. Before proceeding.

Replace the else in our getResponse method with the following code:

// Responses which require transformations
else if (findKeyword(statement, "I want to", 0) >= 0)
{
response = transformIWantToStatement(statement);
}
else
{
// Look for a two word (you <something> me)
// pattern
int psn = findKeyword(statement, "you", 0);
if (psn >= 0
&& findKeyword(statement, "me", psn) >= 0)
{
response = transformYouMeStatement(statement);
}
else
{
response = getRandomResponse();
}
}

Now below this getResponse method, let's add in a few methods that are being called by the code above.

/**
* Take a statement with "I want to <something>." and transform it into
* "What would it mean to <something>?"
* @param statement the user statement, assumed to contain "I want to"
* @return the transformed statement
*/
private String transformIWantToStatement(String statement)
{
// Remove the final period, if there is one
statement = statement.trim();
String lastChar = statement.substring(statement
.length() - 1);
if (lastChar.equals("."))
{
statement = statement.substring(0, statement
.length() - 1);
}
int psn = findKeyword (statement, "I want to", 0);
String restOfStatement = statement.substring(psn + 9).trim();
return "What would it mean to " + restOfStatement + "?";
}
/**
* Take a statement with "you <something> me" and transform it into
* "What makes you think that I <something> you?"
* @param statement the user statement, assumed to contain "you" followed by "me"
* @return the transformed statement
*/
private String transformYouMeStatement(String statement)
{
// Remove the final period, if there is one
statement = statement.trim();
String lastChar = statement.substring(statement
.length() - 1);
if (lastChar.equals("."))
{
statement = statement.substring(0, statement
.length() - 1);
}
int psnOfYou = findKeyword (statement, "you", 0);
int psnOfMe = findKeyword (statement, "me", psnOfYou + 3);
String restOfStatement = statement.substring(psnOfYou + 3, psnOfMe).trim();
return "What makes you think that I " + restOfStatement + " you?";
}

Okay, now you're ready to follow along the student guide with the class and ask some questions along the way.

Review

Let's review some basics before we move onto our next, advanced concept. Now's the time to hit codingbat, SoloLearn, Codecademy, or other training websites to practice. You want the basics down pat so you can focus on new concepts as we move forward.

Drills.java
Drills.java
public class Drills{
public static void main(String[] args){
// Declare 5 different data types with initial values
// A standard for loop printing a message three times
// A for-each loop traversing a String[array]
// An infinite loop
// a short-circut conditional with four tests
// Break a loop if a conditional passes
// Loop through each char in a String
// Print only the first three letters in “word”
// Print all the odd numbers from 1 - 100
// Create a Scanner and take an input
// Create a Scanner, take a number, and count down from that number to 0
}
// Create a method that returns a comparison (include a JavaDoc comment)
}