1 2 3 4 5 6
#!/usr/bin/perl use strict; use warnings; my @stats = (['RequestsA',0],['RequestsB',0],['RequestsC',0],['RequestsD',0],['RequestsE',0]); my ($idx) = (grep {$_->[0] eq 'RequestsD'} @stats); $idx->[1] ++;
2019-08-11T06:33:54 rostiassoziative Datenstruktur
2019-08-11T07:16:00 bianca2019-08-11T06:33:54 rostiassoziative Datenstruktur
Wie sähe die für dieses Beispiel aus?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#!/usr/bin/perl use strict; use warnings; my @stats = (['RequestsA',0],['RequestsB',0],['RequestsC',0],['RequestsD',0],['RequestsE',0]); # Direkt als assoziative Datenstruktur (in Perl ein "Hash") my %stats = ( RequestsA => 0, RequestsB => 0, RequestsC => 0, RequestsD => 0, RequestsE => 0); # Konversionen zwischen den beiden %stats = map { @$_ } @stats; @stats = map { [ $_, $stats{$_} ] } keys %stats; # !! siehe unten # Initialisieren des Hash in einem Aufwasch (alle Elemente auf 0) %stats = map { $_ => 0 } (qw(RequestsA RequestsB RequestsC RequestsD RequestsE)); # und dann anstelle der $idx-Methode einfach nur: $stats{RequestsD}++;
@stats = map { $_->[0] eq 'RequestsD' ? [$_->[0], $_->[1] + 1] : $_ } @stats;
2019-08-11T08:49:23 FIFOJa, mit map(), dann brauchst Du auch $idx nicht
1 2 3 4 5 6 7 8 9 10 11
for my $item ( @stats ) { if ( $item->[0] eq 'RequestsD' ) { $item->[1]++; # @stats wir inplace verändert } } # oder postfix: for my $item ( @stats ) { $item->[1]++ if $item->[0] eq 'RequestsD'; }
2019-08-11T12:59:55 FIFOZwei Dinge muss man allerdings bedenken:
* Bei (sehr) großen Arrays kann das länger als nötig dauern
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
use strict; use warnings; use Benchmark ('timethese'); my $size = 10000; my @stats = map { [ "a$_" => 0] } (0..$size); sub rewrite_array { @stats = map { $_->[0] eq 'a3333' ? [$_->[0], $_->[1] + 1] : $_ } @stats; } sub loop { for my $item ( @stats ) { $item->[1]++ if $item->[0] eq 'a3333'; } } sub void_array { map { $_->[0] eq 'a3333' ? [$_->[0], $_->[1] + 1] : $_ } @stats; } timethese (10000, { '1. Array neu erstellen ' => \&rewrite_array, '2. Schleife ausformuliert ' => \&loop, '3. Array im leeren Kontext' => \&void_array, });
1
2
3
4
5
6
7
Benchmark: timing 10000 iterations of
1. Array neu erstellen ,
2. Schleife ausformuliert ,
3. Array im leeren Kontext...
1. Array neu erstellen : 18 wallclock secs (17.64 usr + 0.00 sys = 17.64 CPU) @ 566.89/s (n=10000)
2. Schleife ausformuliert : 8 wallclock secs ( 7.97 usr + 0.00 sys = 7.97 CPU) @ 1254.71/s (n=10000)
3. Array im leeren Kontext: 8 wallclock secs ( 8.54 usr + 0.01 sys = 8.55 CPU) @ 1169.59/s (n=10000)
2019-08-11T18:43:10 hajDas map läßt sich im vorliegenden Fall allerdings auch beschleunigen, wenn man die Liste nicht neu aufbaut. Dann wird einfach das Element in der Original-Liste hochgezählt.
[...]
Code (perl): (dl )1 2 3sub void_array { map { $_->[0] eq 'a3333' ? [$_->[0], $_->[1] + 1] : $_ } @stats; }
2019-08-11T22:29:39 FIFOmap (und grep) in leerem Kontext gelten ja auch als etwas anrüchig, weil man sich "nur" der Nebeneffekte bedient. Und weil man's deswegen manchmal falsch macht. So wie ich hier: Einfach die Zuweisung wegzulassen funktioniert so nicht. Peinlich, peinlich.... map() ohne Zuweisung kommt mir nicht so schnell in den Sinn, aber je nach Anwendungsfall ist der Performanceunterschied ja deutlich ...
2019-08-12T12:20:32 FIFOaus Deiner korrigierten Neufassung:
Das funktioniert nur, wenn 'a3333' genau einmal vorkommt, da nur das Element 0 vom gegrepten Ausschnitt aus @stats modifiziert wird.
(edit: In diesem Fall ist die for-Schleife mit Abbruch nach dem ersten Match dann effizienter.)
Wenn 'a3333' nie vorkommt, erhältst Du einen Fehler "Can't use an undefined value as an ARRAY reference ..."
2019-08-12T06:46:49 DaximWer professionell mit Perl arbeitet
2019-08-12T08:45:54 rostiZweckmäßig wäre ein Array mit Hashreferenzen
(sub : lvalue { (grep {$_->[0] eq 'RequestsD'} @stats)[0]->[1] })->()++;
map { $_->[1]++ } grep {$_->[0] eq 'RequestsD'} @stats;
$_->[1]++ for grep {$_->[0] eq 'RequestsD'} @stats;
2019-08-19T11:19:02 Linuxerfor statt map im void-Kontext:
Das sollte auch gehen:
Code (perl): (dl )$_->[1]++ for grep {$_->[0] eq 'RequestsD'} @stats;