2

For example, Pseudocode is given below. Based on the input given to x, if x is func1, then func1() must be called. If x is func2, then func2() must be called. Is there any way to do this. I do not want to use if or switch case statements. Is there any other approach to make function call based on user input? (something like treating a function as a variable?

sub func1()
{...}

sub func2() 
{...}

sub mainfunc()
{ 
   x = <STDIN>;
   x();
}
  • 2
    Using unsanitised user-input is a really bad idea. `chomp($x=); &$x` – jhnc Feb 26 '20 at 07:16
  • 1
    @PolarBear: There's a reason why the web site you link to is in Ukraine. It's because it's a copyright violation. Please don't link to copyright violating web sites. – Dave Cross Feb 27 '20 at 09:43
  • @Dave Cross - you probably aware that USA and Europe gave support to Ukraine to 'fight' Russia. I do not see that USA and Europe put any pressure on Ukraine for violation of copyrighted material. Probably you should report it to proper organisation who responsible to maintain legal order. I link to that website to bring an attention of copyright violation in Ukraine, otherwise it will not be noticed. – Polar Bear Feb 27 '20 at 11:19
  • @Dave Cross -- that Ukrainian website was online to my knowledge for at least past 10 years. You probably aware that Ukraine associated with EU and looking to be a member of NATO. At the same time Ukraine violates international law in many areas. – Polar Bear Feb 27 '20 at 11:36
  • @Dave Cross -- For your attention more websites [Perl bookshelf](https://www.cs.ait.ac.th/~on/O/oreilly/perl/index.htm), [Perl bookshelf](https://epdf.pub/the-perl-cd-bookshelf-version-30.html), [O'Reilly CD Bookshelf](https://www.cs.ait.ac.th/~on/O/oreilly/),[O'Reilly CD Bookshelf](http://books.creativecgi.com/),[O'Reilly Reference library](https://bioinfo2.ugr.es/OReillyReferenceLibrary/index.htm), [O'Reilly CD Bookshelf](http://doc.novsu.ac.ru/oreilly/) and there probably more of them. – Polar Bear Feb 27 '20 at 11:36

2 Answers2

8

Looks like you are seeking a dispatch table

use warnings;
use strict;
use feature 'say';

my %option = (
    o1 => sub { say "Code for func1, to run for key 'o1'"; },
    o2 => sub { say "Code that should run for input 'o2'"; },
    #...
);

my $input = <STDIN>;
chomp $input;

# Dereference the code-reference (run code) associated with $input value 
$option{$input}->();

Here sub { ... } defines an anonymous subroutine and returns a reference to code. Another way to get a code reference is by taking the reference of a named sub, with syntax \&sub-name. Being a reference it is a (single-valued) scalar type and so it can be used as a hash value.

So when a user supplies o1 what runs is the code in the reference (sub { ... }) which is the value for the key o1 in the hash, that is $option{o1}.

The syntax for running code reference is much like derefencing an array or a hash reference

$ar->[0]     # dereference array reference, for the first element
$hr->{key}   # dereference hash  reference, for value for key 'key'
$cr->(LIST)  # dereference code  reference, to run with arguments in LIST

The scalar variable $cr here would have the code reference, my $cr = sub { ... }.

zdim
  • 64,580
  • 5
  • 52
  • 81
  • `$option{$input};` won't execute the sub, just gives a warning about a useless use of hash element in void context. See my answer (I think we submitted at the exact same time. GMTA). – Shawn Feb 26 '20 at 07:39
  • @Shawn Thank you, corrected typo (checking for more...) – zdim Feb 26 '20 at 07:40
6

The safe way is to have a hash of names mapping to subroutines, so that arbitrary subs can't be called by a malicious user. Something like:

my %table = ("func1" => \&func1, "func2" => \&func2);
my $x = <STDIN>;
chomp $x;
if (exists $table{$x}) {
  $table{$x}->();
} else {
   # Handle error
}
Shawn
  • 47,241
  • 3
  • 26
  • 60