LinuxQuestions.org
Help answer threads with 0 replies.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 10-26-2009, 04:42 PM   #1
kdelover
Member
 
Registered: Aug 2009
Posts: 311

Rep: Reputation: 36
help with perl subroutine


hello guys,

I have a small perl script which has 3 subroutines a,b, and c in it . An Array (lets call it X) has the names of these subroutines stored in it ,using foreach i am reading out each and every name of the subroutine from the array. My Question is how do i make sure that a subroutine of that name is executed when ever the element from the array is read,For Ex: When subroutine b is read and printed out from the array i want the respective subroutine B to be executed .Hope you ppl got my Question

I need this logic for someother script,i am just pasting a proto-type here.

Code:
#!/usr/bin/perl
use strict;
use warnings;
my @x=("a","b","c");
foreach my $val(@x)
{
print "This is $val\n";
}

sub a 
{
print "This is AAA";
}

sub b
{
print "This is BBB";
}

sub c
{
print "This is CCC";
}
OUTPUT:
In the foreach block i want to write a do statement and execute the corresponding subroutine returned.

This is a (At this point must call subroutine a)
This is b (At this point must call subroutine b)
This is c ( (At this point must call subroutine c)
 
Old 10-26-2009, 05:24 PM   #2
Telemachos
Member
 
Registered: May 2007
Distribution: Debian
Posts: 754

Rep: Reputation: 60
It sounds to me like you want to use a variable as a variable name (a.k.a. a symbolic reference). This is a very bad idea; see this post (and its follow-ups) for why.

However, it's easy to store subroutines as items in an array by using references in Perl:

Code:
#!/usr/bin/perl
use strict;
use warnings;

my @funcs = (\&a, \&b, \&c);

for my $func (@funcs) {
    $func->();
}

sub a {
    print "This is AAA\n";
}

sub b {
    print "This is BBB\n";
}

sub c {
    print "This is CCC\n";
}
Note that for subroutines this simple, you can actually simply leave them anonymous and insert them directly into the array (without naming them separately anywhere):

Code:
#!/usr/bin/perl
use strict;
use warnings;

my @funcs = (
    sub { print "This is AAA.\n"; },
    sub { print "This is BBB.\n"; },
    sub { print "This is CCC.\n"; },
);

for my $func (@funcs) {
    $func->();
}
For more on using subroutines and references together this way, see this link.

Last edited by Telemachos; 10-26-2009 at 05:35 PM.
 
Old 10-26-2009, 05:38 PM   #3
kdelover
Member
 
Registered: Aug 2009
Posts: 311

Original Poster
Rep: Reputation: 36
Thanks a lot telemachos,its working. I just have few questions,i'm new to perl. Why doesn't it work when i remove the double quotes inside the array. I meant like my @x=("\&a","\&b","\&c"); since its an array.

Secondly,what does $func->(); mean. May be if you can tell me what i should google for to know more about it.Thanks Again. Cheers :-)


EDIT:

I was trying this,instead of using print statements in subroutines,i was trying to execute a block of code,for example starting some application. like firefox and thunderbird . The problem i ran into is when sub a is executed( it loads an application say firefox),for sub b to be executed(which will load thunderbird) i have to first close firefox,how can i make sure that thunderbird is open along with firefox SOrry if am asking for too much. Thanks Again.

foreach (@x)
{
#print "This is ","$_\n";
$_->();

}

sub a
{
`/usr/bin/kontact 2> /dev/null `;
}

sub b
{
`/usr/bin/gwenview 2> /dev/null `;
}

Last edited by kdelover; 10-26-2009 at 06:24 PM.
 
Old 10-26-2009, 06:29 PM   #4
Telemachos
Member
 
Registered: May 2007
Distribution: Debian
Posts: 754

Rep: Reputation: 60
Quote:
Originally Posted by kdelover View Post
Thanks a lot telemachos,its working. I just have few questions,i'm new to perl. Why doesn't it work when i remove the double quotes inside the array. I meant like my @x=("\&a","\&b","\&c"); since its an array.
You shouldn't need (and don't want) quotes there. The backslash does a number of things in Perl, but here it's the take a reference to operator. So you should be able to do this:

