2013-11-28 18 views
0

具體地說我的數據結構如下所示如何使用子例程在複雜的數據結構上全局工作?

{ 
    "SomeGuy" => { 
    date_and_time => "11-04-2013", 
    Id => 7, 
    nr => 52, 
    picks => [ 
     { "This is an option" => "Option3" }, 
     { "This is another option" => "Option4" }, 
     { "This is another option" => "Option1" }, 
     { "And another one" => "Something" }, 
    ], 
    time_of => "06:11 AM", 
    total => 1, 
    }, 
    "Another Guy" => { ... }, 
} 

這是輸出經由Data::Dump。實際的數據結構包含更多的記錄,如"SomeGuy"。所有這些結構都是相同的。

我填充這個數據結構是這樣的:

$guys->{$profile}{options}[$total++]{$var} = $var2; 
$guys->{$profile}{Id} = $i; 
$guys->{$profile}{date_and_time} = get_date($Time[0]); 
$guys->{$profile}{time_of} = $Time[1]; 
$guys->{$profile}{total} = keys (% {$guys->{$profile}{options}[0]}); 
$guys->{$profile}{nr} = $pNr; 

擁有這一切,我想接下來做的就是對這個數據結構進行操作。我重申,數據結構中有許多記錄。

當我輸出它的內容時,我得到它混亂的順序,而不是按它填充的順序。我已經用Data::DumperData::Dump和我自己手動迭代記錄來嘗試。

我知道,在Data命名空間的方法是臭名昭著的這一點,這就是爲什麼Data::Dumper提供了一種方法,通過一個子程序進行排序,並Data::Dump提供一個默認的。

所以我有數據結構。它看起來像我期望的那樣,我知道所有的數據,因爲我知道它應該看起來有效。我想根據他們的Id字段對記錄進行排序。我的想法是,我必須使用子例程,並基本上將數據結構的引用傳遞給它並在那裏進行排序。

sub sortt { 
    my $dref = shift @_; 
    foreach my $name (sort { $dref->{$a}{Id} <=> $dref->{$b}{Id} } keys %$dref) { 
     print "$data->{$name}{Id}: $name \n"; 
    } 
} 

調用此方法同(在填充結構相同的範圍內,所以不用擔心有):

sortt(\$guys); 

的錯誤是:

Not a HASH reference at perlprogram.pl line 452 

所以我去在子程序中使用ref以確保我傳遞了實際參考。它說REF

接下來,我進入絕望的模式,並嘗試像調用它的一些愚蠢的事情:

sortt(\%$guys) 

但是如果我沒有弄錯這只是發送一個拷貝到子程序只是在本地複製排序,所以那裏沒有用處。

如果我從子程序中創建一個副本並將其返回,我只想傳遞一個我的數據結構的引用並對其進行排序並使其反映全局(或調用範圍本身)中的這些更改是沒有用的。我將如何做到這一點?

+0

