Decorate-sort-undecorate idiom: Difference between revisions

m
(→‎{{header|Ruby}}: typo in code)
 
(34 intermediate revisions by 16 users not shown)
Line 1:
{{task}}
 
[[Category:Sorting]]
 
;Introduction
Line 212 ⟶ 214:
</pre>
 
=={{header|FactorC sharp|C#}}==
Linq makes this very easy. We can do this using 2 different syntaxes:
{{works with|Factor 0.99 build 2207}}
<syntaxhighlight lang="csharp">
The easiest way is to define your key function as a memoized word. Memoized words store input/output pairs and simply fetch the outputs of inputs they've seen before. Then using the usual <code>sort-by</code> combinator won't recalculate any extraneous lengths.
public static IEnumerable<T> Schwartzian1<T, TKey>(IEnumerable<T> source, Func<T, TKey> decorator) =>
<syntaxhighlight lang="factor">
source.Select(item => (item, key: decorator(item)))
USING: prettyprint sequences sorting ;
.OrderBy(tuple => tuple.key)
.Select(tuple => tuple.item);
 
public static IEnumerable<T> Schwartzian2<T, TKey>(IEnumerable<T> source, Func<T, TKey> decorator) =>
MEMO: length* ( seq -- n ) length ;
from item in source
let key = decorator(item)
orderby key
select item;
 
//Call:
{ "Rosetta" "Code" "is" "a" "programming" "chrestomathy" "site" }
string[] array = {"Rosetta", "Code", "is", "a", "programming", "chrestomathy", "site"};
[ length* ] sort-by .
Console.WriteLine(string.Join(" ", Schwartzian1(array, i => i.Length)));
</syntaxhighlight>
{{out}}
<pre>
a is Code site Rosetta programming chrestomathy
</pre>
 
=={{header|C++}}==
A more direct implementation would be:
<syntaxhighlight lang="cpp">#include <algorithm>
#include <iostream>
#include <type_traits>
#include <vector>
 
template <typename Iterator, typename Function>
void decorate_sort_undecorate(Iterator begin, Iterator end, Function f) {
using ValueType = typename std::iterator_traits<Iterator>::value_type;
using KeyType = std::invoke_result_t<Function, ValueType>;
using KeyValue = std::pair<KeyType, ValueType>;
std::vector<KeyValue> tmp;
tmp.reserve(std::distance(begin, end));
std::transform(begin, end, std::back_inserter(tmp), [&f](ValueType& v) {
return std::make_pair(f(v), std::move(v));
});
std::sort(tmp.begin(), tmp.end(), [](const KeyValue& a, const KeyValue& b) {
return a.first < b.first;
});
std::transform(tmp.begin(), tmp.end(), begin,
[](KeyValue& p) { return std::move(p.second); });
}
 
int main() {
std::string test[] = {"Rosetta", "Code", "is", "a",
"programming", "chrestomathy", "site"};
decorate_sort_undecorate(std::begin(test), std::end(test),
[](const std::string& s) { return s.size(); });
for (const std::string& s : test)
std::cout << s << ' ';
std::cout << '\n';
}</syntaxhighlight>
 
{{out}}
<pre>
a is Code site Rosetta programming chrestomathy
</pre>
 
=={{header|Factor}}==
{{works with|Factor|0.99}}
<code>map-sort</code> employs the decorate-sort-undecorate idiom, while <code>sort-by</code> does not.
<syntaxhighlight lang="factor">
USING: assocs prettyprint sequences sorting.extras ;
 
{ "Rosetta" "Code" "is" "a" "programming" "chrestomathy" "site" }
[ length ] zipmap-withsort .
[ last ] sort-by
keys .
</syntaxhighlight>
{{out}}
Line 300 ⟶ 351:
'''Solution'''
 
Let us create aan listarray for testing:
 
[[File:Fōrmulæ - Decorate-sort-undecorate idiom 01.png]]
Line 313 ⟶ 364:
 
[[File:Fōrmulæ - Decorate-sort-undecorate idiom 04.png]]
 
 
'''Schwartzian transform:''' The following is a solution using a "Schwartzian transform" idiom, and it produces identical results:
Line 418 ⟶ 470:
"programming"
"chrestomathy"</pre>
 
=={{header|J}}==
J's native sort primitive always sorts on a key (which might be the list itself), such that each element of the key is calculated only once. This corresponds to APL's grade up primitive (though comparing dates on this approach vs. lisp's approach seems problematic due to deficiencies in historical evidence).
 
Thus, for example:
 
<syntaxhighlight lang=J> >(/: #@>) ;:'Rosetta Code is a programming chrestomathy site'
a
is
Code
site
Rosetta
programming
chrestomathy
</syntaxhighlight>
 
=={{header|Java}}==
<syntaxhighlight lang="java">
import java.util.AbstractMap;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
 
public final class DecorateSortUndecorateIdiom {
 
public static void main(String[] args) {
List<String> list = List.of( "Rosetta", "Code", "is", "a", "programming", "chrestomathy", "site" );
System.out.println(schwartzian(list, s -> s.length()));
}
/**
* Return a sorted list using the Schwartzian Transform
* which guarantees minimal use of the key extractor function.
*
* Use this method when the key extractor function is an expensive operation.
*/
private static <T, R extends Comparable<R>> List<T> schwartzian(List<T> list, Function<T, R> function) {
return list.stream().map( s -> new AbstractMap.SimpleEntry<T, R>(s, function.apply(s)) )
.sorted( (one, two) -> one.getValue().compareTo(two.getValue()) )
.map( p -> p.getKey() )
.collect(Collectors.toList());
}
 
}
</syntaxhighlight>
{{ out }}
<pre>
[a, is, Code, site, Rosetta, programming, chrestomathy]
</pre>
 
=={{header|JavaScript}}==
Line 593 ⟶ 694:
"programming"
"chrestomathy"
</pre>
 
=={{header|Kotlin}}==
<syntaxhighlight lang="kotlin">
fun main() {
val list = listOf("Rosetta", "Code", "is", "a", "programming", "chrestomathy", "site")
println(list.sortedBySchwartzian(String::length))
}
 
/**
* Returns a sorted list using the Schwartzian Transform which guarantees minimal use of the
* key extractor function. Use when the key extractor function is an expensive operation.
*/
fun <T, R: Comparable<R>> Collection<T>.sortedBySchwartzian(keyFn: (T) -> R): List<T> =
this.map { it to keyFn(it) }
.sortedBy { it.second }
.map { it.first }
</syntaxhighlight>
 
Output:
 
<pre>
[a, is, Code, site, Rosetta, programming, chrestomathy]
</pre>
 
=={{header|Lua}}==
Lua's 'table.sort' accepts a custom compare function as a second argument.
<syntaxhighlight lang="lua">-- Decorate, sort, undecorate function
function dsu (tab, keyFunc)
keyFunc = keyFunc or function (a, b) return a[2] < b[2] end
for key, value in pairs(tab) do
tab[key] = {value, #value}
end
table.sort(tab, keyFunc)
for key, value in pairs(tab) do
tab[key] = value[1]
end
return tab
end
 
-- Use default sort order by not specifying a key function
local list = {"Rosetta", "Code", "is", "a", "programming", "chrestomathy", "site"}
print(unpack(dsu(list)))
 
-- Create a custom key function and pass it as an argument
function descendingOrder (a, b)
return a[2] > b[2]
end
print(unpack(dsu(list, descendingOrder)))</syntaxhighlight>
{{out}}
<pre>a is site Code Rosetta programming chrestomathy
chrestomathy programming Rosetta Code site is a</pre>
 
=={{header|Nim}}==
In Nim, there are several ways to sort a list either by creating an explicit temporary list or using a map-sort-map idiom.
 
The easiest way to sort a list of words by word length consists to use the “sortedByIt” template which eliminates the need to create a decorated list. But this is not what is required in this task.
 
Here is one way to sort the words by length using the decorate-sort-decorate idiom:
 
<syntaxhighlight lang="Nim">import std/[algorithm, sequtils]
 
let wordList = ["Rosetta", "Code", "is", "a", "programming", "chrestomathy", "site"]
 
echo wordList.mapIt((value: it, length: it.len)).sortedByIt(it.length).mapIt(it.value)
</syntaxhighlight>
 
{{out}}
<pre>@["a", "is", "Code", "site", "Rosetta", "programming", "chrestomathy"]
</pre>
 
Note that words with same length will appear in the original list order. If we want them to appear in
alphabetical order, we need to sort the list alphabetically first. As Nim sorting algorithm is stable,
the order of items with same key value (i.e. length in this example) is preserved when sorting.
 
Of course, it is also possible to define a comparison function to sort by length then alphabetically and use it when sorting.
 
=={{header|Nu}}==
<syntaxhighlight lang="nu">
def 'sort by key' [keyfunc] {
( each {|v| {k: ($v | do $keyfunc ), v: $v}}
| sort-by k
| each {get v} )
}
 
"Rosetta Code is a programming chrestomathy site" | split words | sort by key {str length}
</syntaxhighlight>
{{out}}
<pre>
╭───┬──────────────╮
│ 0 │ a │
│ 1 │ is │
│ 2 │ Code │
│ 3 │ site │
│ 4 │ Rosetta │
│ 5 │ programming │
│ 6 │ chrestomathy │
╰───┴──────────────╯
</pre>
 
=={{header|Perl}}==
<syntaxhighlight lang="perl">#!/usr/bin/perl
 
use strict; # https://rosettacode.org/wiki/Decorate-sort-undecorate_idiom
use warnings;
use List::AllUtils qw( nsort_by );
 
my @list = ("Rosetta", "Code", "is", "a", "programming", "chrestomathy", "site");
print "@list\n";
 
my @sortedlist = nsort_by { length } @list;
print "@sortedlist\n";</syntaxhighlight>
{{out}}
<pre>
Rosetta Code is a programming chrestomathy site
a is Code site Rosetta programming chrestomathy
</pre>
 
Line 603 ⟶ 820:
<span style="color: #0000FF;">?</span><span style="color: #000000;">sort_by</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">split</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Rosetta Code is a programming chrestomathy site"</span><span style="color: #0000FF;">),</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">)</span>
<!--</syntaxhighlight>-->
Technically that does not decorate/undecorate and instead uses an anonymous parallel array or two, but it does only calculate each length once.
{{out}}
<pre>
Line 689 ⟶ 907:
programming
chrestomathy</pre>
 
=={{header|Quackery}}==
 
<syntaxhighlight lang="Quackery"> [ ]'[ temp put
[] swap witheach
[ dup temp share do
dip nested join
nested join ]
temp release ] is decoratewith ( [ --> [ )
 
[ [] swap witheach
[ 0 peek nested join ] ] is undecorate ( [ --> [ )
 
$ "Rosetta Code is a programming chrestomathy site" nest$
 
decoratewith size
sortwith [ dip [ 1 peek ] 1 peek > ]
undecorate
witheach [ echo$ sp ]</syntaxhighlight>
 
{{out}}
 
<pre>a is Code site Rosetta programming chrestomathy</pre>
 
=={{header|Raku}}==
Line 705 ⟶ 946:
 
More complicated transforms may ''require'' an explicit schwartzian transform routine. An example of where an explicit transform is desirable is the <code>schwartzian()</code> routine in the [[P-value_correction#Raku|Raku entry for the P-value_correction task]].
=={{header|RPL}}==
This implementation accepts the key function as a callback but uses local named variables.
≪ → seq func
≪ { }
1 seq SIZE '''FOR''' j
seq j GET
DUP func EVAL
2 →LIST 1 →LIST +
'''NEXT'''
≫ ≫ '<span style="color:blue">DECOR</span>' STO
≪ '''IF''' DUP SIZE 2 ≥ '''THEN'''
LIST→ → len
≪ len 1 '''FOR''' n
1 n 1 - '''START'''
DUP2 2 GET SWAP 2 GET
'''IF''' < '''THEN''' SWAP '''END'''
n ROLLD
'''NEXT''' n ROLLD
-1 '''STEP''' len →LIST
≫ '''END'''
≫ '<span style="color:blue">KSORT</span>' STO
≪ → seq
≪ { }
1 seq SIZE '''FOR''' j
seq j GET 1 GET +
'''NEXT'''
≫ ≫ '<span style="color:blue">UNDECOR</span>' STO
 
{ "Rosetta" "Code" "is" "a" "programming" "chrestomathy" "site" } ≪ SIZE ≫ <span style="color:blue">DECOR KSORT UNDECOR</span>
{{out}}
<pre>
1: {"a" "is" "Code" "site" "Rosetta" "programming" "chrestomathy" }
</pre>
 
=={{header|Ruby}}==
Arrays have a <code>sort_by</code> method which does a Schwartzian transform.
Line 755 ⟶ 1,032:
cached: ["a", "is", "Code", "site", "Rosetta", "programming", "chrestomathy"]
transform: ["a", "is", "Code", "site", "Rosetta", "programming", "chrestomathy"]
</pre>
 
=={{header|Sidef}}==
<syntaxhighlight lang="ruby">var str = "Rosetta Code is a programming chrestomathy site"
 
# Built-in
say str.split.sort_by{.len}
 
# Same thing explicitly
say str.split.map {|w| [w, w.len] }.sort{|a,b| a[1] <=> b[1] }.map { _[0] }
 
# Sort by word length, then lexical:
var str2 = str+" seven extra words added to this demo"
say str2.split.sort_by{|word| [word.len, word] }</syntaxhighlight>
{{out}}
<pre>
["a", "is", "Code", "site", "Rosetta", "programming", "chrestomathy"]
["a", "is", "Code", "site", "Rosetta", "programming", "chrestomathy"]
["a", "is", "to", "Code", "demo", "site", "this", "added", "extra", "seven", "words", "Rosetta", "programming", "chrestomathy"]
</pre>
 
Line 762 ⟶ 1,058:
 
It's not specified how words of equal length are to be sorted. The standard sort() method used here (quicksort under the hood) is unstable and so the sort order for such words is not guaranteed.
<syntaxhighlight lang="ecmascriptwren">var schwartzian = Fn.new { |a, f|
System.print(a.map { |e| [e, f.call(e)] } // decorate
.toList
2,120

edits