views:

644

answers:

12

I am writing a mad libs program for fun and to just program something. The program itself is pretty straight forward, but I find myself resorting to several concatenations due to the nature of the game where you place user given words into a sentence. Is there an elegant work around to resort to less concatenations or even eliminate them for something more efficient? I know in the end it will make no difference if I use concatenations or not, but I am curious if there is a more elegant way to write this program.

Update: I am using java, but if there is a general solution to escaping concatenation that would be appreciated too.

+2  A: 

Which language are you using?

Most high level langauges will have something similar to:

String.Format("{0} {1} {2} {3}", word1, word2, word3, word4);
sipwiz
As you might guess this will just hide the concatenation, not avoid them.
sharptooth
Is C high level now? I kid but considering the importance of printf and c as middle level language...
Roman A. Taycher
+1  A: 

There're two aspects of your question.

First, you might wish to avoid using concatenations directly in your code. Then you can use some string-formatting routine from the runtime. This won't skip concatenations, but will move them from your code to the runtime.

Second, you may want to do concatenations more efficently. That's another story. The most important thing here is to preallocate the big enough buffer for the concatenated string, since memory reallocations are quite expensive. Copying substrings into the result strings are usually less expensive and are a necessary evil.

sharptooth
Allocating a big enough buffer for madlibs seems impossible since the user can supply a word of any size.
Anton
Well, that might happen too, but you can have some guess of what the length limit is with say 97 % probability. This will make your inplementation very efficient for most cases and reasonably efficient for all other cases.
sharptooth
It's possible to avoid in-memory concatenation entirely if separate parts of the string are written into output stream sequentially.
Totophil
A: 

By 'avoiding concatenation' I assume you mean allocating the space for the new string and assigning it? If you're on .NET then a StringBuilder should help some I think.

fung
+7  A: 

One solution might be to write out your whole mad libs file and put in special tokens that need to be replaced by the words that are chosen.

You just bought a brand new ${NOUN1}, and it is going to ${VERB1} you.

Then you can use for example: String.replace("${NOUN1}", noun1) for all the words.

Andy White
Just a note, I think using named tokens might be easier to maintain than using the normal String.format tokens like %s, %d
Andy White
Won't tokens also result in the same number of concatenations?
Anton
Yeah, it's still going to concatenate behind the scenes. I'm not sure there's really a clean way to avoid that without getting down to string allocation/manipulations. But maybe that's what you're wanting to mess with! Sorry
Andy White
+1 for resulting in more readable code than a StringBuilder/Buffer
basszero
This does do string concatenation. The key not to do in memory string concatenation is to output string parts sequentially.
Totophil
+1  A: 

Nothing comes to my mind in terms of a general way to avoid concatenation. What you could do is to write yourself some kind of helper class which simplifies the job of concatenation for you. A suggestion I would like to give you however is to not directly concatenate strings as

String x = "Hi";
String y = x + " there!";

The implementation of string concatenation is quite slow in Java, so it's a better practice to use StringBuffer instead, especially if you do a lot of concatenations:

StringBuffer myStringBuffer = new StringBuffer();
myStringBuffer.append("Hi");
myStringBuffer.append(" there!");

Something like this. I didn't check this now in a compiler, but I'm sure you can figure it out yourself.

