<div dir="ltr"><br><div class="gmail_quote">On Wed, Oct 8, 2008 at 11:51 PM, Richard O'Keefe <span dir="ltr"><<a href="mailto:ok@cs.otago.ac.nz">ok@cs.otago.ac.nz</a>></span> wrote:<br><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
I agree that there should be an operation that is<br>
the converse of string:join/2.</blockquote><div> </div><div>*That's* the word I wanted, thanks. <br><br></div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
<br>
I'd like to see the spelling mistake (s/Seperators/Separators/)<br>
fixed in the comment for string:tokens/2.<br>
Can anyone explain to me why<br>
<br>
-spec(tokens/2 :: (string(), string()) -> [[char(),...]]).<br>
<br>
doesn't mention "string()" on the right hand side of the arrow?<br>
<br>
There are three minor quibbles about the proposed<br>
string:split/2.<br>
<br>
(1) We already have lists:split/2 and lists:splitwith/2.<br>
    A module system exists so that names can safely be reused,<br>
    but this is a little too close for comfort.<br>
</blockquote><div><br>Well, split is used in some very widespread languages like Perl, Ruby, and JavaScript. The version I proposed works much like Ruby's but doesn't have all the "kitchen sink" options that are available in Ruby. This being a multi-lingual software world, I decided to go with the Principle of Least Astonishment and do as the Romans do, as it were. I regret  the much earlier naming of lists:split, which is a totally different beast, but I maintain that string:split is the right name. As long as people don't overuse import (which I never use, ever) it should be ok. If you *really* wanted to be perverse, you could call it nioj (only joking, I never understood the attraction of that Algol-68 esac/fi/od stuff).<br>
</div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;"><br>
    While 'unjoin' is uglier than 'split', maybe it would mean<br>
    less confusion?<br>
</blockquote><div><br>Maybe. Yuk.<br> <br></div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;"><br>
(2) What should string:split(Input, "") do?<br>
<br>
    One plausible answer would be to split the input into a<br>
    list of single-character strings.  I know this is what<br>
    Edwin Fine asked for, but is it the _right_ thing to do?</blockquote><div><br>It's what Ruby and JavaScript do, and for all I know, others. That doesn't mean it's *right* but it does mean it's unsurprising for people who have to work across multiple languages that include these and similar ones.<br>
<br></div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;"><br>
    Why is "abc" "" -> ["a","b","c"] the right answer rather<br>
    than ["abc"], for example?  Why is the separator deemed<br>
    to occur only between characters and not at the beginning<br>
    or end, yielding ["","a","b","c",""]?<br>
</blockquote><div><br>Good point. I suppose by convention, really; we are talking about an invisible zero-length string, after all, and there could be an infinite number of them in any string. I think. I am sure you can tell from my "idempotent" gaffe that I'm not a computer scientist by training; I are an enjineer.<br>
<br></div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
    Perhaps the best answer for now is to require the separator<br>
    to be a non-empty list.<br>
</blockquote><div><br>That would work... but could cause exceptions in places where it's not really an obvious error. I know you hate those. Me too.<br><br></div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
<br>
(3) unjoin:unjoin(";;;abc;;de;f;g;;", ";;").<br>
    [[],";abc","de;f;g",[]]<br>
<br>
    Is that the right answer, or should it be<br>
    [";","abc","de;f;g",[]]?<div class="Ih2E3d"></div></blockquote><div><br>Out of interest, what does Ruby do?<br><br> irb(main):001:0> ";;;abc;;de;f;g;;".split ";;"<br>
=> ["", ";abc", "de;f;g"]<br><br>Neither of the above. Hmmm.. why?<br>Ah. "If the <em>limit</em> parameter is omitted, trailing null fields are
suppressed." (<a href="http://www.ruby-doc.org/core/classes/String.html#M000818">http://www.ruby-doc.org/core/classes/String.html#M000818</a>)<br><br>Well. Things *can* get really confusing.<br><br></div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
<div class="Ih2E3d"><br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
It should be possible to perform an idempotent transformation as follows:<br>
</blockquote>
<br></div>
An idempotent transformation F is one such that<br>
<br>
        F(F(X)) = F(X)<br>
