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

May be a silly question to ask from my side but I need help in this. The snippet of code below is taking permission from command prompt whether to remove the <alt> tag or not.
print "do you want to remove <alt> tags Y/N \n"; chomp(my $ok = <>), my $yes = 'y'; my $no = 'n'; $inp = lc($ok); if ($inp eq $yes){ print "removed <alt>"; } elsif ($inp eq $no) { print "not removed <alt>"; }
The function with which I want to merge is this:
sub RemoveAltTag($) { my $doc = shift; ############################## my $cnt = 0; my $nodes = $doc->getElementsByTagName("image"); for(my $i = 0;$i < $nodes->getLength(); $i++) { my $kids = $nodes->item($i)->getChildNodes(); for(my $k =0; $k < $kids->getLength(); $k++) { if($kids->item($k)->toString() =~ /<alt>/i) { $nodes->item($i)->removeChild($kids->item($k)); print "\n Removed <alt> tag" if $VERBOSE; $cnt++ } } } return $cnt;
kindly help me with this.

Replies are listed 'Best First'.
Re: How to merge the given snippet of code into the below functionality.
by kcott (Archbishop) on Feb 03, 2022 at 20:34 UTC

    G'day vlearner,

    In the examples below, I've used perle, a common alias of mine, to report problems.

    $ alias perle alias perle='perl -Mstrict -Mwarnings -Mautodie=:all -MCarp::Always -E +'

    As a bare minimum, your code should include the strict and warnings pragmata. See "Perl introduction for beginners: Safety net".

    There are a variety of issues with the code you posted: some major; some minor; some future gotchas. I'll work through those (mostly) in the order in which they appear.

    chomp(my $ok = <>),

    Terminate statements with a semicolon, not a comma.

    $ perle 'print "Query (Y/N): "; chomp(my $in = <>), say $in;' Global symbol "$in" requires explicit package name (did you forget to +declare "my $in"?) at -e line 1. Execution of -e aborted due to compilation errors. at -e line 1.

    Code that you post here — even test code — should work, so that we don't have to debug it first. The only exception would be code that you can't fix; in which case, it should be accompanied with exception messages. A good place to start is "perl -c" for a syntax check; but do note that it is just a starting point:

    $ perl -MO=Deparse -e 'chomp(my $in = <>), say $in;' chomp(my $in = readline ARGV), $in->say; -e syntax OK
    print "do you want to remove <alt> tags Y/N \n";

    Allow the user to respond immediately after, not underneath, the prompt. Just remove \n to fix.

    my $ok = <>

    Use <STDIN> instead of <>. Compare these:

    # This dies: $ perle 'print "Query (Y/N): "; chomp(my $in = <>); say $in;' some_arg Can't open some_arg: No such file or directory at -e line 1. Use of uninitialized value $in in chomp at -e line 1. Use of uninitialized value $in in say at -e line 1. Query (Y/N): # This allows input: $ perle 'print "Query (Y/N): "; chomp(my $in = <STDIN>); say $in;' som +e_arg Query (Y/N): y y
    print "do you want to remove <alt> tags Y/N \n"; chomp(my $ok = <>), my $yes = 'y'; my $no = 'n';

    The prompt uses uppercase; the code afterwards uses lowercase. Be consistent to improve readability.

    $inp =

    Don't use package variables; use lexical variables. In this instance, $inp is not needed (see below).

    lc($ok)

    Prefer fc() — see that documentation for details. You'll need Perl v5.16 for this: "perl5160delta: New function fc and corresponding escape sequence \F for Unicode foldcase".

    if ($inp eq $yes) {...} elsif ($inp eq $no) {...}

    This is a problem unless your users have keyboards with only the two letters "Y" and "N". :-)

    if (fc($ok) eq fc($yes)) { print "Removing ...\n"; RemoveAltTag($some_doc_variable); } else { print "No removal.\n"; }
    sub RemoveAltTag($)

    Unless you know what you're doing, and why you're doing it, don't use prototypes. See "perlsub: Prototypes" for details.

    my $doc = shift;

    Consider future-proofing your code with: 'my ($doc) = @_;'. For many years, I fell into the trap of writing 'my $param1 = shift;'; then later, adding another parameter, and ending up with 'my ($param1, $param2) = shift;' which, if I recall, was often a difficult bug to track down.

    for(my $i = 0;$i < $nodes->getLength(); $i++)

    This form of for often results in off-by-one errors. It's less typing, clearer code, less error-prone, and more Perlish, to use this form:

    for my $i (0 .. $end) { ... }

    — Ken

Re: How to merge the given snippet of code into the below functionality.
by hippo (Archbishop) on Feb 03, 2022 at 17:10 UTC
    RemoveAltTag ($foo) if $inp eq $yes;

    🦛

Re: How to merge the given snippet of code into the below functionality.
by Fletch (Bishop) on Feb 03, 2022 at 18:02 UTC

    Rather than rolling your own interaction routine you might try IO::Prompter or IO::Prompt from CPAN.

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

Re: How to merge the given snippet of code into the below functionality.
by davido (Cardinal) on Feb 04, 2022 at 04:17 UTC

    Something like this ought to work, though if it were me I would prefer providing the user with some context if I'm going to prompt them to remove an <alt> tag.

    use IO::Prompt::Tiny q(prompt); sub remove_ok { my $prompt = 'Do you want to remove <alt> tags? (y/n)'; my $default = 'n'; my $resp = ''; while ($resp !~ m/^[yn]/i) { $resp = prompt($prompt, $default); } return $resp =~ m/^y/i; } sub RemoveAltTag { my $doc = shift; my $cnt = 0; my $nodes = $doc->getElementsByTagName("image"); for(my $i = 0;$i < $nodes->getLength(); $i++) { my $kids = $nodes->item($i)->getChildNodes(); for(my $k =0; $k < $kids->getLength(); $k++) { if( $kids->item($k)->toString() =~ /<alt>/i && remove_ok() ) { $nodes->item($i)->removeChild($kids->item($k)); print "\n Removed <alt> tag" if $VERBOSE; $cnt++ } } } return $cnt; }

    But again, you might want to show some of the surrounding context so that the user can use that information in deciding what to remove.


    Dave