280 likes | 422 Views
The Hash Cookbook. Ingredients What goes into a hash Basic manipulations Recipes Test for presence of a value in a list Test for uniqueness Difference in lists Sort in any order Use table look-up instead of many if –elsif call subroutines by reference. Ingredients. What is a Hash?.
E N D
Ingredients • What goes into a hash • Basic manipulations • Recipes • Test for presence of a value in a list • Test for uniqueness • Difference in lists • Sort in any order • Use table look-up instead of many if –elsif • call subroutines by reference
What is a Hash? • Not something you smoke • Not something you eat • Not # • Key , Value Pairs • AssociativeArrays • a direct language supported data type • The third major data type in Perl • work very similarly to hash tables (named for hash functions)
Initialize (clear, or empty) a hash my %hash = (); Add a key/value pair to a hash $hash{ $key } = $value; Add several key/value pairs to a hash %hash = ( ‘key1’ => 'value1', ‘key2’ => 'value2', ‘key3’ => 'value3', ); Delete a single key/value pair delete $hash{$key}; Copy a hash my %hash_copy = %hash; Get each key foreach $key (keys (%hash)) { print " $key => $hash{$key}; }
Get each value foreach $value (values(%hash)) { print " $value \n";} Get each key/value pair while ( my ($key, $value) = each(%hash)) {print "$key => $value\n";} Get all of the values or keys @all_keys=keys(%hash); @all_values=values(%hash); Get some of the values (slice) @slice=@hash{$key2,$key3}; # @slice is now ($value2,$value3) Get the size of a hash my $size = keys( %hash); if a hash value exists, is defined, or is true if exists $hash{ $key } if defined $hash{ $key } if $hash{ $key }
Recipes These Recipes show where hashes can be used to solve some common programming challenges.
Test for presence of a value in a list green blue yellow red orange @color_ary green 1 @color_hash blue 1 yellow 1 1 red orange 1
the array way my @color_ary = qw(green blue yellow red orange); my $search=shift; #e.g. red for my $item (@color_ary){ if ($item eq $search) { print "Found $search\n"; exit; } }
the hash way my @color_ary = qw(green blue yellow red orange); put the array in a hash as keys arrays can be read into hashes once, and the hash checked any number of times in the program my %color_hash;for my $item (@color_ary) {$color_hash{$item} = 1; # 1 or any "true" value} find what we are looking for as the key of a hash my $search=shift; if ($color{$search}) {print "Found $search\n";} another way to put the array in the hash my %color_hash = map { $_, 1 } @color_ary;
A A C D A C A A B B A A D C B A C D B Test for uniqueness @list 6 %seen 1 2 2 @uniq
Use a hash to record which items have been seen in a list and how many times my @list=@_; my %seen = (); my @uniq = (); foreach my $item (@list) { push(@uniq, $item) unless $seen{$item}++; } if the item has never been seen it will be 0 before 1 is added to it and therefore pushed into the array. The next time it won’t be. return @uniq; #to find the unique values other ways to do it %seen = (); @uniqu = grep { ! $seen{$_} ++ } @list; List::MoreUtils uniq @list
CAT HAT BAT MAT RAT HAT MAT DOG RAT CAT PIG HAT BAT MAT Difference in lists @A @B DOG 1 %seen RAT 1 CAT 1 PIG 1 @aonly
Find elements in @A that aren't in @B \@A=shift; \@B=shift; %seen = (); # lookup table of items in @B @aonly = (); #build hash table foreach $item (@B) { $seen{$item} = 1 } #Add when not in %seen foreach $item (@A) { push(@aonly, $item) unless ($seen{$item}); } return @aonly;
ox butcher cat cat dog water The_Holy_One 1 angel_of_death 2 butcher 3 ox 4 water 5 fire 6 stick 7 dog 8 cat 9 kid 10 water dog cat cat butcher ox Sort in any order @unsorted @compar @sorted
sort in any order, that is not alphabetical, or numeric my %compar = ('The_Holy_One' => 1, 'angel_of_death' => 2, 'butcher' => 3, 'ox'=> 4, 'water'=>5, 'fire'=>6, 'stick'=>7, 'dog'=>8, 'cat'=>9, 'kid'=>10); my @unsorted = @_; my @sorted = sort any_order @unsorted; return @sorted; sort subroutine compares the hash values sub any_order{ $compar{$a} <=> $compar{$b}; }
One Nine Eight Four zero 0 one 1 un 1 1 une nine 9 neuf 9 Improve the logic of your program Use table look-up instead of many if –elsif example translates numbers from French or English to digits @words %num_for $num 1984
the if-else way my ($words)=@_; my $num; for my $words (@word){ if ($word eq ‘zero’){ $num .= ‘0’; } if ($word =~ /(one|un|une)/){ $num .= ‘1’; } elsif ($word =~ /(two|deux)/){ $num .= ‘2’; } #etc..... elsif ($word =~ /(nine|neuf)/){ $num .= ‘9’; } return $num;
the hash way my %num_for=(‘zero'=>0,’one'=>1,'un'=>1,'une'=>1,'two'=>2, 'deux'=>2,’trois’=>3,’three’=>3,’quatre’=>4, ‘four’=>’4’, ’cinq’=>5,’five’=>5, ’six’=>6,’sept’=>7, ’seven’=>7,’huit’=>8, ’eight’=>8,'nine'=>9,'neuf'=>9); my ($words)=@_; my $num; foreach my $wrd(@words){ my $digit=$num_for{lc $wrd}); if (defined $digit){ $num .=$digit; } } return $num;
j (pointer to jump) h (pointer to hop) s (pointer to sit) calling subroutines by reference Use a hash instead of symbolic reference with a dispatch table you can keep strict references and can use a different name for the sub, but must include all of the subs in a hash.
symbolic reference (name of the subroutine is itself the reference) my $func = shift; my $arg=shift; no strict 'refs'; #assuming that you have use strict $func->( $arg ) if defined &$func; use strict 'refs'; sub jump{print "jump $arg!\n";} sub hop {print "hop $arg!\n";} sub sit {print "sit $arg!\n";}
The hash way –actual reference (reference is a pointer to the code) my %subs=('j' => \&jump, 'h'=>\&hop,'s'=>\&sit); my $func = shift; my $arg=shift; $subs{$func}->($arg) if exists $subs{$func}; sub jump{print "jump $arg!\n";} sub hop {print "hop $arg!\n";} sub sit {print "sit $arg!\n";}
Also: • Change data in files, • have mapped parameters • And much more …
References • Programming Perl (3rd Edition) by Larry Wall, Tom Christiansen, and Jon Orwant • Perl Cookbook, Second Edition by Tom Christiansen and Nathan Torkington • Perl Best Practices by Damian Conway
http://effectiveperl.blogspot.com/2006/01/idiomatic-perl-hash-as-set.htmlhttp://effectiveperl.blogspot.com/2006/01/idiomatic-perl-hash-as-set.html • http://www.devshed.com/c/a/Perl/Hash-Mania-With-Perl/ • http://www.cs.mcgill.ca/~abatko/computers/programming/perl/howto/hash/ • http://www.perl.com/pub/a/2006/11/02/all-about-hashes.html • http://www.ebb.org/PickingUpPerl/pickingUpPerl_6.html • http://www.unix.org.ua/orelly/perl/learn/ch05_04.htm
Our Southwestern-inspired Turkey Sweet Potato Hash is a satisfying and flavorful recipe for turkey leftovers. 1 1/2 cups roughly chopped baked sweet potatoes (about 1 1/2 medium potatoes) 2 1/2 cups shredded cooked turkey 1/4 cup chicken broth 1/2 medium yellow onion, grated 2 cloves garlic, minced 3 tablespoons chopped fresh coriander , plus leaves for garnish 1/3 cup tofu crumbled 1 large egg white 2 teaspoons salt Freshly ground black pepper 1 tablespoon oil, such as soy, peanut, vegetable or corn 2 tablespoons margarine 2 scallions (white and green), thinly sliced Turkey Sweet Potato Hash
Equipment: 10-inch non-stick skillet with oven-proof handle Preheat the oven to 400 degrees F. Mash 1 cup of the sweet potatoes in a large bowl with a fork. Add the remaining 1/2 cup of sweet potatoes, the turkey, broth, onion, garlic, coriander, 1/4 cup of the tofu, and the egg white. Season with the salt and pepper. Stir briskly to combine. Heat the oil and 1 tablespoon margarine in the skillet over medium heat. Add the hash and form it into a round cake the size of the pan with a spatula. Cook, shaking the skillet occasionally, until the bottom sets, about 3 minutes. To turn the hash, set a plate the size of the skillet on top of the pan. Invert the pan so the hash falls intact onto the plate. Melt the remaining tablespoon margarine in the skillet. Slide the hash back into the skillet cooked-side up. If the hash breaks apart, simply re-form the cake with the spatula. Cook, shaking the skillet occasionally, for 2 minutes. Transfer the skillet to the oven and bake the hash until firm, about 10 minutes. Carefully, invert the hash onto a serving plate. Scatter the scallions, coriander, and the remaining tofu on the top