Juri
-1 - A modern Java compiler will compile a simple sequence of string concatenations as an equivalent sequence of StringBuffer operations.
Stephen C
Hmm...normally it is always suggested to use StringBuffer or StringBuilder (in C#) for doing String concatenations. I don't have enough background knowledge on Java compilers. You may be right, but what about J2ME environments? Same Java syntax, but different practices. There you should absolutely not do string concatenations. Therefore generally I'd say that my "rule" still holds.
Juri
+1  A: 

Not sure about Java but in some other languages such as PHP and Javascript creating an array and joining all of its elements can be faster.

Javascript example:

var str = str1 + str2 + str3 + "str4" + str5 + "str6" + str7;
var str = [str1, str2, str3, "str4", str5, "str6", str7].join("");

PHP example:

$str = $str1 . $str2 . $str3 . "str4" . $str5 . "str6" . $str7;
$str = implode("", array($str1, $str2, $str3, "str4", $str5, "str6", $str7));

This method is best if you want to put a delimiter between each string in which case it will not only be faster but more compact and readable.

presario
Bizarre as it may seem, Java has no such functionality built in; it will probably be included (finally!) in Java 7. For now, it's either concatenation, StringBuilder, or a third-party library.
Alan Moore
Might do string concatenation behind the scenes, or comparable amount of work otherwise.
Totophil
A: 

The solution I came up with after seeing everyone else's response is that in general the concatenations cannot be avoided without knowing the size of the final string. So after I receive the replacement words from the user then determine the final size of the string I will create a StringBuilder of that size. This will allow me to append all the other strings without Java internally recreating the StringBuilder. In addition this avoids making unnecessary strings that Java normally creates since Java's append actually creates a string buffer and new string for each "+" operator.

PS. Thank you mmyers for letting me know that StringBuilder is slightly faster than StringBuffer.

PSS. I noticed I got too nitpicky about optimization because it was fun and that my solution most definitely does not answer my original question mostly because its not elegant.

Anton
StringBuilder is usually a little faster than StringBuffer, if you're looking for speed.
Michael Myers
pre-mature optimization city! Token replacement seems cleaner
basszero
Yeah I didn't really go for cleanness since that seemed simpler to me than "pre-mature optimization". This was more a learning goal to find out the "best way" to do it in java. Also thank you mmyers for the StringBuffer advice.
Anton
Just remember that what you really want is what LOOKS the cleanest. If using + looks better than StringBuffer, to not use + is bad programming unless using it over SB actually makes your program pass some benchmark it couldn't pass otherwise.
Bill K
@Anton: What Bill K said. I personally use + until it starts looking like a problem; If you're not doing it in a loop, it really doesn't make enough difference either way to worry about it. I hope I didn't throw you off track by mentioning that something runs a little faster than something else.
Michael Myers
+2  A: 

There are two ways I can see you entirely avoiding concatenation in your game:

  1. Store the story template as as a collection of tokens: immutable parts and word placeholders. Then loop through the collection outputting immutable parts and user words instead of word placeholders.

  2. Write custom print class that will loop through the template string using charAt and output words instead of word placeholders.

Totophil
+2  A: 

Rather than calling String.replace for each word or constantly appending to a StringBuilder, you could put the words in an ordered array and use String.format ( in jdk 1.5 or newer ) or MessageFormat.format in jdk 1.4. Unfortunately the pattern formats for the two are different.

String myPattern = "My %s eats your %s";
// get values from the user here
String result = String.format( myPattern, (Object[])myArray );

or

String myPattern = "My {0} eats your {1}";
// get values from the user here
String result = MessageFormat.format( myPattern, (Object[])myArray );

Here is a complete program that fills in the string with values from the command line.

public class Format
{
    public static void main( String[] args )
    {
        String pattern = "My %s eats your %s";
        System.out.println( String.format( pattern, (Object[])args ));
    }
}
digitaljoel
Both String.format and MessageFormat concatenate strings.
Totophil
A: 

If you have a String an need to modify it very often, using a char[] array and creating a String when done modifying it might do the trick.

Helper Method
A: 

It's certainly possible to do this without concatenation if you want to. Although it's probably not very worthwhile.

It partly depends on where you're going to display the result. If it's on the web or the console, then you can write the result directly to an output stream.

So for example, if your template was something like "the %001 jumped out of the %002" you could:

1) scan the string to find where the variables are. 2) break the string into a list of components "the","jumped out of the", in this case 3) loop through the elements of your list and the results and dump them to output with something like this:

PrintStream  = //get output from somewhere
for(int i = 0; i < pieces.size()-1 ; i++){
   ps.print(peices.get(i));
   ps.print(answers.get(i));
}
ps.print(pieces.get(pieces.size()-1));//last one

Now. I don't know if you would call that 'elegant' or not. It would probably be kind of fast. Like I said, maybe appropriate if this was a web service and it became super popular, etc.

If this is a GUI app, you could write code to draw the strings separately, but that would probably take more time then concatenation.

but one thing you could do is use a ByteArrayOutputStream and pre-allocate enough space so that it could contain the filled out madlib without making additional allocations. That would be pretty quick as well.

Chad Okere
A: 

Why do you want to avoid concatenation if its for cleanliness try lining it up nicely and it may end up looking surprisingly good.Even if you are worried about performance you may not need to use string builder, I believe that if you do a bunch of concatenations simply(ie "fdsfsd" + var1 +var2 +"dsfsdf" +"tyhty" it compiles to the same thing as string builder(effectively the same and looks cleaner). String Builder does make sense if you are concating a ton of stuff in a loop or with a complex flow.

Roman A. Taycher