Code:
my $code_ref = \&sub_name;
That assigns a reference to the subroutine named 'sub_name' to the variable $code_ref. What we're doing in the case of the array is assigning a reference to a subroutine as each item in the three-item array. No quotes are needed. See here and here for more on this.

Quote:
Originally Posted by kdelover
Secondly,what does $func->(); mean.
In the for loop there, $func is aliased to each item in the array in turn. Since each of the things in the array is a reference to a subroutine, you can't simply put it in quotes and print it. You have to dereference it, in order to actually call the subroutine. To do this we use the arrow syntax:

Code:
$code_ref->(@args)
Inside the parens, you can (optionally) place arguments. Since our subroutines don't take arguments, we just say $func->()

Using subroutines in references is probably an intermediate-level Perl thing. You might want to start by reading up on references period and then getting into this. I would recommend these four links, in this order:

I just saw your follow-up, and my instinct is to say that Perl is great for a lot of things, but I don't think I would use it as a program launcher.

Last edited by Telemachos; 10-26-2009 at 06:31 PM.
 
Old 10-27-2009, 02:44 PM   #5
kdelover
Member
 
Registered: Aug 2009
Posts: 311

Original Poster
Rep: Reputation: 36
awesomeee man!! Thanks a lot for the explanation and the links,the link you gave perltraining.com.au its awesome,i have bookmarked it Thanks again Cheers!!!
 
Old 10-27-2009, 03:56 PM   #6
theNbomr
LQ 5k Club
 
Registered: Aug 2005
Distribution: OpenSuse, Fedora, Redhat, Debian
Posts: 5,399
Blog Entries: 2

Rep: Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908
Quote:
Originally Posted by Telemachos View Post
I just saw your follow-up, and my instinct is to say that Perl is great for a lot of things, but I don't think I would use it as a program launcher.
What makes you say this? Is there something different about the way Perl does a fork() + exec() than does any other language (including shells), at least in a Linux/UNIX environment? Not disputing your assertion; just want to understand your reasoning.
--- rod.
 
Old 10-27-2009, 04:25 PM   #7
Telemachos
Member
 
Registered: May 2007
Distribution: Debian
Posts: 754

Rep: Reputation: 60
Quote:
Originally Posted by theNbomr View Post
What makes you say this? Is there something different about the way Perl does a fork() + exec() than does any other language (including shells), at least in a Linux/UNIX environment? Not disputing your assertion; just want to understand your reasoning.
--- rod.
Hmm, well to be honest, I wasn't saying that it would be impossible or even bad to do it in Perl. It was a quick response - just my first thought - and my reason was that if I'm going to be working with a lot of system calls and/or signals, I might probably choose to go lower-level than Perl. That said, I would probably really choose to let someone else code it and use one of the many launchers out there.

So maybe this was just a failure of imagination on my part.
 
Old 10-27-2009, 05:28 PM   #8
kdelover
Member
 
Registered: Aug 2009
Posts: 311

Original Poster
Rep: Reputation: 36
My main intention was to write a application-launcher script,that would pop-up after the user logs in and it would show a GUI window using zenity ( which a checklist) and the user would select what all applications he wants to start. I am sorry i dont have the code at the moment to paste here

One problem i ran into is,lets say if user selects three applications a,b,and c, from the checkbox,am storing the name of those apps in a variable called $Select="a:b:c"( where : is a separator bt.the names) and then using a split function to remove the ":" and store the names of the apps in an array called @apps (@apps="a,b,c").Using foreach i want read out the name of each application name( This infact would be the name of a subroutine being called) and i want that application to be launched.But i dont understand how do i do this since the array must have a reference to the subroutine. The Q is simple,the user would select the application to be started,but selecting and the corresponding subroutine should be called. Hope i made my point clear :-D

my $choice="a:b:c";
my $arr=split(":",$choice);
foreach (@arr)
{
$_->();
}

sub a
{
<start Application a>
}

sub b
{
<start Application b>
}
.
.
.
So on...

Thanks again and sorry for the lengthy Question.
 
Old 10-27-2009, 06:54 PM   #9
Telemachos
Member
 
Registered: May 2007
Distribution: Debian
Posts: 754

