borovez has asked for the wisdom of the Perl Monks concerning the following question:

ANSWERED - THANKS Hello, looking at my sortDictionary, it should be reading in a list of words/definitions from the hash array (%dictionary). Well I want to pass each line it creates into another function writeFiles and what that would do is create a file using the first letter of the line passed in and seeing if its part of the alphabet and then writing it to the file. The problem is, this writeFiles function only works once, and once its done, it does not go back to the sortDictionary to continue sorting/making lines and passing them back to WriteFiles again. So it only does it once. But it should do it as many times as there are keys. Thank you for your help.
sub sortDictionary { foreach my $key (sort keys %dictionary) { $key =~ /^(.)/; my $lline = $key . '/' . $dictionary{$key}; #print $lline; $line = $lline; writeFiles($line); } } sub writeFiles { @alphabet=(a..z); $letter = substr($line,0,1); $namestub = "_words"; for ($i; $i<26; $i++); $filename = @alphabet[$i] . $namestub . ".txt"; if ($letter eq @alphabet[$i]){ open FILE, "+>", $filename or die $!; print FILE "$line\n"; print @alphabet[$i]; close FILE; } } return 0; }

Replies are listed 'Best First'.
Re: Function skips
by jwkrahn (Abbot) on Mar 12, 2012 at 04:44 UTC
    writeFiles function only works once

    That is not your problem.

    open FILE, "+>", $filename or die $!;

    The file mode you are using will overwrite the previous contents of the file.    You need to use append mode which will not overwrite the previous contents:

    open FILE, '>>', $filename or die $!;
Re: Function skips
by Marshall (Canon) on Mar 12, 2012 at 07:08 UTC
    A few other issues: this line $key =~ /^(.)/; does nothing because you neither check for success of this match or use the successful result of $1. You can use a slightly different regex to do the job of getting the first letter and checking that it is lower case (I presume that you want to skip proper names like Bob?). That way writeFiles() doesn't have to do that job and it won't need a for loop.

    Also $line should be passed as a parameter to writeFiles - try to minimize the number of global variables.

    This code will work, but opening a file is "expensive". Further work could result in only opening each file one time.

    Also if you had enabled warnings and strictures, you would see that this line
    for ($i; $i<26; $i++); is not right. Also an indexed C style for loop is relatively rare in Perl. for my $letter (a..z){} would be more common in this situation - but as mentioned before, even that is not necessary.

    #!/usr/bin/perl -w use strict; my %dictionary = ('dog' => 'pet', 'horse' =>'animal', 'doggie' => 'also a pet', 'Bob' => 'person'); sortDictionary(); sub sortDictionary { foreach my $key (sort keys %dictionary) { # process only keys with lower case first letter # e.g. 'Bob' => 'person' would be skipped my $first_letter; next unless (($first_letter) = $key =~ /^([a-z])/); my $line = $key . '/' . $dictionary{$key}; print "processing line: $line\n"; writeFiles( $first_letter, $line); } } sub writeFiles { my ($first_letter, $line) = @_; my $filename = $first_letter . "_words.txt"; print "file: $filename adding $line\n"; open FILE, '>>', $filename or die "can not open $filename $!"; print FILE "$line\n"; close FILE; return; } __END__ processing line: dog/pet file: d_words.txt adding dog/pet processing line: doggie/also a pet file: d_words.txt adding doggie/also a pet processing line: horse/animal file: h_words.txt adding horse/animal
Re: Function skips
by Marshall (Canon) on Mar 12, 2012 at 12:12 UTC
    I now see that this is a follow-up question to hash array.

    There is no need to create an intermediate hash. Read the data, sort it, and then "funnel the lines" in to the various output files. Only one file needs to be open at once - this will vastly increase the speed of the code.

    There are techniques to modify the sort order. But that should be a separate SOPW (Seeker of Perl Wisdom) question. The default sort order here looked "ok" to me.

    There is no need to close FILE, when changing that file handle to a different location (the open() does that automatically). The last FILE will be closed with when the program exits.

    #!/usr/bin/perl -w use strict; use constant DEBUG => 1; my @data = sort <DATA>; # bring all data into memory and sort # other techniques are possible if this # file is so big that this is not feasible my $current_first_letter = ''; foreach my $line (@data) { print "Processing $line" if DEBUG; # # skip entries not beginning with lower case a-z # my $this_first_letter; next unless ( ($this_first_letter) = $line =~ /^([a-z])/ ); # # Start a new file when we change first letters # if ($this_first_letter ne $current_first_letter) { my $filename = $this_first_letter . "_words.txt"; print "Creating New File...$filename...\n" if DEBUG; #this over-writes any existing file of the same name # open FILE, '>', $filename or die "can not open $filename $!"; $current_first_letter = $this_first_letter; } # # write to the current file # print "....adding $line"; print FILE $line; } =prints -- look in the output files for the rest.. -- set DEBUG =>0 to supress this ouput Processing Bob/person Processing d/some fictional animal Creating New File...d_words.txt... ....adding d/some fictional animal #a sort test case # Processing dog/pet ....adding dog/pet Processing doggie/also a pet ....adding doggie/also a pet Processing horse/an animal Creating New File...h_words.txt... ....adding horse/an animal =cut __DATA__ dog/pet d/some fictional animal horse/an animal doggie/also a pet Bob/person