develooper Front page | perl.fwp | Postings from August 2002

Multilingual function lookup table

Thread Next
From:
Andrew.Savige
Date:
August 23, 2002 02:08
Subject:
Multilingual function lookup table
Message ID:
694BB7191495D51183A9005004C0B05452DF37@ir-exchange-srv.ir.com.au
I stumbled across this little quiz I wrote years ago
and thought it would be fun to post it to the list to
see more variety of solutions to this simple problem --
in Perl or any other language (I know we have some
Befunge and BrainF**k programmers on this list ;).

I'm sure there are multiple different Perl solutions
(I embed only one obvious approach below).

Jerome or BooK might even submit a single program
that solves the problem in multiple languages. ;)

/-\ndrew


The Problem
-----------
Given input of a string and a number, you want to call
an appropriate "handler" function (based on the value
of the string) and pass it the number as an argument.
Each handler function returns a non-negative integer.

The function that you must write has signature:
   int invoker(const char* cmd, int z)     [C]
   int invoker(String cmd, int z)          [Java]
   int invoker(const string& cmd, int z)   [C++]
invoker is expected to return whatever the appropriate
handler function returns or -1 if no handler function
can be found.

Given 2 valid commands, "edit" and "chmod", a simple C solution is:

static int EditHandler(int z)
{
   printf("in EditHandler, z=%d\n", z);
   return 1;
}

static int ChmodHandler(int z)
{
   printf("in ChmodHandler, z=%d\n", z);
   return 2;
}

int invoker(const char* cmd, int z)
{
   int rc;
   if (strcmp(cmd, "edit") == 0)
      rc = EditHandler(z);
   else if (strcmp(cmd, "chmod") == 0)
      rc = ChmodHandler(z);
   else
      rc = -1;
   return rc;
}

Suppose you have 1000 different commands.
This solution does not scale very well.
Can you suggest an improvement?

How would you write invoker() in Perl?
How would you write invoker() in C?
How would you write invoker() in Java?
How would you write invoker() in C++?
How would you write invoker() in /bin/sh?

Test Data
---------
Running this:
   int rc;
   rc = invoker("edit", 42);
   printf("rc=%d\n", rc);
   rc = invoker("chmod", 99);
   printf("rc=%d\n", rc);
   rc = invoker("fred", 7);
   printf("rc=%d\n", rc);
should produce:
   in EditHandler, z=42
   rc=1
   in ChmodHandler, z=99
   rc=2
   rc=-1


Perl solution
-------------
use strict;

my %op_table = ( edit  => sub {   my $z = shift;
                                  print "in EditHandler, z=$z\n";
                                  return 1;
                              },
                 chmod => sub {   my $z = shift;
                                  print "in ChmodHandler, z=$z\n";
                                  return 2;
                              }
               );

sub invoker {
   my ($name, $z) = @_;
   exists($op_table{$name}) or return -1;
   $op_table{$name}->($z);
}

my $rc = invoker("edit", 42);
print "rc=$rc\n";
$rc = invoker("chmod", 99);
print "rc=$rc\n";
$rc = invoker("fred", 7);
print "rc=$rc\n";


Java solution
-------------
// Inner class solution.

import java.util.*;

class itest
{
   private int cnt = 0;
   private Hashtable op_table;

   private interface Handler
   {
      public int invoke(int z);
   }

   private class EditHandler implements Handler
   {
      public int invoke(int z)
      {
         System.out.println("EditHandler, cnt="+cnt+" z="+z);
         ++cnt;
         return 0;
      }
   }

   private class ChmodHandler implements Handler
   {
      public int invoke(int z)
      {
         System.out.println("ChmodHandler, cnt="+cnt+" z="+z);
         ++cnt;
         return 0;
      }
   }

   private int invoker(String cmd, int z)
   {
      Handler op = (Handler)op_table.get(cmd);
      if (op == null) {
         System.out.println("oops, command '"+cmd+"' not found");
         return (-1);
      }
      return op.invoke(z);
   }

   public void init()
   {
      op_table = new Hashtable();
      op_table.put("edit", new EditHandler());
      op_table.put("chmod", new ChmodHandler());
      int rc = invoker("edit", 42);
      rc = invoker("chmod", 1);
      rc = invoker("fred", 2);
      rc = invoker("chmod", 3);
   }

   public static void main(String args[])
   {
      itest ii = new itest();
      ii.init();
   }
}


C solution
----------
/* Simple C solution (would be faster with binary search/hash table) */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define N_ITEMS(a)  (sizeof(a)/sizeof(a[0]))

typedef int (*pfnHandler)(int);
typedef struct handler_type handler_type;
struct handler_type {
   const char* name;
   pfnHandler invoke;
};

static int EditHandler(int z)
{
   printf("in EditHandler, z=%d\n", z);
   return 1;
}

static int ChmodHandler(int z)
{
   printf("in ChmodHandler, z=%d\n", z);
   return 2;
}

static const handler_type op_table[] = {
   "edit",  EditHandler,
   "chmod", ChmodHandler
};

static int lookup_cmd(const char* name)
{
   int i;
   for (i = 0; i < N_ITEMS(op_table); ++i)
      if (strcmp(op_table[i].name, name) == 0)
         return i;
   return (-1);
}

static int invoker(const char* name, int z)
{
   int idx;
   if ((idx = lookup_cmd(name)) < 0) return (-1);
   return op_table[idx].invoke(z);
}

int main(int argc, char* argv[])
{
   int rc;
   rc = invoker("edit", 42);
   printf("rc=%d\n", rc);
   rc = invoker("chmod", 99);
   printf("rc=%d\n", rc);
   rc = invoker("fred", 7);
   printf("rc=%d\n", rc);
   return 0;
}


C++ solution
------------
// C++ function object solution.

#include <iostream>
#include <string>
#include <map>
using namespace std;

class FunctionObject {
   private:
      string name;
      int rc;
   public:
      FunctionObject() : name(""), rc(0) {}
      FunctionObject(const string& n, int i) : name(n), rc(i) {}
      int operator() (int z)
      {
         cout << "in " << name << "Handler" << ", z=" << z << endl;
         return rc;
      }
};

typedef map<string, FunctionObject> handler_type;
static handler_type op_table;

static int invoker(const string& name, int z)
{
   handler_type::iterator pos = op_table.find(name);
   if (pos == op_table.end()) return (-1);
   return pos->second(z);
}

int main(int argc, char* argv[])
{
   op_table["edit"]   = FunctionObject("Edit", 1);
   op_table["chmod"]  = FunctionObject("Chmod", 2);
   int rc = invoker("edit", 42);
   cout << "rc=" << rc << endl;
   rc = invoker("chmod", 99);
   cout << "rc=" << rc << endl;
   rc = invoker("fred", 7);
   cout << "rc=" << rc << endl;
   return 0;
}


Bourne shell
------------
#!/bin/sh

cnt=0

editHandler()
{
   echo "EditHandler, cnt=$cnt z=$1"
   cnt=`expr $cnt + 1`
   return 0
}

chmodHandler()
{
   echo "ChmodHandler, cnt=$cnt z=$1"
   cnt=`expr $cnt + 1`
   return 0
}

invoker()
{
   handlername="${1}Handler"
   $handlername "$2"
}

invoker "edit" 42
rc=$?
invoker "chmod" 1
rc=$?
invoker "fred" 2
rc=$?
invoker "chmod" 3
rc=$?


Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About