<br>
I see no idempotent transformation here.  A useful operation,<br>
yes, but an idempotent one, no.<div class="Ih2E3d"></div></blockquote><div class="Ih2E3d"><br>I know, I know. I was trying to find the right word to signify a "round-trip" operation that leaves the operand unchanged, and was in too much of a hurry to make sure that idempotent meant that. Sorry.<br>
<br>This is more of a sort of mutual identity operation, like f(g(x)) = x.<br> <br><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">Examples:<br>

<br>
> string:split(":", ":This:is::a:contrived:example::").<br>
["","This","is","","a","contrived","example","",""]<br>
> string:split("", "Hello").<br>
["H","e","l","l","o"]<br>
<br>
</blockquote>
<br></div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
Since the separator is the SECOND argument of<br>
string:join/2, I suggest that it should be the SECOND<br>
argument of string:unjoin/2 as well.<br>
</blockquote><div><br>That was very careless of me. I specially wrote the specification of string:split to be patterned after string:join, and then screwed up the example. Eheu, mea culpa (no, really, I could kick myself).<br>
<br></div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;"><br>
The following code<br>
(1) has the name I suggested (unjoin/2) rather than the name<br>
    Edwin Fine suggested (split/2);<br>
(2) has the argument order I suggested (consistent with join/2)<br>
    rather than the argument order Edwin Fine suggested;<br>
(3) has the "split into single character strings" behaviour<br>
    when presented with an empty separator that Edwin Fine<br>
    suggested, rather than any of the alternatives I did;<br>
(4) has been tested.<br>
</blockquote><div><br>I'm assuming you are providing this code as an "executable requirements document." I was rather hoping that it could be implemented as a BIF.<br>I'll have to study your code below so I can learn some "fast and fancy" Erlang!<br>
<br></div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;"><br>
unjoin(String, []) -><br>
    unjoin0(String);<br>
unjoin(String, [Sep]) when is_integer(Sep) -><br>
    unjoin1(String, Sep);<br>
unjoin(String, [C1,C2|L]) when is_integer(C1), is_integer(C2) -><br>
    unjoin2(String, C1, C2, L).<br>
<br>
%% Split a string at "", which is deemed to occur _between_<br>
%% adjacent characters, but queerly, not at the beginning<br>
%% or the end.<br>
<br>
unjoin0([C|Cs]) -><br>
    [[C] | unjoin0(Cs)];<br>
unjoin0([]) -><br>
    [].<br>
<br>
%% Split a string at a single character separator.<br>
<br>
unjoin1(String, Sep) -><br>
    unjoin1_loop(String, Sep, "").<br>
<br>
unjoin1_loop([Sep|String], Sep, Rev) -><br>
    [lists:reverse(Rev) | unjoin1(String, Sep)];<br>
unjoin1_loop([Chr|String], Sep, Rev) -><br>
    unjoin1_loop(String, Sep, [Chr|Rev]);<br>
unjoin1_loop([], _, Rev) -><br>
    [lists:reverse(Rev)].<br>
<br>
%% Split a string at a multi-character separator<br>
%% [C1,C2|L].  These components are split out for<br>
%% a fast match.<br>
<br>
unjoin2(String, C1, C2, L) -><br>
    unjoin2_loop(String, C1, C2, L, "").<br>
<br>
unjoin2_loop([C1|S = [C2|String]], C1, C2, L, Rev) -><br>
    case unjoin_prefix(L, String)<br>
      of no   -> unjoin2_loop(S, C1, C2, L, [C1|Rev])<br>
       ; Rest -> [lists:reverse(Rev) | unjoin2(Rest, C1, C2, L)]<br>
    end;<br>
unjoin2_loop([Chr|String], C1, C2, L, Rev) -><br>
    unjoin2_loop(String, C1, C2, L, [Chr|Rev]);<br>
unjoin2_loop([], _, _, _, Rev) -><br>
    [lists:reverse(Rev)].<br>
<br>
unjoin_prefix([C|L], [C|S]) -> unjoin_prefix(L, S);<br>
unjoin_prefix([],    S)     -> S;<br>
unjoin_prefix(_,     _)     -> no.<br>
<br>
<br>
<br>
</blockquote></div><br></div>