September 2017 Update
This commit is contained in:
parent
bba7bfd280
commit
ba8067c3b7
14570 changed files with 153136 additions and 63871 deletions
|
|
@ -1,3 +1,5 @@
|
|||
{{Sorting Algorithm}}
|
||||
|
||||
Natural sorting is the sorting of text that does more than rely on the
|
||||
order of individual characters codes to make the finding of
|
||||
individual strings easier for a ''human'' reader.
|
||||
|
|
|
|||
4
Task/Natural-sorting/00META.yaml
Normal file
4
Task/Natural-sorting/00META.yaml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
category:
|
||||
- Sorting
|
||||
note: Sorting Algorithms
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import std.stdio, std.string, std.algorithm, std.array, std.conv,
|
||||
std.ascii, std.range;
|
||||
std.ascii, std.range;
|
||||
|
||||
string[] naturalSort(string[] arr) /*pure @safe*/ {
|
||||
static struct Part {
|
||||
|
|
@ -7,8 +7,8 @@ string[] naturalSort(string[] arr) /*pure @safe*/ {
|
|||
|
||||
int opCmp(in ref Part other) const pure {
|
||||
return (s[0].isDigit && other.s[0].isDigit) ?
|
||||
cmp([s.to!ulong], [other.s.to!ulong]) :
|
||||
cmp(s, other.s);
|
||||
cmp([s.to!ulong], [other.s.to!ulong]) :
|
||||
cmp(s, other.s);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ string[] naturalSort(string[] arr) /*pure @safe*/ {
|
|||
.strip
|
||||
.tr(whitespace, " ", "s")
|
||||
.toLower
|
||||
.groupBy!isDigit
|
||||
.chunkBy!isDigit
|
||||
.map!(p => Part(p.text))
|
||||
.array;
|
||||
return (r.length > 1 && r[0].s == "the") ? r.dropOne : r;
|
||||
|
|
@ -27,32 +27,40 @@ string[] naturalSort(string[] arr) /*pure @safe*/ {
|
|||
}
|
||||
|
||||
void main() /*@safe*/ {
|
||||
auto tests = [
|
||||
// Ignoring leading spaces.
|
||||
["ignore leading spaces: 2-2", " ignore leading spaces: 2-1", "
|
||||
const tests = [
|
||||
// Ignoring leading spaces.
|
||||
["ignore leading spaces: 2-2", " ignore leading spaces: 2-1", "
|
||||
ignore leading spaces: 2+1", " ignore leading spaces: 2+0"],
|
||||
|
||||
// Ignoring multiple adjacent spaces (m.a.s).
|
||||
["ignore m.a.s spaces: 2-2", "ignore m.a.s spaces: 2-1",
|
||||
"ignore m.a.s spaces: 2+0", "ignore m.a.s spaces: 2+1"],
|
||||
// Ignoring multiple adjacent spaces (m.a.s).
|
||||
["ignore m.a.s spaces: 2-2", "ignore m.a.s spaces: 2-1",
|
||||
"ignore m.a.s spaces: 2+0", "ignore m.a.s spaces: 2+1"],
|
||||
|
||||
// Equivalent whitespace characters.
|
||||
["Equiv. spaces: 3-3", "Equiv.\rspaces: 3-2",
|
||||
"Equiv.\x0cspaces: 3-1", "Equiv.\x0bspaces: 3+0",
|
||||
"Equiv.\nspaces: 3+1", "Equiv.\tspaces: 3+2"],
|
||||
// Equivalent whitespace characters.
|
||||
["Equiv. spaces: 3-3", "Equiv.\rspaces: 3-2",
|
||||
"Equiv.\x0cspaces: 3-1", "Equiv.\x0bspaces: 3+0",
|
||||
"Equiv.\nspaces: 3+1", "Equiv.\tspaces: 3+2"],
|
||||
|
||||
// Case Indepenent sort.
|
||||
["cASE INDEPENENT: 3-2", "caSE INDEPENENT: 3-1",
|
||||
"casE INDEPENENT: 3+0", "case INDEPENENT: 3+1"],
|
||||
// Case Indepenent [sic] sort.
|
||||
["cASE INDEPENENT: 3-2" /* [sic] */, "caSE INDEPENENT: 3-1" /* [sic] */,
|
||||
"casE INDEPENENT: 3+0" /* [sic] */, "case INDEPENENT: 3+1" /* [sic] */],
|
||||
|
||||
// Numeric fields as numerics.
|
||||
["foo100bar99baz0.txt", "foo100bar10baz0.txt",
|
||||
"foo1000bar99baz10.txt", "foo1000bar99baz9.txt"],
|
||||
// Numeric fields as numerics.
|
||||
["foo100bar99baz0.txt", "foo100bar10baz0.txt",
|
||||
"foo1000bar99baz10.txt", "foo1000bar99baz9.txt"],
|
||||
|
||||
// Title sorts.
|
||||
["The Wind in the Willows", "The 40th step more",
|
||||
"The 39 steps", "Wanda"]];
|
||||
// Title sorts.
|
||||
["The Wind in the Willows", "The 40th step more",
|
||||
"The 39 steps", "Wanda"]];
|
||||
|
||||
foreach (test; tests)
|
||||
writeln(test, "\n", test.naturalSort, "\n");
|
||||
void printTexts(Range)(string tag, Range range) {
|
||||
const sic = range.front.canFind("INDEPENENT") ? " [sic]" : "";
|
||||
writefln("\n%s%s:\n%-( |%s|%|\n%)", tag, sic, range);
|
||||
}
|
||||
|
||||
foreach (test; tests) {
|
||||
printTexts("Test strings", test);
|
||||
printTexts("Normally sorted", test.dup.sort());
|
||||
printTexts("Naturally sorted", test.dup.naturalSort());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
181
Task/Natural-sorting/Kotlin/natural-sorting.kotlin
Normal file
181
Task/Natural-sorting/Kotlin/natural-sorting.kotlin
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
// version 1.1.4-3
|
||||
|
||||
val r2 = Regex("""[ ]{2,}""")
|
||||
val r3 = Regex("""\s""") // \s represents any whitespace character
|
||||
val r5 = Regex("""\d+""")
|
||||
|
||||
/** Only covers ISO-8859-1 accented characters plus (for consistency) Ÿ */
|
||||
val ucAccented = arrayOf("ÀÁÂÃÄÅ", "Ç", "ÈÉÊË", "ÌÍÎÏ", "Ñ", "ÒÓÔÕÖØ", "ÙÚÛÜ", "ÝŸ")
|
||||
val lcAccented = arrayOf("àáâãäå", "ç", "èéêë", "ìíîï", "ñ", "òóôõöø", "ùúûü", "ýÿ")
|
||||
val ucNormal = "ACEINOUY"
|
||||
val lcNormal = "aceinouy"
|
||||
|
||||
/** Only the commoner ligatures */
|
||||
val ucLigatures = "ÆIJŒ"
|
||||
val lcLigatures = "æijœ"
|
||||
val ucSeparated = arrayOf("AE", "IJ", "OE")
|
||||
val lcSeparated = arrayOf("ae", "ij", "oe")
|
||||
|
||||
/** Miscellaneous replacements */
|
||||
val miscLetters = "ßſʒ"
|
||||
val miscReplacements = arrayOf("ss", "s", "s")
|
||||
|
||||
/** Displays strings including whitespace as if the latter were literal characters */
|
||||
fun String.toDisplayString(): String {
|
||||
val whitespace = arrayOf("\t", "\n", "\u000b", "\u000c", "\r")
|
||||
val whitespace2 = arrayOf("\\t", "\\n", "\\u000b", "\\u000c", "\\r")
|
||||
var s = this
|
||||
for (i in 0..4) s = s.replace(whitespace[i], whitespace2[i])
|
||||
return s
|
||||
}
|
||||
|
||||
/** Ignoring leading space(s) */
|
||||
fun selector1(s: String) = s.trimStart(' ')
|
||||
|
||||
/** Ignoring multiple adjacent spaces i.e. condensing to a single space */
|
||||
fun selector2(s: String) = s.replace(r2, " ")
|
||||
|
||||
/** Equivalent whitespace characters (equivalent to a space say) */
|
||||
fun selector3(s: String) = s.replace(r3, " ")
|
||||
|
||||
/** Case independent sort */
|
||||
fun selector4(s: String) = s.toLowerCase()
|
||||
|
||||
/** Numeric fields as numerics (deals with up to 20 digits) */
|
||||
fun selector5(s: String) = r5.replace(s) { it.value.padStart(20, '0') }
|
||||
|
||||
/** Title sort */
|
||||
fun selector6(s: String): String {
|
||||
if (s.startsWith("the ", true)) return s.drop(4)
|
||||
if (s.startsWith("an ", true)) return s.drop(3)
|
||||
if (s.startsWith("a ", true)) return s.drop(2)
|
||||
return s
|
||||
}
|
||||
|
||||
/** Equivalent accented characters (and case) */
|
||||
fun selector7(s: String): String {
|
||||
val sb = StringBuilder()
|
||||
outer@ for (c in s) {
|
||||
for ((i, ucs) in ucAccented.withIndex()) {
|
||||
if (c in ucs) {
|
||||
sb.append(ucNormal[i])
|
||||
continue@outer
|
||||
}
|
||||
}
|
||||
for ((i, lcs) in lcAccented.withIndex()) {
|
||||
if (c in lcs) {
|
||||
sb.append(lcNormal[i])
|
||||
continue@outer
|
||||
}
|
||||
}
|
||||
sb.append(c)
|
||||
}
|
||||
return sb.toString().toLowerCase()
|
||||
}
|
||||
|
||||
/** Separated ligatures */
|
||||
fun selector8(s: String): String {
|
||||
var ss = s
|
||||
for ((i, c) in ucLigatures.withIndex()) ss = ss.replace(c.toString(), ucSeparated[i])
|
||||
for ((i, c) in lcLigatures.withIndex()) ss = ss.replace(c.toString(), lcSeparated[i])
|
||||
return ss
|
||||
}
|
||||
|
||||
/** Character replacements */
|
||||
fun selector9(s: String): String {
|
||||
var ss = s
|
||||
for ((i, c) in miscLetters.withIndex()) ss = ss.replace(c.toString(), miscReplacements[i])
|
||||
return ss
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
println("The 9 string lists, sorted 'naturally':\n")
|
||||
val s1 = arrayOf(
|
||||
"ignore leading spaces: 2-2",
|
||||
" ignore leading spaces: 2-1",
|
||||
" ignore leading spaces: 2+0",
|
||||
" ignore leading spaces: 2+1"
|
||||
)
|
||||
s1.sortBy(::selector1)
|
||||
println(s1.map { "'$it'" }.joinToString("\n"))
|
||||
|
||||
val s2 = arrayOf(
|
||||
"ignore m.a.s spaces: 2-2",
|
||||
"ignore m.a.s spaces: 2-1",
|
||||
"ignore m.a.s spaces: 2+0",
|
||||
"ignore m.a.s spaces: 2+1"
|
||||
)
|
||||
println()
|
||||
s2.sortBy(::selector2)
|
||||
println(s2.map { "'$it'" }.joinToString("\n"))
|
||||
|
||||
val s3 = arrayOf(
|
||||
"Equiv. spaces: 3-3",
|
||||
"Equiv.\rspaces: 3-2",
|
||||
"Equiv.\u000cspaces: 3-1",
|
||||
"Equiv.\u000bspaces: 3+0",
|
||||
"Equiv.\nspaces: 3+1",
|
||||
"Equiv.\tspaces: 3+2"
|
||||
)
|
||||
println()
|
||||
s3.sortBy(::selector3)
|
||||
println(s3.map { "'$it'".toDisplayString() }.joinToString("\n"))
|
||||
|
||||
val s4 = arrayOf(
|
||||
"cASE INDEPENENT: 3-2",
|
||||
"caSE INDEPENENT: 3-1",
|
||||
"casE INDEPENENT: 3+0",
|
||||
"case INDEPENENT: 3+1"
|
||||
)
|
||||
println()
|
||||
s4.sortBy(::selector4)
|
||||
println(s4.map { "'$it'" }.joinToString("\n"))
|
||||
|
||||
val s5 = arrayOf(
|
||||
"foo100bar99baz0.txt",
|
||||
"foo100bar10baz0.txt",
|
||||
"foo1000bar99baz10.txt",
|
||||
"foo1000bar99baz9.txt"
|
||||
)
|
||||
println()
|
||||
s5.sortBy(::selector5)
|
||||
println(s5.map { "'$it'" }.joinToString("\n"))
|
||||
|
||||
val s6 = arrayOf(
|
||||
"The Wind in the Willows",
|
||||
"The 40th step more",
|
||||
"The 39 steps",
|
||||
"Wanda"
|
||||
)
|
||||
println()
|
||||
s6.sortBy(::selector6)
|
||||
println(s6.map { "'$it'" }.joinToString("\n"))
|
||||
|
||||
val s7 = arrayOf(
|
||||
"Equiv. ý accents: 2-2",
|
||||
"Equiv. Ý accents: 2-1",
|
||||
"Equiv. y accents: 2+0",
|
||||
"Equiv. Y accents: 2+1"
|
||||
)
|
||||
println()
|
||||
s7.sortBy(::selector7)
|
||||
println(s7.map { "'$it'" }.joinToString("\n"))
|
||||
|
||||
val s8 = arrayOf(
|
||||
"IJ ligatured ij",
|
||||
"no ligature"
|
||||
)
|
||||
println()
|
||||
s8.sortBy(::selector8)
|
||||
println(s8.map { "'$it'" }.joinToString("\n"))
|
||||
|
||||
val s9 = arrayOf(
|
||||
"Start with an ʒ: 2-2",
|
||||
"Start with an ſ: 2-1",
|
||||
"Start with an ß: 2+0",
|
||||
"Start with an s: 2+1"
|
||||
)
|
||||
println()
|
||||
s9.sortBy(::selector9)
|
||||
println(s9.map { "'$it'" }.joinToString("\n"))
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
# Sort groups of digits in number order. Sort by order of magnitude then lexically.
|
||||
sub naturally ($a) { $a.lc.subst(/(\d+)/, ->$/ {0~$0.chars.chr~$0},:g) ~"\x0"~$a }
|
||||
|
||||
# Collapse multiple ws characters to a single.
|
||||
sub collapse ($a) { $a.subst( / ( \s ) $0+ /, -> $/ { $0 }, :g ) }
|
||||
|
||||
# Convert all ws characters to a space.
|
||||
sub normalize ($a) { $a.subst( / ( \s ) /, ' ', :g ) }
|
||||
|
||||
# Ignore common leading articles for title sorts
|
||||
sub title ($a) { $a.subst( / :i ^ ( a | an | the ) >> \s* /, '' ) }
|
||||
|
||||
# Decompose ISO-Latin1 glyphs to their base character.
|
||||
sub latin1_decompose ($a) {
|
||||
my %tr = <
|
||||
Æ AE æ ae Þ TH þ th Ð TH ð th ß ss À A Á A Â A Ã A Ä A Å A à a á a
|
||||
â a ã a ä a å a Ç C ç c È E É E Ê E Ë E è e é e ê e ë e Ì I Í I Î
|
||||
I Ï I ì i í i î i ï i Ò O Ó O Ô O Õ O Ö O Ø O ò o ó o ô o õ o ö o
|
||||
ø o Ñ N ñ n Ù U Ú U Û U Ü U ù u ú u û u ü u Ý Y ÿ y ý y
|
||||
>;
|
||||
|
||||
# Would probably be better implemented as
|
||||
# $a.trans( [%tr.keys] => [%tr.values] );
|
||||
# but the underlying Parrot VM leaks through in current Rakudo.
|
||||
my $re = '<[' ~ %tr.keys.join('|') ~ ']>';
|
||||
$a.subst(/ (<$re>) /, -> $/ { %tr{$0} }, :g)
|
||||
}
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
my @tests = (
|
||||
[
|
||||
"Task 1a\nSort while ignoring leading spaces.",
|
||||
[
|
||||
'ignore leading spaces: 1', ' ignore leading spaces: 4',
|
||||
' ignore leading spaces: 3', ' ignore leading spaces: 2'
|
||||
],
|
||||
{.trim} # builtin method.
|
||||
],
|
||||
[
|
||||
"Task 1b\nSort while ignoring multiple adjacent spaces.",
|
||||
[
|
||||
'ignore m.a.s spaces: 3', 'ignore m.a.s spaces: 1',
|
||||
'ignore m.a.s spaces: 4', 'ignore m.a.s spaces: 2'
|
||||
],
|
||||
{.&collapse}
|
||||
],
|
||||
[
|
||||
"Task 2\nSort with all white space normalized to regular spaces.",
|
||||
[
|
||||
"Normalized\tspaces: 4", "Normalized\xa0spaces: 1",
|
||||
"Normalized\x20spaces: 2", "Normalized\nspaces: 3"
|
||||
],
|
||||
{.&normalize}
|
||||
],
|
||||
[
|
||||
"Task 3\nSort case independently.",
|
||||
[
|
||||
'caSE INDEPENDENT: 3', 'casE INDEPENDENT: 2',
|
||||
'cASE INDEPENDENT: 4', 'case INDEPENDENT: 1'
|
||||
],
|
||||
{.lc} # builtin method
|
||||
],
|
||||
[
|
||||
"Task 4\nSort groups of digits in natural number order.",
|
||||
[
|
||||
<Foo100bar99baz0.txt foo100bar10baz0.txt foo1000bar99baz10.txt
|
||||
foo1000bar99baz9.txt 201st 32nd 3rd 144th 17th 2 95>
|
||||
],
|
||||
{.&naturally}
|
||||
],
|
||||
[
|
||||
"Task 5 ( mixed with 1, 2, 3 & 4 )\n"
|
||||
~ "Sort titles, normalize white space, collapse multiple spaces to\n"
|
||||
~ "single, trim leading white space, ignore common leading articles\n"
|
||||
~ 'and sort digit groups in natural order.',
|
||||
[
|
||||
'The Wind in the Willows 8', ' The 39 Steps 3',
|
||||
'The 7th Seal 1', 'Wanda 6',
|
||||
'A Fish Called Wanda 5', ' The Wind and the Lion 7',
|
||||
'Any Which Way But Loose 4', '12 Monkeys 2'
|
||||
],
|
||||
{.&normalize.&collapse.trim.&title.&naturally}
|
||||
],
|
||||
[
|
||||
"Task 6, 7, 8\nMap letters in Latin1 that have accents or decompose to two\n"
|
||||
~ 'characters to their base characters for sorting.',
|
||||
[
|
||||
<apple Ball bald car Card above Æon æon aether
|
||||
niño nina e-mail Évian evoke außen autumn>
|
||||
],
|
||||
{.&latin1_decompose.&naturally}
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
for @tests -> $case {
|
||||
my $code_ref = $case.pop;
|
||||
my @array = $case.pop.list;
|
||||
say $case.pop, "\n";
|
||||
|
||||
say "Standard Sort:\n";
|
||||
.say for @array.sort;
|
||||
|
||||
say "\nNatural Sort:\n";
|
||||
.say for @array.sort: {.$code_ref};
|
||||
|
||||
say "\n" ~ '*' x 40 ~ "\n";
|
||||
}
|
||||
148
Task/Natural-sorting/Phix/natural-sorting.phix
Normal file
148
Task/Natural-sorting/Phix/natural-sorting.phix
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
--
|
||||
-- demo/rosetta/Natural_sorting.exw
|
||||
--
|
||||
function utf32ch(sequence s)
|
||||
for i=1 to length(s) do
|
||||
s[i] = utf8_to_utf32(s[i])[1]
|
||||
end for
|
||||
return s
|
||||
end function
|
||||
|
||||
constant common = {"the","it","to","a","of","is"},
|
||||
{al,ac_replacements} = columnize({
|
||||
{"Æ","AE"},{"æ","ae"},{"Þ","TH"},{"þ","th"},
|
||||
{"Ð","TH"},{"ð","th"},{"ß","ss"},{"<22>","fi"},
|
||||
{"<22>","fl"},{"",'s'},{"’",'z'},
|
||||
{"À",'A'},{"Á",'A'},{"Â",'A'},{"Ã",'A'},
|
||||
{"Ä",'A'},{"Å",'A'},{"à",'a'},{"á",'a'},
|
||||
{"â",'a'},{"ã",'a'},{"ä",'a'},{"å",'a'},
|
||||
{"Ç",'C'},{"ç",'c'},{"È",'E'},{"É",'E'},
|
||||
{"Ê",'E'},{"Ë",'E'},{"è",'e'},{"é",'e'},
|
||||
{"ê",'e'},{"ë",'e'},{"Ì",'I'},{"Í",'I'},
|
||||
{"Î",'I'},{"Ï",'I'},{"ì",'i'},{"í",'i'},
|
||||
{"î",'i'},{"ï",'i'},{"Ò",'O'},{"Ó",'O'},
|
||||
{"Ô",'O'},{"Õ",'O'},{"Ö",'O'},{"Ø",'O'},
|
||||
{"ò",'o'},{"ó",'o'},{"ô",'o'},{"õ",'o'},
|
||||
{"ö",'o'},{"ø",'o'},{"Ñ",'N'},{"ñ",'n'},
|
||||
{"Ù",'U'},{"Ú",'U'},{"Û",'U'},{"Ü",'U'},
|
||||
{"ù",'u'},{"ú",'u'},{"û",'u'},{"ü",'u'},
|
||||
{"Ý",'Y'},{"ÿ",'y'},{"ý",'y'}}),
|
||||
accents_and_ligatures = utf32ch(al)
|
||||
|
||||
function normalise(string s)
|
||||
sequence utf32 = utf8_to_utf32(s)
|
||||
sequence res = {}
|
||||
integer i = 1, ch, prev
|
||||
for i=1 to length(utf32) do
|
||||
ch = utf32[i]
|
||||
if find(ch," \t\r\n\x0b\x0c") then
|
||||
if length(res)>0 and prev!=' ' then
|
||||
res &= -1
|
||||
end if
|
||||
prev = ' '
|
||||
elsif find(ch,"0123456789") then
|
||||
if length(res)=0 or prev!='0' then
|
||||
res &= ch-'0'
|
||||
else
|
||||
res[$] = res[$]*10+ch-'0'
|
||||
end if
|
||||
prev = '0'
|
||||
else
|
||||
object rep = find(ch,accents_and_ligatures)
|
||||
if rep then
|
||||
rep = lower(ac_replacements[rep])
|
||||
else
|
||||
rep = lower(ch)
|
||||
end if
|
||||
if length(res) and sequence(res[$]) then
|
||||
res[$] &= rep
|
||||
else
|
||||
res = append(res,""&rep)
|
||||
end if
|
||||
prev = ch
|
||||
end if
|
||||
end for
|
||||
for i=1 to length(common) do
|
||||
while 1 do
|
||||
integer k = find(common[i],res)
|
||||
if k=0 then exit end if
|
||||
res[k..k] = {}
|
||||
if length(res) and res[1]=-1 then
|
||||
res = res[2..$]
|
||||
end if
|
||||
end while
|
||||
end for
|
||||
if length(res) and prev=' ' then
|
||||
res = res[1..$-1]
|
||||
end if
|
||||
return res
|
||||
end function
|
||||
|
||||
sequence tests = {
|
||||
{" leading spaces: 4",
|
||||
" leading spaces: 3",
|
||||
"leading spaces: 2",
|
||||
" leading spaces: 1"},
|
||||
{"adjacent spaces: 3",
|
||||
"adjacent spaces: 4",
|
||||
"adjacent spaces: 1",
|
||||
"adjacent spaces: 2"},
|
||||
{"white space: 3-2",
|
||||
"white\r space: 3-3",
|
||||
"white\x0cspace: 3-1",
|
||||
"white\x0bspace: 3+0",
|
||||
"white\n space: 3+1",
|
||||
"white\t space: 3+2"},
|
||||
{"caSE independent: 3-1",
|
||||
"cASE independent: 3-2",
|
||||
"casE independent: 3+0",
|
||||
"case independent: 3+1"},
|
||||
{"foo1000bar99baz9.txt",
|
||||
"foo100bar99baz0.txt",
|
||||
"foo100bar10baz0.txt",
|
||||
"foo1000bar99baz10.txt"},
|
||||
{"foo1bar",
|
||||
"foo100bar",
|
||||
"foo bar",
|
||||
"foo1000bar"},
|
||||
{"The Wind in the Willows",
|
||||
"The 40th step more",
|
||||
"The 39 steps",
|
||||
"Wanda"},
|
||||
{"ignore ý accents: 2-2",
|
||||
"ignore Ý accents: 2-1",
|
||||
"ignore y accents: 2+0",
|
||||
"ignore Y accents: 2+1"},
|
||||
{"Ball","Card","above","aether",
|
||||
"apple","autumn","außen","bald",
|
||||
"car","e-mail","evoke","nina",
|
||||
"niño","Æon","Évian","æon"},
|
||||
}
|
||||
|
||||
sequence s, n, t, tags
|
||||
|
||||
function natural(integer i, integer j)
|
||||
return compare(t[i],t[j])
|
||||
end function
|
||||
|
||||
for i=1 to length(tests) do
|
||||
s = tests[i]
|
||||
n = sort(s)
|
||||
t = repeat(0,length(s))
|
||||
for j=1 to length(s) do
|
||||
t[j] = normalise(s[j])
|
||||
end for
|
||||
tags = custom_sort(routine_id("natural"),tagset(length(s)))
|
||||
if i=3 then -- clean up the whitespace mess
|
||||
for j=1 to length(s) do
|
||||
s[j] = substitute_all(s[j],{"\r","\x0c","\x0b","\n","\t"},{"\\r","\\x0c","\\x0b","\\n","\\t"})
|
||||
n[j] = substitute_all(n[j],{"\r","\x0c","\x0b","\n","\t"},{"\\r","\\x0c","\\x0b","\\n","\\t"})
|
||||
end for
|
||||
end if
|
||||
printf(1,"%-30s %-30s %-30s\n",{"original","normal","natural"})
|
||||
printf(1,"%-30s %-30s %-30s\n",{"========","======","======="})
|
||||
for k=1 to length(tags) do
|
||||
printf(1,"%-30s|%-30s|%-30s\n",{s[k],n[k],s[tags[k]]})
|
||||
end for
|
||||
puts(1,"\n")
|
||||
end for
|
||||
91
Task/Natural-sorting/Scheme/natural-sorting.ss
Normal file
91
Task/Natural-sorting/Scheme/natural-sorting.ss
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
(import (scheme base)
|
||||
(scheme char)
|
||||
(scheme write)
|
||||
(only (srfi 1) drop take-while)
|
||||
(only (srfi 13) string-drop string-join string-prefix-ci? string-tokenize)
|
||||
(srfi 132))
|
||||
|
||||
|
||||
;; Natural sort function
|
||||
(define (natural-sort lst)
|
||||
; <1><2> ignores leading, trailing and multiple adjacent spaces
|
||||
; by tokenizing on whitespace (all whitespace characters),
|
||||
; and joining with a single space
|
||||
(define (ignore-spaces str)
|
||||
(string-join (string-tokenize str) " "))
|
||||
; <5> Remove articles from string
|
||||
(define (drop-articles str)
|
||||
(define (do-drop articles str)
|
||||
(cond ((null? articles)
|
||||
str)
|
||||
((string-prefix-ci? (car articles) str)
|
||||
(string-drop str (string-length (car articles))))
|
||||
(else
|
||||
(do-drop (cdr articles) str))))
|
||||
(do-drop '("a " "an " "the ") str))
|
||||
; <4> split string into number/non-number groups
|
||||
(define (group-digits str)
|
||||
(let loop ((chars (string->list str))
|
||||
(doing-num? (char-numeric? (string-ref str 0)))
|
||||
(groups '()))
|
||||
(if (null? chars)
|
||||
(map (lambda (s) ; convert numbers to actual numbers
|
||||
(if (char-numeric? (string-ref s 0))
|
||||
(string->number s)
|
||||
s))
|
||||
(map list->string groups)) ; leave groups in reverse, as right-most significant
|
||||
(let ((next-group (take-while (if doing-num?
|
||||
char-numeric?
|
||||
(lambda (c) (not (char-numeric? c))))
|
||||
chars)))
|
||||
(loop (drop chars (length next-group))
|
||||
(not doing-num?)
|
||||
(cons next-group groups))))))
|
||||
;
|
||||
(list-sort
|
||||
(lambda (a b) ; implements the numeric fields comparison <4>
|
||||
(let loop ((lft (group-digits (drop-articles (ignore-spaces a))))
|
||||
(rgt (group-digits (drop-articles (ignore-spaces b)))))
|
||||
(cond ((null? lft) ; a is shorter
|
||||
#t)
|
||||
((null? rgt) ; b is shorter
|
||||
#f)
|
||||
((equal? (car lft) (car rgt)) ; if equal, look at next pair
|
||||
(loop (cdr lft) (cdr rgt)))
|
||||
((and (number? (car lft)) ; compare as numbers
|
||||
(number? (car rgt)))
|
||||
(< (car lft) (car rgt)))
|
||||
((and (string? (car lft)) ; compare as strings
|
||||
(string? (car rgt)))
|
||||
(string-ci<? (car lft) (car rgt))) ; <3> ignoring case
|
||||
((and (number? (car lft)) ; strings before numbers
|
||||
(string? (car rgt)))
|
||||
#f)
|
||||
((and (string? (car lft)) ; strings before numbers
|
||||
(number? (car rgt)))
|
||||
#t))))
|
||||
lst))
|
||||
|
||||
;; run string examples
|
||||
(define (display-list title lst)
|
||||
(display title) (newline)
|
||||
(display "[\n") (for-each (lambda (i) (display i)(newline)) lst) (display "]\n"))
|
||||
|
||||
(for-each
|
||||
(lambda (title example)
|
||||
(display title) (newline)
|
||||
(display-list "Text strings:" example)
|
||||
(display-list "Normally sorted:" (list-sort string<? example))
|
||||
(display-list "Naturally sorted:" (natural-sort example))
|
||||
(newline))
|
||||
'("# Ignoring leading spaces" "# Ignoring multiple adjacent spaces (m.a.s.)"
|
||||
"# Equivalent whitespace characters" "# Case Independent sort"
|
||||
"# Numeric fields as numerics" "# Numeric fields as numerics - shows sorting from right"
|
||||
"# Title sorts")
|
||||
'(("ignore leading spaces: 2-2" " ignore leading spaces: 2-1" " ignore leading spaces: 2+0" " ignore leading spaces: 2+1")
|
||||
("ignore m.a.s spaces: 2-2" "ignore m.a.s spaces: 2-1" "ignore m.a.s spaces: 2+0" "ignore m.a.s spaces: 2+1")
|
||||
("Equiv. spaces: 3-3" "Equiv.\rspaces: 3-2" "Equiv.\x0c;spaces: 3-1" "Equiv.\x0b;spaces: 3+0" "Equiv.\nspaces: 3+1" "Equiv.\tspaces: 3+2")
|
||||
("cASE INDEPENDENT: 3-2" "caSE INDEPENDENT: 3-1" "casE INDEPENDENT: 3+0" "case INDEPENDENT: 3+1")
|
||||
("foo100bar99baz0.txt" "foo100bar10baz0.txt" "foo1000bar99baz10.txt" "foo1000bar99baz9.txt")
|
||||
("foo1bar99baz4.txt" "foo2bar99baz3.txt" "foo4bar99baz1.txt" "foo3bar99baz2.txt")
|
||||
("The Wind in the Willows" "The 40th step more" "The 39 steps" "Wanda")))
|
||||
4
Task/Natural-sorting/Zkl/natural-sorting-1.zkl
Normal file
4
Task/Natural-sorting/Zkl/natural-sorting-1.zkl
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
fcn dsuSort(x,orig){ // decorate-sort-undecorate sort
|
||||
x.enumerate().sort(fcn([(_,a)],[(_,b)]){a<b})
|
||||
.apply('wrap([(n,_)]){orig[n]});
|
||||
}
|
||||
4
Task/Natural-sorting/Zkl/natural-sorting-2.zkl
Normal file
4
Task/Natural-sorting/Zkl/natural-sorting-2.zkl
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Ignoring leading spaces
|
||||
ts1:=T("ignore leading spaces: 2-2", " ignore leading spaces: 2-1",
|
||||
" ignore leading spaces: 2+0", " ignore leading spaces: 2+1");
|
||||
dsuSort(ts1.apply("strip"),ts1).println();
|
||||
4
Task/Natural-sorting/Zkl/natural-sorting-3.zkl
Normal file
4
Task/Natural-sorting/Zkl/natural-sorting-3.zkl
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Ignoring multiple adjacent spaces (m.a.s)
|
||||
ts2:=T("ignore m.a.s spaces: 2-2", "ignore m.a.s spaces: 2-1",
|
||||
"ignore m.a.s spaces: 2+0", "ignore m.a.s spaces: 2+1");
|
||||
dsuSort(ts2.apply('-(" ")),ts2).println();
|
||||
4
Task/Natural-sorting/Zkl/natural-sorting-4.zkl
Normal file
4
Task/Natural-sorting/Zkl/natural-sorting-4.zkl
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Equivalent whitespace characters
|
||||
ts3:=T("Equiv. spaces: 3-3", "Equiv.\rspaces: 3-2", "Equiv.\x0cspaces: 3-1",
|
||||
"Equiv.\x0bspaces: 3+0", "Equiv.\nspaces: 3+1", "Equiv.\tspaces: 3+2");
|
||||
dsuSort(ts3.apply('-.fp1("\n\r\t\f\b\x0b ")),ts3).println();
|
||||
4
Task/Natural-sorting/Zkl/natural-sorting-5.zkl
Normal file
4
Task/Natural-sorting/Zkl/natural-sorting-5.zkl
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Case Indepenent sort
|
||||
ts4:=T("cASE INDEPENENT: 3-2", "caSE INDEPENENT: 3-1",
|
||||
"casE INDEPENENT: 3+0", "case INDEPENENT: 3+1");
|
||||
dsuSort(ts4.apply("toLower"),ts4).println();
|
||||
15
Task/Natural-sorting/Zkl/natural-sorting-6.zkl
Normal file
15
Task/Natural-sorting/Zkl/natural-sorting-6.zkl
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# Numeric fields as numerics
|
||||
fcn fieldize(s){
|
||||
s.apply(fcn(c){"1234567890".holds(c) and c or "."}).split(".")
|
||||
.filter().apply("toInt");
|
||||
}
|
||||
fcn fcmp(a,b){ // T(key, T(numeric fields)), eg L(0, L(100,99,0))
|
||||
a[1].zip(b[1]).reduce(fcn(_,[(a,b)]){
|
||||
if(a==b)return(True); // continue to next field
|
||||
return(Void.Stop,a<b);
|
||||
},True);
|
||||
}
|
||||
fcn fsort(list){
|
||||
list.apply(fieldize).enumerate().sort(fcmp)
|
||||
.apply('wrap([(n,_)]){list[n]});
|
||||
}
|
||||
6
Task/Natural-sorting/Zkl/natural-sorting-7.zkl
Normal file
6
Task/Natural-sorting/Zkl/natural-sorting-7.zkl
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
ts5:=T("foo100bar99baz0.txt", "foo100bar10baz0.txt", "foo1000bar99baz10.txt",
|
||||
"foo1000bar99baz9.txt");
|
||||
fsort(ts5).println();
|
||||
|
||||
x:=T("x9y99","foo10.txt","x10y0","foo9.txt","x9y100");
|
||||
fsort(x).println();
|
||||
Loading…
Add table
Add a link
Reference in a new issue