September 2017 Update

This commit is contained in:
Ingy döt Net 2017-09-23 10:01:46 +02:00
parent bba7bfd280
commit ba8067c3b7
14570 changed files with 153136 additions and 63871 deletions

View file

@ -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.

View file

@ -0,0 +1,4 @@
---
category:
- Sorting
note: Sorting Algorithms

View file

@ -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());
}
}

View 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"))
}

View file

@ -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)
}

View file

@ -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";
}

View 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

View 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")))

View 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]});
}

View 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();

View 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();

View 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();

View 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();

View 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]});
}

View 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();