http://qs1969.pair.com?node_id=445513


in reply to Continuing from "Turning foreach into map?" - perlreftut and References

As was already pointed out, this case is simple enough that you could do a lot with simple hashes, without the need for references. For example, if you wanted a way to determine what group a particular food belonged to, you could set up a hash whose key => value pairs would be food names and their corresponding food groups. What makes this particular problem simple is that each food can belong to only one group.

To make the problem a bit more difficult, suppose that you had additional food groups and that they overlapped, so that any one food could belong to more than one group. For example, one additional group could be "Vegetarian", so that a food like "tofu" would belong both in "proteins" and "Vegetarian". In such a situation it would be useful to have a way to answer the question such as "does food X belong in group Y". For this you could use a hash of hashes. In this HoH, lets call it groups_to_foods the primary keys would be the food group names, and the secondary key would be the food names. You would assign to all of these ordered pairs a value of 1. The value is not important, since all you want to do is check (with defined) that some value has been entered for a particular combination of food group and food name (thereby answering the question of whether a the food of that name belongs to the group).

Here are a few more simple follow-up exercises. Given the HoH food_to_groups described above, what would be the Perl code to answer the question "does 'Cream' belong to group 'proteins'"? How about for the question "how many foods are in the group 'carbohydrates'"?

If you got the last question, you may wonder how one would code the solution to the opposite type of question, e.g. how many groups does 'tofu' belong to? One way to answer this question would be to count the number of groups Y for which the question "does 'tofu' belong in group Y" comes out true. But if your program had to deal with this type of question a lot, you may want to set up the inverse look up table. This would another HoH, but for this one the primary keys would be foods, and the secondary keys would be the food groups each food belonged to. Suppose that you had the first look up hash groups_to_foods, how would you populate the inverse lookup foods_to_groups HoH?

OK, here's is the next level. Suppose that, after setting up the first HoH above, groups_2_foods, you wanted to change all the primary keys (the group names) to all uppercase. Of course, you could just do the whole thing over with the new uppercased group names, but this would not be very interesting or instructive. There is a way to solve this problem that requires only moving around references. To use this approach the functions delete and uc would come in handy.

A third follow-up exercise: You are so pleased with the solution to the last exercise that you decide to write a sub that will uppercase the keys of any input hash. How would you do it? There is a gotcha in this problem: what if the original hash has both keys 'foo' and 'FOO'? Or, worse yet, keys 'foo', 'Foo', and 'FOO'? To begin with, however, solve the problem under the assumption that the keys of the original hash are all-lowercase, so no collisions can occur when you uppercase them. Once you get that to work you can think of how to handle the more general case.

Last exercise, maybe a bit more advanced, but if you have read perlreftut you should have all the background you need to solve it. You are very impressed with the usefulness of the function that you coded for the third exercise, but you notice that you could do something more general. What if instead of having the sub uppercase the keys of a hash, it modified these keys in some other arbitrary way, which you could specify by passing a second subroutine to the first subroutine? As with the previous exercise, there is the risk of collisions when you modify the keys, but if you figured out a way to deal with this problem in that exercise, then you may be able to use the same approach here. This last subroutine would be similar in spirit to my_map from Re: Turning foreach into map?, and it would serve as a nice segue back that post.

I'll post my solutions in a follow-up node. If you get stuck, just study the solution.

the lowliest monk