Strukturerhaltendes "Tiefen"-map

15/11/2009 - 12:07 von Tobias Nissen | Report spam
Moin!

Ich habe ein fast inhaltsgleiches Posting schon gestern abgesetzt. Aber
irgendwie scheinen die NNTP-Server hier (Uni) zu spinnen. Google hat's
jedenfalls nicht, also nochmal über einen anderen Anbieter. Das gab mir
auch die Gelegenheit ein paar Sachen zu àndern.

Ich möchte eine Datenstruktur bestehend aus Strings, Arrays,
Array- und Hashreferenzen durchlaufen und Werte darin àndern. Also aus

$VAR1 = {
'eins' => 'zwei',
'drei' => 'vier',
'fünf' => [
'sechs', 'sieben', [
'acht',
{ 'neun' => 'zehn' }
]
]
};

soll dann etwa

$VAR1 = {
'eins' => 'ZWEI',
'drei' => 'VIER',
'fünf' => [
'SECHS', 'SIEBEN', [
'ACHT',
{ 'neun' => 'ZEHN' }
]
]
};

werden, wenn das Ziel ist, alle Strings in Großbuchstaben zu wandeln.
Welche Transformation dabei letztlich angewendet wird, soll natürlich
definierbar sein.

Strukturerhaltend soll hierbei heißen, dass sich alle Datentypen an der
gleichen Stelle wie vorher befinden¹ und dass auf Hashes wie vor der
Transformation zugegriffen werden kann (der Schlüssel soll also nicht
veràndert werden). Ich mache das bisher mit folgendem rekursiven Code
(lauffàhig unter http://paste.debian.net/51563/ verfügbar):

my $a = {
eins => 'zwei',
drei => 'vier',
'fünf' => [
'sechs',
'sieben',
[ 'acht', { neun => 'zehn' } ]
]
};

upupup($a);

sub upupup {
# fall 1, @_ ist ein array
return map {upupup($_)} @_ if scalar(@_) > 1;

# fall 2, @_ ist eine arrayreferenz
return [map {upupup($_)} @{$_[0]}]
if scalar(@_)==1 and ref($_[0]) eq 'ARRAY';

# fall 3, @_ ist eine hashreferenz
if (scalar(@_)==1 and ref($_[0]) eq 'HASH') {
foreach my $key (keys %{$_[0]}) {
$_[0]->{$key} = upupup($_[0]->{$key});
}
return $_[0];
}

# sonst ist @_ ein string
return uc($_[0]);
}

Der Code erfüllt die Aufgabenstellung, ist aber nicht schön. Dazu
einige Fragen:

1. Ich kann das doch nicht alles mit einem einzigen map abfrühstücken,
richtig? D.h. ich muss die Datenstruktur von Hand durchlaufen und
wieder eins zu eins zusammensetzen, ja? Mit anderen Worten, Perls
map verhàlt sich so, wie man's von map² erwartet?

2. Die if-Statements und das Hin- und Hercasten ist selten dàmlich.
Geht das schöner?

3. Fall 3 geht sicherlich mit weniger Syntax. Aber kann man da auch ein
platzsparendes map benutzen? Ich sehe jedenfalls nicht wie.

4. Gibt es ein Modul welches diese Funktionalitàt (also eine Art
"Tiefenmap", etwa ein etwas abgespecktes foldr³) schon bietet?

Schöne Grüße!
Tobias

¹ die Reihenfolge der Hashes soll natürlich egal sein :-)
² http://en.wikipedia.org/wiki/Map_(higher-order_function)
³ http://en.wikipedia.org/wiki/Fold_(higher-order_function)
 

Lesen sie die antworten

#1 Tobias Nissen
15/11/2009 - 12:32 | Warnen spam
Tobias Nissen wrote:
[...]
upupup($a);



Das ist so natürlich mit dem ursprünglichen Code Quark und funktioniert
nur mit der gegebenen Hashreferenz.

Ich wollte die Datenstruktur eigentlich gar nicht direkt mutieren. So
ist's besser (siehe http://paste.debian.net/51566/):

diff --git a/deepmap.pl_old b/deepmap.pl
index 8eff265..ea040cf 100644
a/deepmap.pl_old
+++ b/deepmap.pl
@@ -18,7 +18,7 @@ my $a = {

print "Vorher: ".Dumper($a);

-upupup($a);
+my $b = upupup($a);

sub upupup {
# fall 1, @_ ist ein array
@@ -30,15 +30,16 @@ sub upupup {

# fall 3, @_ ist eine hashreferenz
if (scalar(@_)==1 and ref($_[0]) eq 'HASH') {
+ my $newhashref;
foreach my $key (keys %{$_[0]}) {
- $_[0]->{$key} = upupup($_[0]->{$key});
+ $newhashref->{$key} = upupup($_[0]->{$key});
}
- return $_[0];
+ return $newhashref;
}

# sonst ist @_ ein string
return uc($_[0]);
}

-print "Nachher: ".Dumper($a);
+print "Nachher: ".Dumper($b);

Ähnliche fragen