From 3bb14319e5a414da3de76fbdd432d75a19449637 Mon Sep 17 00:00:00 2001 From: Carl Gay Date: Mon, 21 Jan 2008 13:13:00 +0000 Subject: [PATCH] better join --- strings.dylan | 70 +++++++++++++++++++++++----------- tests/strings-test-suite.dylan | 24 ++++++------ 2 files changed, 60 insertions(+), 34 deletions(-) diff --git a/strings.dylan b/strings.dylan index 8d289a8..68ed2de 100644 --- a/strings.dylan +++ b/strings.dylan @@ -39,9 +39,6 @@ define open generic digit-to-integer (c :: , #key) => (i :: define open generic trim (string :: , #key) => (new-string :: ); -define open generic join - (items :: , separator :: , #key) => (new-string :: ); - define open generic replace (original :: , pattern :: , replacement :: , #key) => (new-string :: , num-replacements :: ); @@ -783,35 +780,62 @@ define method digit-to-integer end method digit-to-integer; // ---------------------------------------------------------------------- +define open generic join + (items :: , separator :: , #key key, conjunction) + => (joined :: ); + // join(range(from: 1, to: 3), ", ", // key: integer-to-string, // conjunction: " and "); // => "1, 2 and 3" define method join - (seq :: , separator :: , + (sequences :: , separator :: , #key key :: = identity, - conjunction :: false-or()) - => (result :: ) - with-output-to-string (out) - let len-1 :: = seq.size - 1; - for (i :: from 1, - item in seq) - let v = key(item); - select (v by instance?) - => write(out, v); - => write-element(out, v); - otherwise => write(out, as(, v)); - end; - //write(out, as(, key(item))); - if (i < len-1) - write(out, separator); - elseif (i == len-1) - write(out, conjunction | separator); - end; + conjunction :: false-or()) + => (joined :: ) + let length :: = sequences.size; + if (length == 0) + error("Attempt to join an empty sequence.") + elseif (length == 1) + key(sequences[0]) + else + let result-size :: + = (reduce(method (len, seq) + len + seq.size + end, + 0, + sequences) + + (separator.size * (length - 1)) + + if (conjunction) + // the last separator is replaced by the conjunction + conjunction.size - separator.size + else + 0 + end); + let first = key(sequences[0]); // don't call key > once on sequences[0] + let result = make(object-class(first), size: result-size); + let result-index :: = 0; + local method copy-to-result (seq :: ) + result := replace-subsequence!(result, seq, start: result-index); + result-index := result-index + seq.size; + end; + copy-to-result(first); + let max-index :: = length - 1; + for (i :: from 1 to max-index) + let seq :: = sequences[i]; + copy-to-result(if(conjunction & i == max-index) + conjunction + else + separator + end); + copy-to-result(key(seq)); end; - end + result + end if end method join; + + // In common-dylan library... // Split a sequence into parts at each occurrance of the 'separator' diff --git a/tests/strings-test-suite.dylan b/tests/strings-test-suite.dylan index e36f45c..39b413e 100644 --- a/tests/strings-test-suite.dylan +++ b/tests/strings-test-suite.dylan @@ -208,17 +208,19 @@ define strings function-test trim () end function-test trim; define strings function-test join () - for (item in list(list(list("", "-"), ""), - list(list(#("a", "b", "c"), "-"), "a-b-c"), - list(list(#("a", "b", "c"), ", ", conjunction:, " and "), - "a, b and c"), - list(list("abc", ", ", conjunction:, " and ", key:, uppercase), - "A, B and C"))) - let (join-args, expected-result) = apply(values, item); - let test-name = fmt("join(%s)", - join(join-args, ", ", key: method (x) fmt("%=", x) end)); - check-equal(test-name, apply(join, join-args), expected-result); - end for; + let abc = #["a", "b", "c"]; + check-equal("join one element", join(#["foo"], "-"), "foo"); + check-equal("join with empty separator", join(abc, ""), "abc"); + check-equal("join with non-empty separator", join(abc, "-"), "a-b-c"); + check-equal("join with conjunction", + join(abc, ", ", conjunction: " and "), + "a, b and c"); + check-equal("join with conjunction and key", + join(abc, ", ", conjunction: " and ", key: uppercase), + "A, B and C"); + check-condition("join an empty sequence is an error", + , + join(#[], "-")); end function-test join; define strings function-test integer-to-digit ()