Page 1 of 1

string::concat vs string::write

Posted: 21 Sep 2015 8:35
by Peter Muraya
Hi,
I often need to concatenate a number of strings, say, X,Y and Z. Which is the better practice than the other. And why?

Code: Select all

string::concat(X,Y,Z)
or

Code: Select all

string::write(X,Y,Z)  

Posted: 22 Sep 2015 7:25
by Ferenc Nagy
Peter,
string::write(X,Y,Z) works even if one of X, Y or Z is not string but a number.
The limit of count of X,Y,Z parameters is smaller than the allowed count of parameters for string::concat.
string::concat(X,Y,Z) works only for strings.
The result will be the same strange "eksvaluewayvaluezedvalue".
I do not recommend you any of the above.
I prefer
1) string::concatwithdelimiter([X,Y,Z]," ") which gives
"eksvalue wayvalue zedvalue".
2) string::format("X=%, Y=%, Z=%.",X,Y,Z).
3) If you add field widths and define monospace font for your output window then you get nice columns:

Code: Select all

string::format("X=%-20.20s, Y=%-20.20s, Z=%-20.20s.",X,Y,Z).

Posted: 22 Sep 2015 8:41
by Peter Muraya
Thank you Frank. You have added 2 other options that I also use occasionally. Is there any memory-use/execution-speed advantage of any one over the others? Or are all they based on a similar internal coding model?

Posted: 22 Sep 2015 10:29
by Thomas Linder Puls
There is no significant performance difference between the two approaches.

Both these methods can however give a quite significant overhead if used extensively. The problem is that both these approaches will copy all the strings into a new string.

This simple program:

Code: Select all

clauses     run() :-         N = 1000,         S = list1(N),         stdio::write(S).   class predicates     list1 : (unsigned N) -> string Tree. clauses     list1(0) = "0" :-         !.       list1(N) = T2 :-         T1 = list1(N-1),         T2 = string::format("%, %", T1, N).
It creates a string of the form "0, 1, 2, ..., 1000". But doing so it creates 1000 strings of increasing length each time copying to previous string into the new one. As result the algorithm is O(N^2).

The "right" way to do this is by using a stream instead of creating strings on the fly:

Code: Select all

class predicates     list2 : (unsigned N) -> string Tree. clauses     list2(N) = S:getString() :-         S = outputStream_string::new(),         list2b(S, N).   class predicates     list2b : (outputStream S, unsigned N). clauses     list2b(S, 0) :-         !,         S:write("0").       list2b(S, N) :-         list2b(S, N - 1),         S:writef(", %", N).
To see that this really matters I have run the following little test:

Code: Select all

clauses     run() :-         profileTime::init(),         foreach N in [100, 1000, 10000] do             C1 = convert(profileTime::costName, string::format("list1(%)", N)),             profileTime::start_pr(C1),             _S1 = list1(N),             profileTime::stop_pr(C1),             C2 = convert(profileTime::costName, string::format("list2(%)", N)),             profileTime::start_pr(C2),             _S2 = list2(N),             profileTime::stop_pr(C2)         end foreach,         profileTime::printAndReset().
Which gives results as shown in the image. I guess it is obvious that list1 have a problem compared to list2.

Posted: 22 Sep 2015 17:03
by Peter Muraya
Thomas,
Thanks. The message is that for intensive string work, of the following 5 string joining functions, the last one is better than all the others in terms of performance because it does not make and keep string copies. For less intensive work, the other alternatives may be much neater to use than the outputstream_str library. Is this a correct summary?

Code: Select all

string::concat string::write string::format sting::concatWithDelimeter outputstream_str::write, writef

Posted: 22 Sep 2015 21:52
by Thomas Linder Puls
Yes, that sounds fine.