2016-09-23 1 views
1

Ich bin relativ neu mit Perl und ich habe seit einigen Tagen mit diesem festgefahren. Hoffe, du kannst mir helfen.Kombinieren Hashes mit Arrays für einige Schlüssel

Ich arbeite mit zwei Dateien, die ich vereinfachen, wie ich sie hatte zu verarbeiten vorher:

file_one mit einer Liste von names (name_1, name_2, NAME_3 ...) und numbers (Zahl_1, Zahl_2, NUMBER_3. ..), die jeweils

zugeordnet

und file_two mit einer Liste von numbers (Zahl_2 und number_6) und items (item_a, item_b mit Zahl_2 verbunden ist, und item_b, item_c mit number_6 assoziiert)

Meine Idee machen sollte Hashes beider Dateien und kombinieren sie. Der Punkt, an dem ich festhalte, ist, wenn ich der Liste der Elemente in den Hash (Hash eines Arrays) beitreten und sie dann verwenden muss. Der erste Hash funktioniert also gut, aber der zweite hat das Problem.

Ich versuchte mit push (@{ $hash2{$numbers} }, $items), aber dann weiß ich nicht, wie man es mit dem anderen wegen der Referenz, die ich verwendete, kombiniert.

Die letzte Aufgabe wäre, zwei Namen zu vergleichen, um zu erhalten, welche Elemente sie teilen. Und es wäre großartig, wenn es nur mit Perl gemacht werden könnte und möglichst keine Module verwenden würde.

Vielen Dank

+1

Das Einfügen eines Ausschnitts der beiden Dateien wäre sehr hilfreich, um das Problem zu verstehen. Sie müssen keine Formatierung vornehmen, markieren Sie sie einfach und drücken Sie '{}' für ein Codebeispiel. – Schwern

+1

Ein Beispiel ist immer besser als eine Beschreibung, solange Sie eine ganze Reihe von Fällen anzeigen. * "file_one" mit einer Liste von Namen (name_1, name_2, name_3 ...) und Nummern (number_1, number_2, number_3 ...) bzw. "* ** Schwern **, unten, scheint ziemlich sicher in seiner Interpretation von Ihrem Wörter, aber ich habe meine Zweifel – Borodin

+1

Es gibt keine Möglichkeit, eine Tabelle in Markdown zu erstellen. Die beste Option wäre das Kopieren und Einfügen mehrerer Zeilen Ihrer Datendateien in Ihre Frage. Wenn Sie alle Zeilen um vier Leerzeichen einrücken (was Sie tun können, indem Sie alle Zeilen markieren und * Strg-K * drücken), werden sie vorformatiert angezeigt. – Borodin

Antwort

2

Wenn ich Sie richtig bin zu verstehen, müssen Sie dies:

foo => 1 
bar => 2 
baz => 3 

Dann haben Sie:

2 => a, b 
3 => b, c 

Und Sie wollen wissen, welche Elemente bar und baz teilen (zum Beispiel).

Eine Option besteht darin, sie in zwei Tabellen in einer SQLite Datenbank zu setzen und SQL zu verwenden. Dies kann der einfachste, flexibelste und leistungsfähigste Weg sein, mit relationalen Daten wie diesem umzugehen. Vor allem, wenn es eine Menge davon gibt und vor allem, wenn Sie viele verschiedene Suchanfragen durchführen möchten. Dadurch wird vermieden, dass eine Menge benutzerdefinierter Code und eine wahrscheinlich zunehmend komplizierte Datenstruktur geschrieben werden müssen.


Doing es in Perl, hier ist eine Skizze.

Zuerst lesen Sie in der zweiten Datei, die die Blätter enthält (die Elemente zeigen nicht auf etwas anderes), in einen Hash der Arrays. Sie winden mit einer Struktur auf wie:

$nums2items{2} = [qw(a, b)]; 

dann in der ersten Datei in einen Hash lesen, sondern die Zahlen als Werte zu speichern, zu speichern, was %nums2items Referenzen.

$names2items{foo} = $nums2items{1}; 

Nun, wenn Sie möchten, zu überprüfen, ob bar und baz etwas teilen, können Sie die Arrays erhalten und ihre Kreuzung mit Array::Utils finden.

use Array::Utils qw(intersect); 

print join ", ", intersect(@{$names2items{bar}}, @{$names2items{baz}}); 

Wenn Sie vorhaben, diese viel zu tun, und die Reihenfolge der Elemente spielt keine Rolle, ist es effizienter, die Elemente als Hash zu speichern. Dies vermeidet, dass zwei Listen sortiert und verglichen werden müssen. Es ist was intersect tut sowieso, verwandelt eine Liste in einen Hash (oder einen Satz) und vergleicht sie mit der anderen Liste.

use strict; 
use warnings; 
use v5.10; 

my %nums2items = (
    2 => { a => 1, b => 1,   d => 1 }, 
    3 => {   b => 1, c => 1, d => 1, e => 1 }, 
); 
my %names2nums = (
    bar => $nums2items{2}, 
    baz => $nums2items{3} 
); 