Rep: Reputation: 60
There are lots of ways to handle this, but here's one: add another layer of indirection. That is, don't think of the user as directly choosing applications to launch. Instead, set up a situation where the user selects from a set of options that you give him or her:

Code:
    What apps do you want to launch?
        (1) Firefox
        (2) Thunderbird
        (3) Mutt
        (4) Gvim
The user enters (say) the numbers, and the numbers go into an array, say called @choices. You, however, have already (cleverly) created a hash that uses the numbers 1-4 as keys and the corresponding function calls as code references. You loop over the items in @choices, using those to get at the function calls in the hash. Of course, since you can't trust the user to give you reasonable input, you also have to filter what you get.

Here's a mockup of some of this to give you an idea of what I mean.

Code:
#!/usr/bin/env perl
use strict;
use warnings;

my %choice_calls = (
    1 => sub { print "start Firefox\n" },
    2 => sub { print "start Thunderbird\n" },
    3 => sub { print "start Mutt\n" },
    4 => sub { print "start Gvim\n" }
);

print "Give me a colon-separated list of choices:\n";
print "\t(1) Firefox\n";
print "\t(2) Thunderbird\n";
print "\t(3) Mutt\n";
print "\t(4) Gvim\n";

chomp(my $choice_string = <STDIN>);
my @choices = split /:/, $choice_string;

my @good_choices = grep {   exists $choice_calls{$_} } @choices;
my @bad_choices  = grep {  !exists $choice_calls{$_} } @choices;

for my $choice (@good_choices) {
    $choice_calls{$choice}->();
}

if (@bad_choices) {
    print "\nBy the way, you also gave these bad choices.\n";
    print "Please type more carefully in the future: @bad_choices.\n"
}
If you run this and enter something like 1:4:9, you can get an idea of the way it might work. (Note that I have no idea how Zenity works. It may add some helpful menu/choice creating stuff that I'm not thinking of.)

Also, by the way, I officially apologize for the variable names. I'm too tired to be more creative right now, so they're all variations of 'choice'. Think of it as a negative example: don't clutter up your namespace with nothing but variations of one variable name!

Last edited by Telemachos; 10-27-2009 at 06:56 PM.
 
Old 10-28-2009, 03:24 AM   #10
kdelover
Member
 
Registered: Aug 2009
Posts: 311

Original Poster
Rep: Reputation: 36
Thanks telemachos. What zenity does is give the user a small gui windows,where he can select what app he would like to launch,something like this
http://linux.byexamples.com/archives...og-examples-2/

It comes very handy while taking user-inputs.I'll Try out the script you mentioned,i wonder if i can do it along with zenity :-).Is there any way i can add a /& explicitly into the array to create a reference.

By the way Thanks again for that perl site,i have downloaded couple of Pdfs from there on perl for system admins,its really great,concise and at the same time very informative :-)
 
Old 10-28-2009, 06:56 AM   #11
Telemachos
Member
 
Registered: May 2007
Distribution: Debian
Posts: 754

Rep: Reputation: 60
Quote:
Originally Posted by kdelover View Post
Is there any way i can add a /& explicitly into the array to create a reference.
You want a backslash there not a frontslash (\&), but yes, you can do it that way:

Code:
<snip>
my %choice_calls = (
    1 => \&firefox,
    2 => \&thunderbird,
    3 => \&mutt,
    4 => \&gvim,
);
<snip>
sub firefox { print "start Firefox...\n"; }
sub thunderbird { print "start Thunderbird...\n"; }
sub mutt { print "start Mutt...\n"; }
sub gvim { print "start Gvim...\n"; }
Just put the explicit items like \&firefox where you want them and declare the subroutines as normal elsewhere.
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Perl Subroutine help abs_77 Programming 4 05-06-2009 02:07 AM
Call Perl subroutine from console estratos Programming 2 04-22-2008 10:46 AM
perl subroutine call shifty_eyes Programming 2 10-01-2005 08:43 PM
Need Perl/CGI help! Undefined Subroutine CragStar Programming 4 03-05-2002 07:28 AM
Perl Subroutine problem sykkn Programming 0 02-20-2002 09:22 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 11:15 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration