Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Rosetta Dispatch Table

by eyepopslikeamosquito (Chancellor)
on Nov 21, 2017 at 21:14 UTC ( #1203952=perlmeditation: print w/replies, xml ) Need Help??

Ha ha, nysus just reminded me of an old interview question I used to ask. Implement a simple dispatch table.

Let's start with a specification:

  • The key of the dispatch table is a string \w+
  • The name of the callback function is the key name with _callback appended
  • Each callback function takes a single string parameter and returns a positive number

You must write the invoker function, which takes two arguments (the name and the string argument to be passed to the callback):

  • If the name is invalid (e.g. "fred" below), invoker must return a negative number
  • Otherwise, invoker must pass its second argument to the callback function and return what the callback function returns

To clarify, here is a sample implementation.

use strict; use warnings; # Callback functions --------------------------------------- sub first_callback { my $z = shift; print "in first_callback, z=$z\n"; return 1; } sub last_callback { my $z = shift; print "in last_callback, z=$z\n"; return 2; } # Implementation of dispatch table ------------------------- # (You need to write this code) my %op_table = ( first => \&first_callback, last => \&last_callback, ); sub invoker { my ($name, $z) = @_; exists($op_table{$name}) or return -1; $op_table{$name}->($z); } # Main program for testing --------------------------------- for my $name ( "first", "last", "fred" ) { my $rc = invoker( $name, $name . '-arg' ); print "$name: rc=$rc\n"; }

Running the above test program produces:

in first_callback, z=first-arg first: rc=1 in last_callback, z=last-arg last: rc=2 fred: rc=-1

Points to consider:

  • Is a hash the recommended way to implement a dispatch table in Perl?
  • How many other ways can you think of to implement it in Perl? (working demonstration code would be good)
For more fun, feel free to implement the above specification in another language of your choice.

Replies are listed 'Best First'.
Re: Rosetta Dispatch Table
by tybalt89 (Priest) on Nov 21, 2017 at 23:07 UTC

    Why create your own dispatch table when perl has one built in?

    Or - violating specifications for fun? and profit?

    #!/usr/bin/perl # http://perlmonks.org/?node_id=1203952 use strict; use warnings; # Callback functions --------------------------------------- sub first::callback { my $z = pop; print "in first_callback, z=$z\n"; return 1; } sub last::callback { my $z = pop; print "in last_callback, z=$z\n"; return 2; } sub UNIVERSAL::callback { -1 } # Implementation of dispatch table ------------------------- # (You need to write this code) #my %op_table = ( first => \&first_callback, # last => \&last_callback, # ); sub invoker { my ($name, $z) = @_; $name->callback($z); } # Main program for testing --------------------------------- for my $name ( "first", "last", "fred" ) { my $rc = invoker( $name, $name . '-arg' ); print "$name: rc=$rc\n"; }

    Have I once again managed to fail an interview?

      That's the sort of ingenuity and inventiveness I was hoping to provoke. Thanks.

      Have I once again managed to fail an interview?
      I think we both know I would be delighted to offer you a job without requiring an interview ... though I understand you are no longer looking. :)

      some annotations:
      • I would be cautious about name spaces like first:: and last:: , if you really need extra packages I'd use an extra parent one like callback::first etc
      • I'd use ->can beforehand to check if the callback is really available, to avoid an unexpected die without good notice
      • the STASH - your built in dispatch table - is a hash , just global (which leads to the first remark :)

      Cheers Rolf
      (addicted to the Perl Programming Language and ☆☆☆☆ :)
      Wikisyntax for the Monastery

        Is there a case that ->can is needed that is not caught by UNIVERSAL::callback ?

Re: Rosetta Dispatch Table
by Limbic~Region (Chancellor) on Nov 22, 2017 at 13:57 UTC
    eyepopslikeamosquito,
    Over a decade ago, I wrote When should I use a dispatch table?. I no longer code and have all but left the Perl community to focus on other things. Just wondering if the performance overhead of dispatch tables still as much as they were back when I first investigated?

    Cheers - L~R

Re: Rosetta Dispatch Table
by Eily (Prior) on Nov 22, 2017 at 17:45 UTC

    Did you get much variation in the working results? It seems kind of straightforward to me, but maybe it's because I'm too used to that idiom.

    Is a hash the recommended way to implement a dispatch table in Perl?
    There are at least two ways to do this with an array (either the key is an enum, or you iterate over the array for each call), but I wanted to try with closures (with an OO interface, to hide the complexity of creating the closure chain when adding the callbacks):
    use strict; use warnings; # Callback functions --------------------------------------- sub first_callback { my $z = shift; print "in first_callback, z=$z\n"; return 1; } sub last_callback { my $z = shift; print "in last_callback, z=$z\n"; return 2; } # Implementation of dispatch table ------------------------- # (You need to write this code) package Invoker { sub new { my ($class, $default) = @_; bless \(sub { shift; $default->(@_) }), $class; } sub add { my ($self, $key, $callback) = @_; my $alt = $$self; $$self = sub { my $name = shift; $name eq $key ? $callback->(@_) : + $alt->($name => @_) }; $self; } sub dispatch { my $self = shift; &{ $$self }(@_); } } sub default_callback { return -1; } my $invoker = Invoker->new(\&default_callback) ->add(first => \&first_callback) ->add(last => \&last_callback); sub invoker {$invoker->dispatch(@_)}; # Main program for testing --------------------------------- for my $name ( "first", "last", "fred" ) { my $rc = invoker( $name, $name . '-arg' ); print "$name: rc=$rc\n"; }
    This creates a chain of closures that each contain a key, the corresponding callback and a ref to the next closure in the chain. If the name matches the key, the callback is called, otherwise the parameters are passed to the next closure in the chain.

    At first this was just supposed to be a silly implementation, though it did make me think about your first question: a hash is the best solution when using an exact match to a key as the dispatch condition, but the (iterative) array-based or closure-based implementations can accept any condition (though the only advantage of the closure-based solution over array-based is that it's more fun).

    Edit: the init method has been renamed new, because I don't know why I didn't just do that in the first place. And made the explanation more complete

      Bonus (got the idea by reading tybalt89's version)

      sub Invoker::first { shift; &first_callback } sub Invoker::last { shift; &last_callback } sub Invoker::AUTOLOAD { -1 } sub invoker { my $name = shift; Invoker->$name(@_); }
      It looks and kind of quacks like a symbolic ref on the method name, except it works under strict and warnings.

      Edit: Actually if you ignore the first parameter of the callbacks, you can just use the symbols table as the containing hash and use the method above :)

      use strict; use warnings; # Callback functions --------------------------------------- sub first_callback { my (undef, $z) = @_; print "in first_callback, z=$z\n"; return 1; } sub last_callback { my (undef, $z) = @_; print "in last_callback, z=$z\n"; return 2; } sub default_callback { -1 } # Dispatch ------------------------------------------------- %Dispatch:: = ( first => *first_callback, start => *first_callback, last => *last_callback, AUTOLOAD => *default_callback); sub invoker { my $name = shift; Dispatch->$name(@_); } # Main program for testing --------------------------------- for my $name ( qw< first start last fred > ) { my $rc = invoker( $name, $name . '-arg' ); print "$name: rc=$rc\n"; }

Re: Rosetta Dispatch Table
by eyepopslikeamosquito (Chancellor) on Nov 22, 2017 at 22:59 UTC

    Thanks to everyone for their interesting and instructive responses.

    For completeness, some candidates tried using symrefs, via something like this:

    use strict; use warnings; sub Invoker::first_callback { my $z = shift; print "in first_callback, z=$z\n"; return 1; } sub Invoker::last_callback { my $z = shift; print "in last_callback, z=$z\n"; return 2; } sub invoker { my ($name, $z) = @_; my $handler = 'Invoker::' . $name . "_callback"; no strict 'refs'; my $rc = eval { &$handler($z) }; return defined($rc) ? $rc : -1; } # Main program for testing --------------------------------- for my $name ( "first", "last", "fred" ) { my $rc = invoker( $name, $name . '-arg' ); print "$name: rc=$rc\n"; }
    or this:
    use strict; use warnings; package Invoker; sub first_callback { my $z = shift; print "in first_callback, z=$z\n"; return 1; } sub last_callback { my $z = shift; print "in last_callback, z=$z\n"; return 2; } sub invoker { my ($name, $z) = @_; my $handler = $name . '_callback'; no strict 'refs'; exists(${__PACKAGE__.'::'}{$handler}) or return -1; &$handler($z); } package main; # Main program for testing --------------------------------- for my $name ( "first", "last", "fred" ) { my $rc = Invoker::invoker( $name, $name . '-arg' ); print "$name: rc=$rc\n"; }

    For cheap thrills, just now I tried implementing via Class::MOP. I'm a Class::MOP ignoramus, so please feel free to suggest better ways to do it.

    use strict; use warnings; use Class::MOP; # Callback functions --------------------------------------- sub first_callback { my $z = $_[1]; print "in first_callback, z=$z\n"; return 1; } sub last_callback { my $z = $_[1]; print "in last_callback, z=$z\n"; return 2; } # Implementation of dispatch table ------------------------- my $invoker_pkg = Class::MOP::Package->create('Invoker'); $invoker_pkg->add_package_symbol('&first', \&first_callback); $invoker_pkg->add_package_symbol('&last', \&last_callback); # use Data::Dumper; # my $r = $invoker_pkg->namespace(); warn Dumper($r); sub invoker { my $name = shift; $invoker_pkg->has_package_symbol('&' . $name) or return -1; Invoker->$name(@_); } # Main program for testing --------------------------------- for my $name ( "first", "last", "fred" ) { my $rc = invoker( $name, $name . '-arg' ); print "$name: rc=$rc\n"; }
Re: Rosetta Dispatch Table
by duelafn (Vicar) on Nov 22, 2017 at 01:27 UTC

    I do like the hash for clean and obvious enumeration, but I hate the explicit hash and typing key names more than once, so I tend to hide both declaration and invocation in subs:

    #!/usr/bin/perl -s use strict; use warnings; use 5.014; our $h; sub callback; our %CALLBACKS; # Callback functions --------------------------------------- callback first => sub { my $z = shift; print "in first_callback, z=$z\n"; return 1; }; callback last => sub { my $z = shift; print "in last_callback, z=$z\n"; return 2; }; # Implementation of dispatch table ------------------------- # (You need to write this code) sub callback { my ($name, $cb) = @_; $CALLBACKS{$name} = $cb; } sub invoker { my ($name, $z) = @_; return -1 unless exists($CALLBACKS{$name}); $CALLBACKS{$name}->($z); } sub help { say "Available callbacks: @{[ sort keys %CALLBACKS ]}"; } # Main program for testing --------------------------------- exit help() if $h; for my $name ( "first", "last", "fred" ) { my $rc = invoker( $name, $name . '-arg' ); print "$name: rc=$rc\n"; }

    Results:

    $ perl /tmp/1203952.pl -h Available callbacks: first last $ perl /tmp/1203952.pl in first_callback, z=first-arg first: rc=1 in last_callback, z=last-arg last: rc=2 fred: rc=-1

    Good Day,
        Dean

Re: Rosetta Dispatch Table
by sundialsvc4 (Abbot) on Nov 24, 2017 at 00:14 UTC

    Since we seem to have strayed onto the topic of “interviews from the interviewer’s point of view ... which is quite refreshing, actually ... I think that we should begin by acknowledging that our profession is one of intense pressure.   But, the root-cause of that pressure is, I think, novel(!).   Please let me quickly explain.

    I once read an e-book (I never saw it in print) called Managing the Mechanism, in which the author pointed out that “computer software is a machine automaton.”   That is the true nature of this thing that we are building, and by its very nature it is intrinsic(!) to the most-fundamental business processes of whatever business may right-now employ us.   We are creating “a robot that must somehow win the football game,” or crash-and-burn trying, while we are confined to the locker room, entirely unable to intervene.

    Furthermore, I consider it entirely-safe to assert that “the actual task has never been ‘programming-language specific.’”   Not Perl, not PHP, not JOVIAL, not anything.   All of us use whatever is at hand to do what needs be done, and we do it every day using an endless series of technical compromises(!), not absolutes.   All of this we know.   I am preaching to the choir.

    And so:   ENTER, STAGE LEFT: The Candidate

    Well, IMHO, first of all, while it used to be that we were dealing with esoterica that no one else knew, today we are probably dealing with people who first encountered (say ...) Perl in fifth grade, if not kindergarten.   Also, it is impossible, I aver, to expect to find any candidate who off-the-curb already possesses exactly the technical milieu that “our team” possesses, “right here.”   If we are waiting to find any such person, IMHO we are “Waiting for Godot.”

    Therefore, when interviewing, I try to look for three things in particular:

    1. Experience:   I’m sorry, but I cannot be running a school.
    2. Resourcefulness:   If you have never in your life encountered “the Perl language” before, I am not going to ipso facto hang-up the phone on you.   What else have you done?   How fast do you think that you can “come up to speed,” and, most(!) importantly, “are you, in fact, willing to Sign Up™ to do it long-before your 60-day probation period runs out?
    3. Political Street-Smarts Willingness(!):   What used to be called, “stick-to-it-iveness,” or simply, “professionalism.”   Can you, and will(!) you, adapt yourself to the conditions as they come, or will you “kick against the traces,” expect the business to conform to you, and disappear without-a-word when it doesn’t?   Will you continue to be “part of the team,” even when we seem to be eight-or-more points down going into the final inning?   And, will you continue thereafter to be part of the team, even if we lose?

    I freely admit that some of these perspectives are shaped by my career-history of striving to turn-around projects that have sunk into “failure mode.”   Which, I readily admit, is (thank God ...) an edge-case.   I have encountered a lot of demoralized people along the way.   When I interview, I don’t want to create another one.

    Even though I know that the candidate will encounter intense pressure on the my job, I also take for granted that “my job” is not this candidate’s first rodeo, nor will it be the last.   I am frankly most-interested in just how this person will conform him/herself to the team in which s/he will very-necessarily be situated.   Will (or will not) such a person “blend in?”   Six months hence, will this person ... first of all, still be here ... be an asset to the team, or a mistake (that I(!) just made ... today) ... ???

      • Experience: I’m sorry, but I cannot be running a school.
        • Quote—Although I take for granted that the candidate is conversant in the Perl ... language(s), I also know that I could teach it to the right candidate—unquote.
      • Resourcefulness...“are you, in fact, willing to Sign Up™ to do it long-before your 60-day probation period runs out?
        • Do you really-o, truly-o promise you won't let this office down before we have to eat something like $15K in hiring and onboarding expenses?
      • Political Street-Smarts Willingness(!): What used to be called, “stick-to-it-iveness,” .. even if we lose?
        • Ho-lee shit! Such naked tribalism. "Even if we lose?" Talent jumps ship precisely because it can. Why wouldn't it? And what does it say that you actively recruit for willing losers?

        In general, “Mom,” I think that we are today still behaving as though the technical skills that we are looking for – in filling any(!) such position regardless(!!) of language or tool – are still “esoteric, and hard to come by.”   This is no longer the case, and I feel that we should grudgingly ascribe some of our “hiring fails” at least partially to this cause.

        But also:   have we seriously forgotten the job that we somehow successfully landed, which was based on some technology or language that we had never even heard of before we walked in that door?   We self-congratulated ourselves that we were “bluffing,” but were we?   Maybe the hiring manager was actually looking for resourcefulness, knowing (far more than we did, at the time ...) that we would have to “make it up as we went along” on the job, anyway?

        I very-candidly admit that I bluffed my way into my first Perl-centric engagement in just this way, full-well knowing that I could manage the peculiarities of <<this_language>> precisely as I had already done so many times in the past.   And as I, of course, subsequently did.

        But my specific(!) never-planned-for career history has nonetheless served me quite well, because it repeatedly put me in very-close contact with business stakeholders, in the context of ever-changing technical situations that I was thereafter obliged to immediately assimilate, “somehow, always, four paws down.”   I found that technical prowess ... in this-or-that tool de jour ... is not the decision-factor that should qualify or disqualify a candidate.   The actual deal-breaker characteristics are purely human, purely social ones ... and, very hard to come by.

        I refuse to “sugar coat” the situation.   As one well-known public official quipped, “I inherited a mess.”   Are you both “able” and “willing” to dive into this messy and thoroughly-ambiguous situation with me ... with the team that I am now striving to assemble ... and thence to stick it out until our team has delivered our (internal or external) customer from the snapping jaws of his problem?   Are you both “technically good enough” and “socially professional enough” to own this customer’s problem, and thus to enable our team to solve it as no one else before us ever did?

        Uh huh.   Interesting interviews.   But, a tremendously rewarding (albeit, “hang on to your hat!”) career.

Re: Rosetta Dispatch Table
by sundialsvc4 (Abbot) on Nov 22, 2017 at 01:41 UTC

    “Oh dear ... you say you actually did that to some poor candidate ...??”

    ;-)

      “Oh dear ... you say you actually did that to some poor candidate ...??” ;-)

      Yes, I always ask job candidates to write code.

      I'm often surprised when a candidate “talks a good game” ... but then “struggles” when asked to write “actual working code”. Perhaps I'm being unfair -- though I'm sure you would pass with flying colours -- but I always insist that the code actually runs. I don't accept extemporaneous code™ or even “a quasi-Perl sketch with C++ comments”(!)®   ;-)

      I'm still not at all confident of my interviewing skills though. See On Interviewing and Interview Questions for some history of my struggles with interviewing over the years.

        Heh ...   The only thing that I hate worse than taking an interview ... is giving one.   :-D

        These days, I no longer attempt to do interviews where I ask a candidate to write source-code “on the spot.”   Because, well, “we all have our good days and our bad days,” and a candidate who really wants the job is pretty stressed-out already.   Just today, I was working on a little PHP (ick ...) subroutine, and, even though I knew exactly what I was setting-out to do, my brain was in “stupid mode™” ... (a known side-effect of exposure to PHP) ... and it took me a silly-amount of extra minutes to actually get it right.

        If I simply give someone something that – at that instant (give ’em a break, they’re interviewing!) – they couldn’t do on the spot(!) (God bless ’em ... who could?), I do not feel that I have actually learned anything about them.   Except what they look like when they sweat.   I try to be sensitive to the fact that this kind of employment is a two-way street:   we are both evaluating each other, and (if we are any good at this ...) we both have options.   I don’t want to present my workplace as a pressure-cooker that only an idiot would want to work for.   (“Even if it ...” uhh, nevermind.)

        Today, I will sometimes ask a candidate to “bring a little source-code to show me,” and I very-quickly learned to further specify, “on no more than five ordinary sheets of paper.”   I will then strike-up a conversation while I read the code, then ask the candidate to specifically explain “what does that do?”   (The fakers fall flat ... I offer them a cup of coffee as I show them out.)

        (I also stress that I really don’t care if it is Perl source-code, as long as it is actually their source-code, and as long as it fits on no more than five ordinary sheets of paper ...)

        But, since I am really looking for “‘always land four-paws-down™’ problem-solving ability,” as well as teamwork and a disposition to work as part of a team rather than as a “self-important lone wolf,” I will sometimes just ask something like ... “So, what’s the latest thing that you’ve done at your present job that was really important to someone else – say, a co-worker – and who was it who thought so?”   (I learned to add “say, a co-worker” so that I wouldn’t learn about another company’s vice-presidents or anyone else that the candidate supposed would sound impressive.)   And then, the kicker:   “Okay, and who else on your team helped you to do that?”   (I very-carefully watch their body language as they react to this.)

        Or:   “Without naming names, of course, who on your present team, besides your manager, has been of the greatest help to you recently, and how so?”

        I have long-ago concluded that:   “Our business is not ‘arcane’ anymore.”   No matter what language(s) we use, the technical skills are commonplace, and/or the ability to quickly learn them.   (No one anymore, when we say at a dinner party that we are “computer programmers,” will look at us strangely, even if they still don’t know what we actually do.)   Today, if this candidate is sitting in front of me, it is a pretty safe bet that s/he is technically qualified.   Therefore, the most-important things that I need to look for are social skills, including creativity, persistence, tact, teamwork, diplomacy(!), and the ability to handle frustration.

        Although I take for granted that the candidate is conversant in the [Perl ...] language(s), I also know that I could teach it to the right candidate, if they had not already resourcefully self-taught themselves before the first day on the job.   (Perl, somewhat-quirky though it is, is actually not that obtuse ... or, remarkable.   Sorry ...)

        • “Knowing, off the top of your head, “how to write a program in <this> language (be it Perl or otherwise) ... these days, won’t [automatically™] get you hired.
        • “Knowing, off the top of your head (supplemented of-course by not-too-much Googling®), how to resourcefully apply <this> tool(s) to solve <that> business problem... and(!!) to be diplomatic when asked to do it by a moron ... most-certainly (still) will.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://1203952]
Approved by ww
Front-paged by ww
help
Chatterbox?
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others scrutinizing the Monastery: (4)
As of 2018-07-20 14:50 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    It has been suggested to rename Perl 6 in order to boost its marketing potential. Which name would you prefer?















    Results (435 votes). Check out past polls.

    Notices?