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

I am writing a constructor in which I want to initialize certain aspects of the object algebraically like so:
my $g = Games::Grid->new (border => 4, width => sub { $W/4}, height => sub { $H }, fill => +'=');
What this means is that the width of the game grid is 1/4 the value of $W, which should be bound to the terminal width. The height is equal to the terminal width. And the grid should be filled with equal signs.

The following code in new:

{ my $W = $self->{terminal}->{_co}; my $H = $self->{terminal}->{_li}; $self->{terminal}->{WIDTH} = $config{width}->(); $self->{terminal}->{HEIGHT} = $config{height}->(); }
is not working because the closure was evaluated as soon as I defined the hash and $W was not in its scope.

How can I delay the attempt to bind $W until I evaluate the closure within the constructor?

Replies are listed 'Best First'.
Re: Algebraic Hash Configuration via Delayed Closure?
by btrott (Parson) on Mar 06, 2001 at 23:36 UTC
    It's not a closure that you're using, because a closure "binds" to lexical variables that exist *when the closure is created*. As you said, $W doesn't exist then; it exists *later*.

    So what you should probably do instead is just something like this:

    my $g = Games::Grid->new (..., width => sub { $_[0]/4}, height => sub { $_[0] }, ...);
    And then do:
    $self->{terminal}->{WIDTH} = $config{width}->($W); $self->{terminal}->{HEIGHT} = $config{height}->($H);
    In other words, pass $W and $H as args rather than rely on them being set when the sub is executed. If it were a closure, this would work; but it's not, bceause you're relying on the lexicals existing when you *execute* the subref, not when you *define* it.

    String eval might be another way of accomplishing your goal, but I think you'd be better off just passing args.

Re: Algebraic Hash Configuration via Delayed Closure?
by chipmunk (Parson) on Mar 06, 2001 at 23:37 UTC
    My first idea was to pass in the width/height:
    my $g = Games::Grid->new( border => 4, width => sub { $_[0]/4 }, height => sub { $_[0] }, fill => '=', ); { my $W = $self->{terminal}->{_co}; my $H = $self->{terminal}->{_li}; $self->terminal->{WIDTH} = $config{width}->($W); $self->terminal->{HEIGHT} = $config{height}->($H); }
    I suspect that may not be flexible enough to meet your needs, though...

    How about using package variables for $W and $H, instead of lexical variables?

    my $g = Games::Grid->new( border => 4, width => sub { $terminal::W/4 }, height => sub { $terminal::H }, fill => '=', ); { $terminal::W = $self->{terminal}->{_co}; $terminal::H = $self->{terminal}->{_li}; $self->terminal->{WIDTH} = $config{width}->(); $self->terminal->{HEIGHT} = $config{height}->(); }
      Actually, the way to delay the evaluation of the closure is to put it in single quotes and then eval() it when you have placed $W and $H into scope.

      It always helps to see other people's input on these problems as I turn it over in my head.

        Oh, please don't use eval() for that. That's not a smart reason to use eval(). There are better ways to approach the problem, as have been shown.

        japhy -- Perl and Regex Hacker
Re: Algebraic Hash Configuration via Delayed Closure?
by AgentM (Curate) on Mar 07, 2001 at 01:32 UTC
    Side Note: Just looking at your terminal heights and widths, this program screams "give me Curses." Curses gives you unimaginable control over every aspect of the terminal window, but even using it to return COL and ROW sizes is beneficial- have your tried sending your program SIGWINCH?
    AgentM Systems nor Nasca Enterprises nor Bone::Easy nor Macperl is responsible for the comments made by AgentM. Remember, you can build any logical system with NOR.
    A reply falls below the community's threshold of quality. You may see it by logging in.