[Templates] shuffle list vmethod - feature request and proposal

Sergey Martynoff sergey@martynoff.info
Sun, 12 Feb 2006 03:52:00 +0300


This is a multi-part message in MIME format.

------=_NextPart_000_0020_01C62F87.AE05BD60
Content-Type: text/plain;
	charset="us-ascii"
Content-Transfer-Encoding: 7bit


It is such a common situation when you need to place one or two
random banners, text chunks or links to a web page. Sometimes it
is wiser to get random values in perl code, but often I feel
extreme dearth of 'shuffle' virtual method - particularly when one
of our html coders asks me how to do it in TT.

Yes, I know about Template::Plugin::Shuffle by Tatsuhiko Miyagawa,
but it is rarely preinstalled on hosting servers and based on
another module, Algorithm::Numerical::Shuffle. And I usually feel
too lazy to go and install it myself (and our system administrator
is even more lazy).

So I suggest adding 'shuffle' to standard virtual methods. I have
made quite simple and small realization (independent of any
modules), and propose it to be included in TT 2.15.

Attached files contain test (to be placed in t/vmethods directory)
and patches to lib/Template/Stash.pm and vmethods documentation
source in src/Manual/VMethods.tt2 from separate 'docsrc' package
(I'm not sure in my English, so please read the docs carefully).

Here is it proposed vmethod:

    'shuffle'   => sub {
        my ($list, $limit) = @_;
        my @r = \( @$list ); # a list of refs to @$list items
        my $i = @$list;
        $limit = $i if $limit < 1 || $limit > $i;
        return [ map {
            my $n = int rand $i--;
            ( ${$r[$n]}, $r[$n] = $r[$i] )[0];
        } (1 .. $limit) ];
    },


-- 
Sergey Martynoff

------=_NextPart_000_0020_01C62F87.AE05BD60
Content-Type: application/octet-stream;
	name="shuffle.t"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="shuffle.t"

#=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D -*-perl-*-=0A=
#=0A=
# t/vmethods/shuffle.t=0A=
#=0A=
# Testing 'shuffle' list virtual method.=0A=
#=0A=
# Written by Sergey Martynoff <sergey@martynoff.info>=0A=
#=0A=
# This is free software; you can redistribute it and/or modify it=0A=
# under the same terms as Perl itself.=0A=
#=0A=
#=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=0A=
=0A=
use strict;=0A=
use warnings;=0A=
use lib qw( ./lib ../lib ../../lib );=0A=
use Template::Test;=0A=
=0A=
# make sure we're using the Perl stash=0A=
$Template::Config::STASH =3D 'Template::Stash';=0A=
=0A=
test_expect(\*DATA);=0A=
=0A=
__DATA__=0A=
=0A=
#------------------------------------------------------------------------=0A=
# 'shuffle' list virtual method=0A=
#------------------------------------------------------------------------=0A=
=0A=
-- test --=0A=
[% list =3D [ ]; # empty list=0A=
   shuf =3D list.shuffle;=0A=
   shuf.first;=0A=
   shuf.join;=0A=
   shuf.size;=0A=
   list.shuffle(3).join;=0A=
-%]=0A=
=0A=
-- expect --=0A=
0=0A=
=0A=
=0A=
-- test --=0A=
[% list =3D [ "single_element\n" ];=0A=
   list.shuffle.first;=0A=
   list.shuffle(1).first;=0A=
   list.shuffle(3).first;=0A=
-%]=0A=
=0A=
-- expect --=0A=
single_element=0A=
single_element=0A=
single_element=0A=
=0A=
=0A=
-- test --=0A=
[% list =3D [ 'one' 'two' 'three' ];=0A=
   FOREACH i =3D [ 0 .. 5 ];=0A=
      "$i:";=0A=
      FOREACH a IN list.shuffle(i);=0A=
         IF a =3D=3D 'one' || a =3D=3D 'two' || a =3D=3D 'three';=0A=
            " ok";=0A=
         END;=0A=
      END; # /foreach a=0A=
      "\n";=0A=
   END; # /foreach i=0A=
-%]=0A=
=0A=
-- expect --=0A=
0: ok ok ok=0A=
1: ok=0A=
2: ok ok=0A=
3: ok ok ok=0A=
4: ok ok ok=0A=
5: ok ok ok=0A=
=0A=
=0A=
-- test --=0A=
[% list =3D [ 7 9 11 13 15 17 19 ];=0A=
   FOREACH i =3D [ 1 .. 5 ];=0A=
      "$i: ";=0A=
      n =3D list.shuffle(i).first;=0A=
      IF n >=3D 7 && n <=3D 19 && n % 2 =3D=3D 1;=0A=
         "odd 7 to 19\n";=0A=
      END;=0A=
   END;=0A=
-%]=0A=
=0A=
-- expect --=0A=
1: odd 7 to 19=0A=
2: odd 7 to 19=0A=
3: odd 7 to 19=0A=
4: odd 7 to 19=0A=
5: odd 7 to 19=0A=
=0A=
=0A=
-- test --=0A=
[% list =3D [ 1 2 4 8 16 32 ];=0A=
   FOREACH i =3D [ 1 .. 4 ];=0A=
      "$i: ";=0A=
      sum =3D 0;=0A=
      SET sum =3D sum + n FOREACH n =3D list.shuffle;=0A=
      "$sum\n";=0A=
   END;=0A=
-%]=0A=
=0A=
-- expect --=0A=
1: 63=0A=
2: 63=0A=
3: 63=0A=
4: 63=0A=
=0A=
=0A=
-- test --=0A=
[% list =3D [ 1 2 3 4 5 6 7 ];=0A=
   s =3D list.join;=0A=
   FOREACH i =3D [ 1 .. 270 ];=0A=
      shuf =3D list.shuffle;=0A=
      IF list.join =3D=3D s && shuf.join !=3D s && shuf.sort.join =3D=3D =
s;=0A=
        result =3D "expected";=0A=
      END;=0A=
   END;=0A=
   result;=0A=
-%]=0A=
=0A=
-- expect --=0A=
expected=0A=
=0A=

------=_NextPart_000_0020_01C62F87.AE05BD60
Content-Type: application/octet-stream;
	name="shuffle_docs.patch"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="shuffle_docs.patch"

diff -ur docsrc/src/Manual/VMethods.tt2 docsrc-sm/src/Manual/VMethods.tt2=0A=
--- docsrc/src/Manual/VMethods.tt2	Thu Jul 24 18:55:04 2003=0A=
+++ docsrc-sm/src/Manual/VMethods.tt2	Sun Feb 12 03:36:25 2006=0A=
@@ -286,6 +286,22 @@=0A=
 In the example, the 'books' list can contains hash references with =0A=
 an 'author' key or objects with an 'author' method.=0A=
 =0A=
+=3Ditem shuffle=0A=
+=0A=
+Returns a list of elements of a list shuffled in a random order.=0A=
+=0A=
+    [% mylist =3D [ 1, 2, 3, 4, 5 ] %]=0A=
+    [% shuffled =3D mylist.shuffle %]=0A=
+=0A=
+An argument can be provided to specify desired number of items in=0A=
+returned random list.=0A=
+=0A=
+    [% one_random_item =3D mylist.shuffle(1).first %]=0A=
+    [% three_random_banners =3D all_banners.shuffle(3) %]=0A=
+=0A=
+If the argument specified is less than 1 or greater that number of=0A=
+items in a list, it is silently ignored.=0A=
+=0A=
 =3Ditem unshift(item), push(item)=0A=
 =0A=
 Adds an item to the start/end of a list.=0A=

------=_NextPart_000_0020_01C62F87.AE05BD60
Content-Type: application/octet-stream;
	name="shuffle_vmethod.patch"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="shuffle_vmethod.patch"

diff -ur Template-Toolkit-2.14a/lib/Template/Stash.pm =
Template-Toolkit-2.14-sm/lib/Template/Stash.pm=0A=
--- Template-Toolkit-2.14a/lib/Template/Stash.pm	Thu Feb  2 19:29:07 2006=0A=
+++ Template-Toolkit-2.14-sm/lib/Template/Stash.pm	Sun Feb 12 03:46:50 =
2006=0A=
@@ -332,6 +332,16 @@=0A=
             return [ splice(@$list) ];=0A=
         }=0A=
     },=0A=
+    'shuffle'   =3D> sub {=0A=
+        my ($list, $limit) =3D @_;=0A=
+        my @r =3D \( @$list ); # a list of refs to @$list items=0A=
+        my $i =3D @$list;=0A=
+        $limit =3D $i if $limit < 1 || $limit > $i;=0A=
+        return [ map {=0A=
+            my $n =3D int rand $i--;=0A=
+            ( ${$r[$n]}, $r[$n] =3D $r[$i] )[0];=0A=
+        } (1 .. $limit) ];=0A=
+    },=0A=
 =0A=
     defined $LIST_OPS ? %$LIST_OPS : (),=0A=
 };=0A=

------=_NextPart_000_0020_01C62F87.AE05BD60--