要排序的鑰匙,但你用排序鍵做的是打印出來。你想改變* hash元素的順序*,但這是不可能的。你真的應該知道什麼是[hash](http://en.wikipedia。org/wiki/Hash_table)是。 – ikegami

回答

0

$guys已經是一個哈希裁判,所以你只需要sortt($guys)

如果你想有一個排序的數據結構,你需要的東西是這樣的:

my @guys_sorted = 
    map { { $_ => $guys->{$_} } } 
    sort { $guys->{$a}{Id} <=> $guys->{$b}{Id} } keys %$guys; 

print(Dumper(\@guys_sorted)); 

或者,在一個子:

sub sortt { 
    # returns a SORTED ARRAY of HASHREFS 
    my $ref = shift; 
    return map { { $_ => $ref->{$_} } } 
      sort { $ref->{$a}{Id} <=> $ref->{$b}{Id} } keys %$ref; 
} 

print(Dumper([sortt($guys)])); 
+0

我試過了,但還是沒有排序。 Data :: Dumper和手動通過它以無序的方式輸出它。我知道sortt sub是正確的,因爲我在本地打印它,並且它確實按照正確的順序進行打印。 – user3046061

+0

把正確的順序放在什麼地方?你是說'sortt'是否會改變數據結構?因爲它現在甚至沒有試圖這樣做 – ikegami

+0

鑰匙,「夥計們」。我希望他們按照他們的「Id」值升序排列。我想讓我的所有結構都按這個值排序,並且改變其他所有內容,而不僅僅是某個級別的鍵。 – user3046061

4

撇開你的語法問題「不是裁判」,你是從在第一時間錯誤的一端接近的問題。我會留下小的語法細節給其他人(見池上的評論)。

你不能所有的人進行排序,因爲$guys哈希,而不是一個數組。哈希是不是有史以來在Perl中排序。如果您想對其進行分類,您有三種解決方案:

  1. 將名稱的有序列表存儲爲單獨的陣列。

    my @ordered_names = sort { $guys->{$a}{Id} <=> $guys->{$b}{Id} } keys %$guys

    然後你使用順序排列,並返回到hashref個人記錄。

  2. 添加名稱,以個人傢伙的哈希值,而不是外散列引用。 $guys應該是數組引用。這種方法的缺點是,你不能再通過他們的名字查找一個人的記錄 - 如果需要該功能,請使用#1。

    @ codnodder的回答顯示瞭如何做到這一點,如果你不關心通過名字來訪問記錄。

  3. 使用Tie::*模塊。 (Tie::IxHash,Tie::Hash::Sorted)。不推薦,因爲它比較慢。

+0

就像我說的,我真的需要通過名稱鍵訪問功能。雖然搜索時發現有人提到Tie :: *命名空間,但問題是我真的需要堅持使用基本的Perl(或者加上更常見的模塊)。這只是緩慢地處理這樣簡單。如果我事先知道散列無序屬性,那麼你的第一個選項就顯而易見了。以我的藉口來說,這個愚蠢的句法錯誤一定會使我的判斷完全混亂。 – user3046061

+0

@ user3046061啊,所以你要求我們在這裏複製一些CPAN模塊?爲什麼? – ikegami

+0

我說實話,我只是沒有考慮過爲什麼我沒有得到參考權利。 – user3046061

0

這是一個非常複雜的數據結構。如果你通常在你的程序中使用這樣的結構,我建議你把你的Perl編程技巧提高一些,然後研究一下Object Oriented Perl

面向對象的Perl相當簡單:您的對象僅僅是您之前創建的數據結構。方法只是與該對象一起工作的子例程。大多數方法都是getter/setter方法來設置你的結構。

最初,它是有點多寫,但一旦你得到了它的竅門,額外的書寫很容易被保存補償是調試和維護你的代碼。

面向對象的Perl做了兩件事:它首先確保你的結構是正確的。例如:

$some_guy->{Picks}->[2]->{"this is an option"} = "Foo!"; 

哎呀!那應該是{picks}。想象一下,試圖在你的代碼中發現錯誤。

在OO Perl的,如果我輸錯一個方法的名稱,該程序將立即把它撿起來:

$some_guy->picks(
    { 
     "This is an option" -> "Foo!", 
     "This is option 2" => "Bar!", 
    } 
) 

如果我有$some_guy->Picks,我會得到一個運行時錯誤。

它也讓你認爲你的結構作爲對象。例如,你在排序什麼?你正在對你的夥計ID進行排序,並且這些傢伙被存儲在一個名爲%guys的散列中。

# $a and $b are hash keys from `%guys`. 
# $guys{$a} and $guys{$b} represent the guy objects. 
# I can use the id method to get they guys' IDs 
sort_guys_by_id { 
    guys{$a}->id cmp guys{$b}->id; #That was easy! 
} 

看看tutorial。你會發現自己編寫的程序更好,錯誤更少。

+0

我讀過Ovid的書,它看起來像Perl中的默認OO有點麻煩,我承認在考慮這一點時我記住了這一點。有沒有跡象表明這兩種方法如何影響速度?多久我應該退出使用普通的數據結構?我看過一些代碼,其中有一個4級的代碼。 (我在那裏有3我認爲) – user3046061

+1

這不是一個答案。這是指導[我不會從這裏開始玩笑](http://simonkidd.wordpress.com/2010/08/12/if-i-were-you-i-wouldnt-start-from - /) – Borodin

+0

這個問題已經回答。問題在於OP錯過了理解他自己的數據結構,並指出了錯誤的數據結構。當你有高度複雜的結構時,這是一個常見問題。如果OP將具有高度複雜的結構,則OP應該開始使用面向對象的方法。 –

1

Perl散列是天生無序的。你無法對它們進行排序,或根本沒有對它們進行重新排序。您必須編寫代碼才能按所需順序訪問元素。

你的sortt子程序除了打印ID和每個散列元素的名稱(按ID排序)之外什麼也不做。除此之外,因爲您在嘗試使用變量$data,當您實際設置了$dref時。這很可能是您的Not a HASH reference錯誤的原因,但除非您顯示完整的代碼或至少指出哪一個是perlprogram.pl line 452,否則我們無法進一步提供幫助。

做你想做的事情的最好方法是創建一個散列鍵的數組,你可以按你想要的順序排序。像這樣

my @names_by_id = sort { $guys->{$a}{Id} <=> $guys->{$b}{Id} } keys %$guys; 

然後你可以用它來訪問散列的排序順序,這樣,它打印相同的輸出作爲sortt打算,但格式多一點很好

for my $name (@names_by_id) { 
    printf "%4d: %s\n", $guys->{$name}{Id}, $name; 
} 

如果您想對排序順序中的哈希元素進行其他操作,那麼您必須使用這種技術。

+0

迄今爲止接受的答案工作得很好。你可能需要在這種第二部分$ b。我手寫了大部分OP,所以會出現一些錯誤。 – user3046061

+0

@ user3046061:謝謝。你的診斷是正確的,我已經修復了我的帖子。我只檢查它會編譯 – Borodin

0

將你的心放在一個有序的數據結構上,我建議如下。它是一個簡單的散列數組,而不是使用name字符串作爲單元散列的關鍵字,它爲每個人的數據添加了一個新的name鍵。

我希望,Data::Dump輸出,它是不言自明的。它是由Id場按照您的要求排序,但它仍然的缺點是單獨的索引陣列將允許在所有任何排序沒有修改或複製原始散列數據。

use strict; 
use warnings; 

use Data::Dump; 

my $guys = { 
    "SomeGuy " => { 
    date_and_time => "11-04-2013", 
    Id => 7, 
    nr => 52, 
    picks => [ 
     { "This is an option" => "Option3" }, 
     { "This is another option" => "Option4" }, 
     { "This is another option" => "Option1" }, 
     { "And another one" => "Something" }, 
    ], 
    time_of => "06:11 AM", 
    total => 1, 
    }, 
    "Another Guy" => { Id => 9, nr => 99, total => 6 }, 
    "My Guy" => { Id => 1, nr => 42, total => 3 }, 
}; 

my @guys_sorted = map { 
    my $data = $guys->{$_}; 
    $data->{name} = $_; 
    $data; 
} 
sort { 
    $guys->{$a}{Id} <=> $guys->{$b}{Id} 
} keys %$guys; 

dd \@guys_sorted; 

輸出

[ 
    { Id => 1, name => "My Guy", nr => 42, total => 3 }, 
    { 
    date_and_time => "11-04-2013", 
    Id => 7, 
    name => "SomeGuy ", 
    nr => 52, 
    picks => [ 
     { "This is an option" => "Option3" }, 
     { "This is another option" => "Option4" }, 
     { "This is another option" => "Option1" }, 
     { "And another one" => "Something" }, 
    ], 
    time_of => "06:11 AM", 
    total => 1, 
    }, 
    { Id => 9, name => "Another Guy", nr => 99, total => 6 }, 
] 
+0

我沒有使用這樣的東西,因爲我找不到一個令人信服的理由。我所使用的結構與接受的答案中的結構類似,更好地映射到我想要查看和思考數據的方式。另外,如果我不得不採取替代方案,那麼我最終會寫出更少的代碼,這對我來說很重要。僅僅在Data :: Dump上打印更漂亮的事實對於調試來說是一個優勢。這總是很重要。 – user3046061