# Take the intersection in O(n) time. 
say join ", ", grep { $names2nums{bar}{$_} } keys %{$names2nums{baz}}; 

einen Hash wie das Verwenden von, wo der Schlüssel die Sache ist, und der Wert 1 ist, ist eine sehr häufige und effiziente Art und Weise einen Satz darstellt.

Oder Sie können das Modul Set::Tiny verwenden. Es ist sehr geradlinig. Wenn Sie lernen möchten, mit Sets in Perl zu arbeiten, empfehle ich dringend, die Quelle zu lesen.

+0

Ja, im Grunde ist Ihre Interpretation korrekt, danke fürs Antworten. Dafür müsste ich aber nur Perl verwenden, und auch keine Module, da würde ich gerne lernen wie es geht ... ich meine natürlich wenn es möglich ist – Gutinu

+0

@Gutinu das ist natürlich möglich. Die Leute, die Array :: Utils geschrieben haben, haben es geschafft. Lesen Sie die Quelle, um zu sehen, wie das gemacht wird. – simbabque

+3

@Gutinu "Ich will/kann keine Module verwenden" löst eine rote Flagge für Perl-Programmierer aus. Sie sind so wichtig, und viele Orte haben fehlgeschlagen "keine CPAN-Module" -Richtlinien. Um zu lernen, was ich vorschlagen würde, ist es langwierig, dann mach es mit Modulen, dann mach es mit SQLite. Das heißt "Ich möchte es selbst schreiben" und "so würde man es für die Produktion mit einer echten Datenmenge machen". Versuchen Sie selbst 'intersect' zu schreiben, und schauen Sie dann in Array :: Utils (https://metacpan.org/source/ZMIJ/Array-Utils-0.5/Utils.pm) nach, wie sie es gemacht haben. – Schwern

0

Von Ihrem Kommentar zu Schwern es scheint, dass Sie Dateien wie folgt aussehen:

foo, 1 
bar, 2 
biz, 3 
bas, 4 

und

1, jacks blue horse 
2, the green horse 
3, jacks 
4, bing 

und erfolgreich sie in zwei Hashes mit den Werten vor dem Komma als Schlüssel lesen und das nach dem Wert. Nun, was Sie die Wörter paarweise nehmen und Wörter ausdrucken, die sie gemeinsam haben. Sie möchten keine Module verwenden, sondern tun es in rohen Perl.

Erstens, warum ist nicht das zweite ein Array von Arrays anstelle von einem Hash, wenn es numerisch codiert ist?

Zweitens, warum verschmelzen Sie sie? Warum nicht verschachtelte Schleifen verwenden:

my @key_list = keys %hash_1; 
while (@key_list) 
    { 
    my $curr_key = shift @key_list; 
    for my $next_key (@key_list) 
     { 
     my @curr_list = @{$hash_2{$hash_1{$curr_key}}}; 
     my @next_list = @{$hash_2{$hash_1{$next_key}}}; 
     while (@curr_list) 
     { 
     my $curr_word = shift @curr_list; 
     for my $next_word (@next_list) 
      { 
      print "$curr_key and $next_key share $curr_word\n" 
       if $curr_word eq $next_word; 
      } 
     } 
     } 
    } 

Es ist ein bisschen rohe Gewalt, aber es würde den Job erledigt bekommen. Stattdessen könnten Sie die ausgezeichneten Set :: Module verwenden. Ein Teil des Wissens und der Verwendung einer modernen Sprache wie Perl oder C++ ist es, den Standard und die gebräuchlichen Bibliotheken zu kennen und zu benutzen.

+0

"* Warum nicht verschachtelte Schleifen? *" Die Antwort ist, weil es sehr ineffizient und sehr schwierig zu verstehen ist * vier * verschachtelte Schleifen. Ihre Schleife ist mindestens "O (n!)" Und wahrscheinlich schlechter, dh wenn sich die Liste der Tasten verdoppelt, vervierfacht sich die Laufzeit. Schließlich macht das Dereferenzieren aller Listen Kopien von jedem von ihnen, was die Speichernutzung erhöht. Oder Sie sind sehr clever und veranschaulichen, warum man Module verwenden sollte, anstatt sie selbst zu schreiben. :) – Schwern

+0

Ich bin mir der Ineffizienz durchaus bewusst, aber das OP hat angedeutet, dass er Module vermeiden möchte, die nur wenige Methoden über die Brute-Force hinausgehen lassen. Ich bevorzuge Ihre Methode von Arrays :: Utils dazu. Ich würde dazu neigen, die Schnittmenge von Set :: Scalar zu verwenden. Deshalb habe ich am Ende darauf hingewiesen, wie wichtig es ist, verfügbare Module zu kennen und zu nutzen. – HerbN

+0

Ich fügte meiner Antwort eine O (n) Lösung hinzu, die Hashes als Mengen verwendet. Guck mal. – Schwern

Verwandte Themen