Subroutine interaction

Ask a Question related to PERL Beginners, Design and Development.

  1. #1

    Default Subroutine interaction

    Hi all,

    the following structure does not work as intended:

    sub read_index {

    my $gedicht_path = shift;
    my (%author_indexlines, %author_headlines, %gedicht_lines, @author_letters, @gedicht_letters, @alphabet_index);
    find (\&read_poem, $gedicht_path);
    print %author_indexlines; # nothing is printed out here!
    return \(%author_indexlines, %author_headlines, %gedicht_lines, @author_letters, @gedicht_letters, @alphabet_index);
    }


    sub read_poem {

    # scalars to read from the poem page
    my ($vorname, $nachname, $title, $namekey, $titlesort_string, $nameletter, $titleletter, $page, $relative_path);

    my $pathname = $File::Find::name;
    return 0 unless $pathname =~ /\.html$/;

    # definition of the file path relative to the $gedicht_path
    $relative_path = $pathname;
    $relative_path =~ s#$gedicht_path/##; # Perl complains about uninitialized value here
    [filling the hashes and arrays declared above]
    print %author_indexlines; # the hash is printed correctly here
    }

    I expect the variable $gedicht_path to be accessible inside the read_poem subroutine, since it is called from within read_index, it is not (I get an 'uninitialized value' error).

    Second, I expect the hashes and arrays in declared in read_index to be filled in read_poem. read_poem does not complain about undeclared variables, and I can print the appropriate %author_indexlines etc. from within read_poem, but not from read_index, as intended.

    Can anyone point me to the (probably obvious) problem with the locally scoped variables?

    Thanks,

    Jan
    --
    Either this man is dead or my watch has stopped. - Groucho Marx
    Jan Eden Guest

  2. Similar Questions and Discussions

    1. subroutine problem
      Hi all, I have recently started to learn perl. After reading Randal Schwartz’s Learning perl, I decided to give my first program a whirl. Upon...
    2. undefined subroutine
      I'm doing a php to pl migration and having a little trouble. Help appreciated! Undefined subroutine &main::fopen called at...
    3. how to trace a subroutine?
      I have a subroutine "foo" in a file "my.pl". I invoke it by: require "my.pl"; &foo; This has worked fine for a while. Now I try to make...
    4. reference to a subroutine in @INC
      Hi, I am trying to package a perl script and the modules it uses , in a tar file. When untarred on any machine, the modules can be found in a known...
    5. Subroutine as a new Task
      Hello, how can I start a soubroutine as a new task. The main script should run on until the end is reached. But the subroutine should run in...
  3. #2

    Default Re: Subroutine interaction

    On Jan 28, Jan Eden said:
    >sub read_index {
    > my $gedicht_path = shift;
    > my (%author_indexlines, %author_headlines, %gedicht_lines, @author_letters, @gedicht_letters, @alphabet_index);
    > find (\&read_poem, $gedicht_path);
    > print %author_indexlines; # nothing is printed out here!
    The only way that hash can get populated outside of this function is if it
    you pass it by reference to another function, and populate it THERE. A
    my() variable is scoped lexically -- that is, physically. It only exists
    here, inside this block of code, unless you send its reference to another
    scope.
    > return \(%author_indexlines, %author_headlines, %gedicht_lines, @author_letters, @gedicht_letters, @alphabet_index);
    >}
    >
    >sub read_poem {
    >
    > # scalars to read from the poem page
    > my ($vorname, $nachname, $title, $namekey, $titlesort_string, $nameletter, $titleletter, $page, $relative_path);
    >
    > my $pathname = $File::Find::name;
    > return 0 unless $pathname =~ /\.html$/;
    >
    > # definition of the file path relative to the $gedicht_path
    > $relative_path = $pathname;
    > $relative_path =~ s#$gedicht_path/##; # Perl complains about uninitialized value here
    > [filling the hashes and arrays declared above]
    You're not filling the same hashes and arrays; you're filling GLOBAL
    hashes and arrays that happen to have the same name. If you were using
    'strict', you'd be told about that.
    > print %author_indexlines; # the hash is printed correctly here
    >}
    >
    >I expect the variable $gedicht_path to be accessible inside the read_poem
    >subroutine, since it is called from within read_index, it is not (I get
    >an 'uninitialized value' error).
    It's not, because it exists ONLY in the read_index() function.
    >Second, I expect the hashes and arrays in declared in read_index to be
    >filled in read_poem. read_poem does not complain about undeclared
    >variables, and I can print the appropriate %author_indexlines etc. from
    >within read_poem, but not from read_index, as intended.
    What warnings about undeclared variables are you expecting? Unless you
    have 'strict' on, Perl doesn't stop you from saying, out of the blue,

    push @foo, 'this';
    >Can anyone point me to the (probably obvious) problem with the locally
    >scoped variables?
    One way you can fix your situation is to use a closure -- an anonymous
    code block created inside read_index():

    sub read_index {
    my $gedicht_path = shift;
    my (%author_indexlines, %author_headlines, %gedicht_lines,
    @author_letters, @gedicht_letters, @alphabet_index);

    my $read_poem = sub { # notice, no NAME for the sub
    # scalars to read from the poem page
    my ($vorname, $nachname, $title, $namekey, $titlesort_string,
    $nameletter, $titleletter, $page, $relative_path);

    my $pathname = $File::Find::name;
    return 0 unless $pathname =~ /\.html$/;

    # definition of the file path relative to the $gedicht_path
    $relative_path = $pathname;
    $relative_path =~ s#$gedicht_path/##;

    # do stuff with hashes and arrays
    };

    find ($read_poem, $gedicht_path);
    print %author_indexlines; # now it'll work

    # return whatever
    }

    --
    Jeff "japhy" Pinyan [email]japhy@pobox.com[/email] [url]http://www.pobox.com/~japhy/[/url]
    RPI Acacia brother #734 [url]http://www.perlmonks.org/[/url] [url]http://www.cpan.org/[/url]
    <stu> what does y/// stand for? <tenderpuss> why, yansliterate of course.
    [ I'm looking for programming work. If you like my work, let me know. ]

    Jeff 'Japhy' Pinyan Guest

  4. #3

    Default Re: Subroutine interaction

    Jan Eden wrote:
    >
    > the following structure does not work as intended:
    >
    > sub read_index {
    >
    > my $gedicht_path = shift;
    > my (%author_indexlines, %author_headlines, %gedicht_lines, @author_letters, @gedicht_letters, @alphabet_index);
    > find (\&read_poem, $gedicht_path);
    > print %author_indexlines; # nothing is printed out here!
    > return \(%author_indexlines, %author_headlines, %gedicht_lines, @author_letters, @gedicht_letters, @alphabet_index);
    > }
    >
    >
    > sub read_poem {
    >
    > # scalars to read from the poem page
    > my ($vorname, $nachname, $title, $namekey, $titlesort_string, $nameletter, $titleletter, $page, $relative_path);
    >
    > my $pathname = $File::Find::name;
    > return 0 unless $pathname =~ /\.html$/;
    >
    > # definition of the file path relative to the $gedicht_path
    > $relative_path = $pathname;
    > $relative_path =~ s#$gedicht_path/##; # Perl complains about uninitialized value here
    > [filling the hashes and arrays declared above]
    > print %author_indexlines; # the hash is printed correctly here
    > }
    >
    > I expect the variable $gedicht_path to be accessible inside the read_poem
    > subroutine, since it is called from within read_index, it is not (I get an
    > 'uninitialized value' error).
    >
    > Second, I expect the hashes and arrays in declared in read_index to be filled
    > in read_poem. read_poem does not complain about undeclared variables, and I
    > can print the appropriate %author_indexlines etc. from within read_poem, but
    > not from read_index, as intended.
    >
    > Can anyone point me to the (probably obvious) problem with the locally scoped
    > variables?
    Hi Jan.

    Lexical ('my') variables are accessible only within the innermost block or
    file in which they are declared. As the body of a subroutine is a block
    things like your $gedicht_path are accessible only within &read_index.

    You need to declare the list of hashes and arrays /before/ and /outside/
    any subroutine that needs them. Like this:

    my (%author_indexlines, %author_headlines, ... );

    sub read_index {
    :
    }

    sub read_poem {
    :
    }

    Also, think carefully about whether you need so much common data. I would
    expect to see the sort of thing you're using contained within a single hash.
    Without knowing more about what you're doing I can't help any further.

    HTH,

    Rob


    Rob Dixon Guest

  5. #4

    Default Re: Subroutine interaction


    Jeff 'japhy' Pinyan wrote:
    >On Jan 28, Jan Eden said:
    >
    >>sub read_index {
    >>my $gedicht_path = shift; my (%author_indexlines, %author_headlines,
    >>%gedicht_lines, @author_letters, @gedicht_letters, @alphabet_index);
    >>find (\&read_poem, $gedicht_path); print %author_indexlines; #
    >>nothing is printed out here!
    >
    >The only way that hash can get populated outside of this function is
    >if it you pass it by reference to another function, and populate it
    >THERE. A my() variable is scoped lexically -- that is, physically.
    >It only exists here, inside this block of code, unless you send its
    >reference to another scope.
    >
    Yes, that's what I suspected. But I was tricked because ...
    >
    >You're not filling the same hashes and arrays; you're filling GLOBAL
    >hashes and arrays that happen to have the same name. If you were
    >using 'strict', you'd be told about that.
    >
    ... I *did* use strict in my main script. I did not within the module the subs reside in, though. Since the main script never saw the subroutines, its strict pragma did not notice my error. Doh!
    >>Can anyone point me to the (probably obvious) problem with the
    >>locally scoped variables?
    >
    >One way you can fix your situation is to use a closure -- an
    >anonymous code block created inside read_index():
    >
    Thank you! I will try this as soon as possible.

    Learning more and more,

    Jan
    --
    How many Microsoft engineers does it take to screw in a lightbulb? None. They just redefine "dark" as the new standard.
    Jan Eden Guest

  6. #5

    Default Re: Subroutine interaction

    Jan Eden wrote:
    >
    > Learning more and more
    Just as it should be :)

    Well done,

    Rob


    Rob Dixon Guest

  7. #6

    Default Re: Subroutine interaction

    Jan Eden wrote:
    > Hi all,
    >
    > the following structure does not work as intended:
    >
    > sub read_index {
    >
    > my $gedicht_path = shift;
    > my (%author_indexlines, %author_headlines, %gedicht_lines, @author_letters, @gedicht_letters, @alphabet_index);
    > find (\&read_poem, $gedicht_path);
    > print %author_indexlines; # nothing is printed out here!
    > return \(%author_indexlines, %author_headlines, %gedicht_lines, @author_letters, @gedicht_letters, @alphabet_index);
    > }
    >
    >
    > sub read_poem {
    >
    > # scalars to read from the poem page
    > my ($vorname, $nachname, $title, $namekey, $titlesort_string, $nameletter, $titleletter, $page, $relative_path);
    >
    > my $pathname = $File::Find::name;
    > return 0 unless $pathname =~ /\.html$/;
    >
    > # definition of the file path relative to the $gedicht_path
    > $relative_path = $pathname;
    > $relative_path =~ s#$gedicht_path/##; # Perl complains about uninitialized value here
    > [filling the hashes and arrays declared above]
    > print %author_indexlines; # the hash is printed correctly here
    > }
    >
    > I expect the variable $gedicht_path to be accessible inside the read_poem subroutine, since it is called from within read_index, it is not (I get an 'uninitialized value' error).
    Hi Jan,

    I'm afraid that this represents a basic misunderstanding of the purpose of subroutines. Subroutines and other containment tools protect and simplify problems by reducing the number of variables that may aggect the outcome. If they freely had access to each other's
    data, there could be no such assurance.

    Your code as posted show at least two of the three essential front doors for moving data in and out of subroutines. I havent looked closely enough yet to see if any of your arguments are references--that would be a third. Your function both take in arguments or
    agument lists through the @_ array. This is the appropriate way to get values into a function. The first one also returns a value, the classic means for getting information out of "returned from" an array If you need to affect some object or variable from within
    the function, you can pass a reference as an argument, and access the original through the reference.

    The power of subroutines comes from requiring you to explicitly decide, and thus be aware of, what information is being passed i and out, and why.


    > Second, I expect the hashes and arrays in declared in read_index to be filled in read_poem. read_poem does not complain about undeclared variables, and I can print the appropriate %author_indexlines etc. from within read_poem, but not from read_index, as intended.
    perldoc perlref
    perldoc perlreftut

    Would be good places to start.
    >
    >
    > Can anyone point me to the (probably obvious) problem with the locally scoped variables?
    It is not a problem. It is the most powerful tool that we, as programmers, have in making complex problems manageable. The problems arise when variables can be accessed, and corrupted by any code anywhere in a program.
    >
    >
    > Thanks,
    >
    > Jan
    Joseph

    R. Joseph Newton Guest

  8. #7

    Default Re: Subroutine interaction


    Rob Dixon wrote:
    >You need to declare the list of hashes and arrays /before/ and
    >/outside/ any subroutine that needs them. Like this:
    >
    >my (%author_indexlines, %author_headlines, ... );
    >
    >sub read_index {
    >:
    >}
    >
    >sub read_poem {
    >:
    >}
    >
    That's the way I had done it before. But I thought I'd fiddle around with returning references to the hashes and arrays from read_index to write_index(for learning purposes, not for efficieny).
    >Also, think carefully about whether you need so much common data. I
    >would expect to see the sort of thing you're using contained within
    >a single hash. Without knowing more about what you're doing I can't
    >help any further.
    I am not sure if I could store all this in one hash:

    %author_indexlines contains the author's name as a key and a line with author name and a link as its value

    % author_headlines contains the same keys and the author's name with an anchor for the respective link in %author_indexlines as its value

    %gedicht_lines contains the same keys and an array of poems for the respective author

    @author_letters and @gedicht_letters contain the letters for the navigationbar on top of the index page. I did (A..Z) in an earlier version of the script, but then I'd have a Q entry everywhere even if there was no poem title or author beginning with Q.

    @alphabet_index contains the content of all the arrays from the %gedicht_lines hash. In this case, I could really put it into the &write_index subroutine since it's used nowhere else.

    But I have no idea how to merge the other variables without loosing track of how to access which part. Please have a look at the index files produced at

    <http://jan-eden.de/public/literatur/maerchen/index.html>
    <http://jan-eden.de/public/literatur/maerchen/index2.html>

    if you are interested. I'd be happy to improve the script further. The closure did a great job, and I noticed that everything I needed to know is right here in both Programming Perl and the Perl Cookbook. Sometimes I find it,sometimes I need this list.

    Thanks again to Rob and Japhy,

    Jan
    >
    >HTH,
    >
    >Rob
    >
    >
    >
    --
    How many Microsoft engineers does it take to screw in a lightbulb? None. They just redefine "dark" as the new standard.
    Jan Eden Guest

  9. #8

    Default Re: Subroutine interaction

    On Jan 29, 2004, at 2:18 AM, Jan Eden wrote:
    > I am not sure if I could store all this in one hash:
    I bet you can. I'm an optimist. ;)

    I haven't been following this thread too closely, but let's just think
    about it in the general sense. What you basically has is a lot of
    information about specific authors, right? Can we shove an author in a
    hash? Sure:

    my %author = (
    Name => 'Whatever',
    Indexline => 'Whatever',
    Headline => 'Whatever',
    Gedicht_lines => [ 'Poem1', 'Poem2', 'PoemN' ]
    # etc... You get the idea.
    };

    Again, I haven't been following too closely, so forgive me if I don't
    have your format right.

    From there, it's a very small leap to a global hash:

    # assuming a previously defined %authors...

    $authors{$author{Name}} = \%author;

    Well, hopefully that'll give you some new ideas.

    James

    James Edward Gray II Guest

  10. #9

    Default Re: Subroutine interaction

    James Edward Gray II wrote:
    >On Jan 29, 2004, at 2:18 AM, Jan Eden wrote:
    >
    >> I am not sure if I could store all this in one hash:
    >
    >I bet you can. I'm an optimist. ;)
    >
    >I haven't been following this thread too closely, but let's just think
    >about it in the general sense. What you basically has is a lot of
    >information about specific authors, right? Can we shove an author in a
    >hash? Sure:
    >
    I do not have much information about a single author, it's basically just his first and last name (which is used in different orders - John Doe and Doe, John). I used two hashes for the authors because I am lazy.

    But I agree that I could use a single %authors hash instead of %authors_indexlines and %authors_headlines, where the keys consist of a combination of first and last name (converted to 7-bit without white space, as I have it now). The values would consist of references to two-element hashes (first and last name).

    Putting my @alphabet_index into the write subroutine, I'd be down to four variables (%authors, %gedichte, @gedicht_letters and @author_letters).

    The lasst two arrays are necessary and I doubt that my script would be moreefficient when merging the %authors and %gedichte. But I'll fiddle some more.
    >Well, hopefully that'll give you some new ideas.
    >
    Always grateful for your help,

    jan
    --
    Common sense is what tells you that the world is flat.
    Jan Eden Guest

Posting Permissions

  • You may not post new threads
  • You may post replies
  • You may not post attachments
  • You may not edit your posts

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139