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

Hello wise monks

it comes to me by circumstances, that I have to create a construct in a sub, which gives me back a hash (or object) of 2 subs which each return a value.

This is the goal. The circumstances are too complicated to describe them here.

I know that what I want seems to be done much easier by referencing the hash itself. But please, would you mind thinking of how it proably might be done to use the wanted sub just right as I would try to?

The goal of all is this one line of code

print $_->name, ': ', $_->val, "\n" for $objA->parameter( 'a.x' );

I have tried some notations which all do not work because I am writing it false.

Would be great if someone has an idea.

best regards, Thomas

\n

Replies are listed 'Best First'.
Re: how to let sub return hash of 2 sub?
by choroba (Cardinal) on May 27, 2015 at 14:53 UTC
    To create a code or subroutine reference, just use sub without a name:
    my $x = sub { print shift };

    To call such a subroutine, use the ->() dereference:

    $x->('Hello world!');

    It's a bit unclear what exactly you need. The following sub returns a hash of two subs:

    #! /usr/bin/perl use warnings; use strict; sub two_subs { return { first => sub { return 'First: ' . shift }, second => sub { return 'Second: ' . shift }, } } print two_subs()->{first}->('A'), ' ', two_subs()->{second}->('B'), "\ +n";
    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ

      ... and if OP wants to use already existing sub, use its reference ...

      ... sub two_subs { ... return { 'first' => \&ElseWhere::first , 'second' => \&ElseWhere::second } ; } # In ElseWhere ... sub first { 'first(): ' . shift } sub second { 'second(): ' . shift } ...
Re: how to let sub return hash of 2 sub?
by ikegami (Patriarch) on May 27, 2015 at 14:49 UTC

    That line will work if you return a list of objects.

    Say you want to create an object for each element of a hash, where name is the key of the element, and val is the value of the element.

    sub parameter { ... return map { NameValTuple->new( name => $_, val => $rv{$_} } keys(% +rv); }
    If you instead returned a list of hashes, it would look like
    print $_->{name}, ': ', $_->{val}, "\n" for $objA->parameter( 'a.x' );

    To return a list of hashes, you'd use

    sub parameter { ... return map { +{ name => $_, val => $rv{$_} } } keys(%rv); }

    (The "+" might not be needed. You can remove it if you don't get an error without it.)

      Thanks at first, dear Ikegami.

      I see that I have to describe a little bit more, what I'm trying to do. But bag you pardon, please don't laugh too much about me, it's my fault.

      I have written now a short file of code, which should describe nearly, what I want to do.

      ___ testpar.pl ___

      #!/usr/bin/perl use objectA; my %myhash = ( 'a.x' => 1, 'b.y' => 2, 'c.x' => 3, ); my $objA = objectA->new(); $objA->map_query( \%myhash ); print $_->name, ': ', $_->val, "\n" for $objA->parameter( 'a.x' ); print $_->name, ': ', $_->val, "\n" for $objA->parameter( 'b.y' ); return;

      ___ objectA.pm ___

      package objectA; sub new { my $class = shift; my $self = {}; bless $self, $class; return $self; } sub map_query { my $self = shift; my $hash = shift; map { $self->{ values }->{ $_ } = $$hash{ $_ }; } keys %{ $hash }; } sub parameter { my $self = shift; my $para = shift; my $param = $para =~ m/^([^\.]*\.)(.*?)$/i ? $2 : $para; # return { sub name{ $param; }, sub value{ $self->{ values }->{ $pa +ra } || ''; } }; my $name = \&parameter_name( $param ); my $val = \&parameter_value( $self->{ values }->{ $para } ); return { $name, $val }; } sub parameter_name { my $param = shift; return $param; } sub parameter_value { my $parval = shift; return $parval || ''; } 1;
        Since my solution works for a zero or more objects, it also works for a single object.
        sub parameter { ... return NameValTuple->new( name => $name, val => $val ); }

        It's still simpler to return a hash since you don't have to create a new class.

        sub parameter { ... return map { +{ name => $_, val => $rv{$_} } } keys(%rv); }

        But you'd have to change

        print $_->name, ': ', $_->val, "\n" for $objA->parameter( 'a.x' );
        to
        print $_->{name}, ': ', $_->{val}, "\n" for $objA->parameter( 'a.x' );

        In that case, return a list (or array reference) instead of a rather useless hash reference (as $name is converted into a string which then cannot be used as a sub reference) ...

        # In testpar.pl ... ... print join ': ' , map $_->() , $objA->parameter( 'a.x' ); ... # In A ... package A; ... sub parameter { ... return ( \&parameter_name( $param ) , \&parameter_value( $self->{ values }->{ $para } ) ) ; } ...
Re: how to let sub return hash of 2 sub?
by stevieb (Canon) on May 27, 2015 at 15:04 UTC

    Here's one example of a sub returning a hash with two subs, each of which returns something. Without having more context, it's difficult to see exactly what you want long-term, but hopefully this'll give you an idea to get you started.

    #!/usr/bin/perl use strict; use warnings; sub fetch_dispatch{ my $dispatch = { name => sub { return "name = this"; }, val => sub { return "val = that"; }, }; return $dispatch; } my $dt = fetch_dispatch(); print $dt->{name}(), ': ', $dt->{val}(), "\n";

    -stevieb

Re: how to let sub return hash of 2 sub?
by aaron_baugher (Curate) on May 27, 2015 at 19:17 UTC

    With a bit of extra syntax, you don't have to use objects. :-)

    #!/usr/bin/env perl use 5.010; use strict; use warnings; # a closure over a hash which takes a key as an argument and returns t +wo subs # which return the key and its value from the hash sub get_caller { my $h = shift; return { parameter => sub { my $k = shift; my $v = $h->{$k}; return { name => sub { return $k }, val => sub { return $v } }; } }; } my $c = get_caller( {'a.x' => 1, 'b.y' => 2, 'c.x' => 3} ); print $_->{name}(), ' : ', $_->{val}(), "\n" for $c->{parameter}('a.x' +);

    I think it would be possible to get rid of some of the curlies and parens and get it down to the simpler syntax of the original, but I'm still working through Higher Order Perl. This "functions that create functions to return functions" stuff gets a little mind-bending.

    Aaron B.
    Available for small or large Perl jobs and *nix system administration; see my home node.

Re: how to let sub return hash of 2 sub?
by Anonymous Monk on May 27, 2015 at 16:03 UTC

    As ikegami already said above, for the example line of code you give to work, parameter needs to return objects. If you want to work with objects, it would probably help if you looked at an OO framework such as Moose or e.g. Moo to make writing your classes easier. If you want to be enlightened on callbacks, closures, iterators, and using code refs in general, Higher-Order Perl is an excellent read. Either way however, what you are doing seems like it might be a little overcomplicated, but we can't help you with the underlying task if you don't share what that is :-)