http://www.perlmonks.org?node_id=479

If you have a question on how to do something in Perl, or you need a Perl solution to an actual real-life problem, or you're unsure why something you've tried just isn't working... then this section is the place to ask.

However, you might consider asking in the chatterbox first (if you're a registered user). The response time tends to be quicker, and if it turns out that the problem/solutions are too much for the cb to handle, the kind monks will be sure to direct you here.

Post a new question!

User Questions
Passing a list as a subroutine's return value ( list of HTML::Element objects )
2 direct replies — Read more / Contribute
by mldvx4
on Sep 27, 2023 at 04:28

    Greetings

    I would like to pass a list of HTML::Element objects as a return value from a subroutine. I thought since the list would be just a list, that the normal approach would apply. Clearly I am misunderstanding something, or perhaps there is a more appropriate approach:

    #!/usr/bin/perl use HTML::TreeBuilder::XPath; use HTML::Element; use HTML::Entities qw(decode_entities); use Data::Dumper; use strict; use warnings; my $html = &layer(3); print $html->as_XML,"\n"; exit(0); sub layer { my ($layer) = (@_); my $ul = HTML::Element->new('ul'); my $li = HTML::Element->new('li'); $li->push_content("Layer $layer"); $ul->push_content($li); if($layer--) { my $h = &layer($layer); my @c = &unescape_entities($h); # offending line print "Wrong structure:\n",Dumper(@c),"\n ----\n"; exit(1); $ul->push_content($h); } else { my $foo = ' foo < bar'; my $literal = HTML::Element->new('~literal', text=>$foo); $li->push_content($literal); $ul->push_content($li); } return($ul); } sub unescape_entities { my ($html) = (@_); my $tmp = HTML::TreeBuilder::XPath->new; $tmp->parse(decode_entities($html->as_XML)); my @c = $tmp->findnodes('//body/*'); print "Right structure:\n", Dumper(@c),"\n ----\n"; $tmp->delete; return(@c); # this is getting transformed }

    I would expect that the script (minus the exit) to produce a nested HTML unordered list. What happens is that the variable actually recovered from the subroutine is basically empty, as seen by comparing the output from the two Dump calls.

    I've looked at the manual pages for the modules given above as well as the one for perlrref. Please nudge me in the right direction.

Testing with Test::Mock::Tiny::HTTP
2 direct replies — Read more / Contribute
by Bod
on Sep 26, 2023 at 16:11

    I am writing some tests of a module that fetches a webpage using HTTP::Tiny->get

    To test it, I am trying to use Test::Mock::HTTP::Tiny but I've never tried to use a Test::Mock module before. It's been on my radar since kcott mentioned their existence many moons ago. Now I have the need for one...but the documentation is lacking (to put it mildly!)

    First I've run this code to get the mock data:

    use strict; use warnings; use HTTP::Tiny; use Test::Mock::HTTP::Tiny; my $http = HTTP::Tiny->new; my $resp = $http->get('http://www.way-finder.uk/'); open my $fh, '>', 'mock_html.dat'; print $fh Test::Mock::HTTP::Tiny->captured_data_dump; close $fh;
    Then I've renamed all the references to the domain www.way-finder.uk (a real domain) to www.testing.crawl (a mock domain). I've done this because I don't want the tests going out to a live site as it will change over time invalidating the tests.

    My test file looks like this:

    use strict; use warnings; use Test::More; use Test::Mock::HTTP::Tiny; use WWW::Crawl; plan tests => 1; $/ = undef; open my $fh, '<', 't/mock_html.dat' or die "Can't open datafile"; my $replay = <$fh>; close $fh; die "Nothing to replay" unless $replay; Test::Mock::HTTP::Tiny->set_mocked_data($replay); my $crawl = WWW::Crawl->new( 'timestamp' => 'a', ); my @links = $crawl->crawl('https://www.testing.crawl', \&link); cmp_ok ( scalar @links, '==', 8, 'Correct link count'); sub link { diag ($_[0]); }
    The method $crawl->crawl uses HTTP::Tiny->get to get the web address.

    My expectation was that Test::Mock::HTTP::Tiny would replay the website to HTTP::Tiny but instead it gives the HTTP error 599 - Internal Exception

    Is this the right way to use Test::Mock objects or am I completely off track here?

    EDIT:
    Corrected typo in title

Finding when a feature or keyword was introduced
5 direct replies — Read more / Contribute
by Bod
on Sep 26, 2023 at 13:12

    Is there an easy way to find out when a feature or keyword was introduced to Perl?
    In other words, which version introduced it...

    I have a module that uses pos, a keyword I infrequently use. How can I find out when this, or any other keyword, was introduced so I can set the minimum required Perl version?

    Obviously I could go through each delta until I find it but that seems rather tedious...

Writing Perl with Emacs: Are there perl-mode users around?
2 direct replies — Read more / Contribute
by haj
on Sep 25, 2023 at 09:54

    Emacs comes with two different major modes to edit Perl code: perl-mode and cperl-mode.

    perl-mode is somewhat stuck with the Perl syntax of 5.14, has less features, but a cleaner implementation. cperl-mode is up to date with Perl 5.38 and has deeper understanding of Perl syntax, but a somewhat arcane implementation, most of it written in the previous century.

    With all due respect to TIMTOWTDI, maintaining two major modes turns out to be not enough fun in the long run, and last week Stefan Kangas opened a wishlist item to Making perl-mode.el obsolete.

    The mail thread shows that some people prefer perl-mode because it is less "colorful" and intrusive than cperl-mode. Therefore, the idea is to enable cperl-mode to (optionally) look like and behave like perl-mode. That way, perl-mode.el can be obsoleted without making those users uncomfortable: perl-mode would continue to exist as a custom theme of cperl-mode.

    Users of perl-mode are now encouraged to try cperl-mode, and to report bugs against cperl-mode where they prefer the behavior of perl-mode. This has already started. The "current" cperl-mode.el is available from the repository: cperl-mode.el and is supposed to work with Emacs 26 and newer.

    Keep the reports going!

    This article also appears on blogs.perl.org.

    Edited: Fixed the link to blogs.perl.org. Thanks to hippo for noticing!

How can I send a "transposed slice" of an array of hashes and by extension an array of arrays, or hash of hashes to a subroutine
5 direct replies — Read more / Contribute
by ObiPanda
on Sep 24, 2023 at 16:34

    I very much appreciate the thorough answers I receive here: they have helped me get reacquainted with Perl. The hardest part I still encounter is to understand what the symbols mean, not so much the concepts.

    I'm simply asking for a "better" way to accomplish a task I already can do.

    An example: I want to know if there's a way to send just one key & value per array/hash - preferably by reference - to a subroutine. In the example code below, I want to send all and only the {Sub_Name}s and their respective values. The code below works and I've come up with two ways to list the Sub_Name. I'm simply wanting to know if there's a better way to do it, so I don't have to make an array or send the entire array of hashes to the subroutine.

    #!/usr/bin/env perl #use 5.36.1; use strict; use warnings; use Data::Dumper; use autodie; use File::Find; use File::Copy; use File::Rename; use feature 'fc'; use File::Path qw( make_path ); my $Wait_Time = 10; # Implement Time Delay # Subscription DATA sets my @Subscription = ( { Sub_Name => "Morph", Archive_File => "Morph Archive.txt", }, { Sub_Name => "Analogue", Archive_File => "Analogue Archive.txt", }, { Sub_Name => "Cat", Archive_File => "Cat Archive.txt", }, { Sub_Name => "Zoonotic", Archive_File => "Zoonotic Archive.txt", }, { Sub_Name => "Hydro", Archive_File => "Hydro Archive.txt", }, ); #Subscription of Names my @Subscription_Names_List; for (@Subscription) {push @Subscription_Names_List, $_->{Sub_Name};} List_Subscriptions(\@Subscription_Names_List); for (@Subscription) { Display_Subs(\%$_); } sub Display_Subs { my ($my_Sub) = @_; print "Testing Subscription to: $my_Sub->{Sub_Name}\n"; return $my_Sub; } sub List_Subscriptions { my (@Sub_ARRAY) = @{$_[0]}; # my (@Sub_ARRAY) = @_; print "The Current Subscription List:\n\n"; for (@Sub_ARRAY) { print " \t$_ \n"; } print "\n\n"; }
Windows precompiled binaries or DIY compile
8 direct replies — Read more / Contribute
by ObiPanda
on Sep 23, 2023 at 12:43

    Is it better to use a "packaged" easily installable version of Perl such as Strawberry on a Windows OS or to compile a version or there is no practical difference, etc...?

    I primarily ask for two reasons: stability and speed. An underlying assumption to both reasons is that compiling code will always be faster, because it will compile based on specific chipsets and its respective native math libraries.

      1. Stability. My systems run both AMD CPUs and GPUs. I have wondered if instability in GAMES are sometimes caused by various issues of the binaries being compiled for Intel CPUs and NVidia GPUs. By extension, is there an inherent stability benefit of compiling Perl on a Windows system?
      2. Speed. This likely has no real practical value unless one is running a "large" server on Windows; but, I wanted to ask anyway. So, by a similar analogy to GAMES stability, since there is a real benefit to GAMES which have been optimized for either AMD or Intel/NVidia and sometimes specifically a chipset, is there a speed benefit to compiling Perl on Windows?

    thanks

Algorithm RFC: fast (pseudo-)random shuffle with no repetition
5 direct replies — Read more / Contribute
by Anonymous Monk
on Sep 22, 2023 at 17:20
    The was the SO question recently, and as it sometimes happens, when I think "oh, this can be fun to play with, for better algorithm", it brewed for itself somewhere in subconscious, until the eureka moment a couple days ago (I'm in no hurry :-)): "Of course! Be greedy, demand twice as needed!"

    There was advice to use brute force, at least as accepted answer in linked question (from 2021) there. Not sure if I got it right, I don't read Ruby, and didn't try other answers. Both brute subroutines below aren't actually used: they are totally unusable for lists with ~15 unique strings or more, plus any decent amount of duplicates. I wrote 2nd one because I couldn't believe it at first (accepted solution??). They are left just in case anyone wants to try (or point at my mistakes in implementation?), and can be ignored.

    Back to answers at SO, there are 2 Perl solutions. One (a) doesn't compile; (b) if fixed, emits a warning for un-initialized value; (c) if fixed (or ignored), it seems to work OK. But for (corner-case, of course) input of (b,a,a), it gives answer (b,a). I didn't look further.

    Another solution (by esteemed monk) fails randomly for e.g. corner-case (a,a,a,b,b) -- the only answer can be (a,b,a,b,a), of course. Why does it fail? Output list is initialized to e.g. (a,b). If 1st key to iterate is "a", then one of 2 remaining "a"'s is added to give (a,b,a) with no place for 2nd remaining "a". So, easy fix would be kind of "breadth-first" hash consumption. I'm sorry if code I had to add looks ugly to the author.

    This fixed version will serve as reference to compare my solution to, it generates truly random lists.

    With algorithm I suggest -- ask for twice as many random indexes from remaining pool, then simply reject (comb out) half of them. It guarantees there will be no consecutive dupes (and of course doesn't mean "only odd or even indexes for this value").

    One obvious compromise on randomness will be "dupes are never placed at both head and tail" -- except corner-cases such as 'aba' or 'abaca', of course. There are actually 3 cases, depending on size of remaining pool. Cases "2" and "3" restrict randomness further. E.g., for 'aaaabbbcc', the 'c' is never placed at indexes 0 or 1 -- unlike the "reference SO implementation with true randomness".

    However, lines with "die" in them can be un-commented (and they were un-commented during benchmarking) if input is not an artificial corner-case -- this code is never reached with realistic data. I mean, other than corner-cases and head/tail restriction, my algorithm seems to produce random enough result.

    (In fact, one of "requests" of "RFC" is how to estimate randomness (entropy) for multiple runs of subroutine. Didn't look into that yet.)

    Further "requests" are: can it be improved? Both List::MoreUtils::samples and e.g. (unused) Math::Prime::Util::randperm return their result shuffled, which I don't need and have to sort back to order! And more, e.g. samples takes random samples and therefore should know which items were unselected, but I have no better way to find out "which" except with more work using singleton. It feels like huge amount of unnecessary work I do (though it's still much faster than "SO reference solution"). Or maybe, perhaps, someone would suggest even faster solution?

    (+ I understand there's sloppiness on my side in e.g. $uniq variable name doesn't actually mean number of unique items which fake_data returns. I hope this (and similar) can be forgiven.)

    use strict; use warnings; use feature 'say'; use List::Util qw/ shuffle /; use List::MoreUtils qw/ part samples singleton /; use ntheory qw/ forperm lastfor /; use Algorithm::Combinatorics qw/ permutations /; use Benchmark 'cmpthese'; my @input = shuffle( qw( a a a a b b b c c )); # corner-cases @input = shuffle( qw( a a a b b )); # srand 123; @input = fake_data( 555, 55 ); #say scalar @input; # 2096 sub fake_data { my ( $uniq, $pivot ) = @_; my @tmp = map { sprintf '< %06d >', rand 1e9 } 0 ... $uniq; my @out; push @out, @tmp[ 0 .. $_ ] for 0 .. $pivot; @out = shuffle( @out, @tmp[ $pivot + 1 .. $uniq ]); return @out } cmpthese 10, { SO_fixed => sub { die unless SO_fixed( \@input )}, my_shuffle => sub { die unless my_shuffle( \@input )}, }; sub brute { my $input_ref = shift; my @output; forperm { my $prev = ''; for ( @_ ) { return if $prev eq $input_ref-> [ $_ ]; $prev = $input_ref-> [ $_ ] } @output = @{ $input_ref }[ @_ ]; lastfor, return } @$input_ref; return \@output } sub brute2 { my $input_ref = shift; my @output; my $iter = permutations( $input_ref ); PERM: while ( my $p = $iter-> next ) { my $prev = ''; for ( @$p ) { next PERM if $prev eq $_; $prev = $_ } @output = @$p; last PERM } return \@output } sub SO_fixed { my $input_ref = shift; my %counts; ++$counts{ $_ } for @$input_ref; my @strings = shuffle keys %counts; LOOP: { my $any = 0; for my $string ( keys( %counts ) ) { next if $counts{ $string } == 1; $counts{ $string } --; $any = 1; my @safe = grep { $_ == 0 || $strings[ $_ - 1 ] ne $string + } grep { $_ == @strings || $strings[ $_ ] ne $string + } 0 .. @strings; return undef unless @safe; my $pick = $safe[ rand( @safe ) ]; splice( @strings, $pick, 0, $string ); } redo LOOP if $any } return \@strings } sub my_shuffle { my $input_ref = shift; my @output; my %counts; $counts{ $_ } ++ for @$input_ref; my ( $single, $multi ) = part { $counts{ $_ } > 1 } keys %counts; my @multi = sort { $counts{ $b } <=> $counts{ $a }} @$multi; my @pool = ( 0 .. $#$input_ref ); for my $str ( @multi ) { my $count = $counts{ $str }; my @take; if ( $count <= @pool / 2 ) { # case 1 my @excess = sort { $a <=> $b } samples( 2 * $count, @pool + ); my $n = int rand 2; my @idx = grep { $n ^ $_ % 2 } 0 .. $#excess; @take = @excess[ @idx ]; } elsif ( 2 * $count - 1 == @pool ) { # case 2 #die 'This code is unreachable for realistic input'; my @idx = grep { not $_ % 2 } 0 .. $#pool; @take = @pool[ @idx ]; } else { # case 3 #die 'This code is unreachable for realistic input'; my $prev = -2; my @ok = grep { my $res = $_ - $prev; $prev = $_; $res > 1 } @pool; return undef if $count > @ok; @take = samples( $count, @ok ); } @pool = singleton @pool, @take; @output[ $_ ] = $str for @take; } @output[ @pool ] = @$single if @pool; return \@output; } __END__ (warning: too few iterations for a reliable count) Rate SO_fixed my_shuffle SO_fixed 2.29/s -- -95% my_shuffle 42.7/s 1763% --
Perl output is not inducing file download as expected
4 direct replies — Read more / Contribute
by Polyglot
on Sep 22, 2023 at 07:37
    I'm using the following subroutine to send a binary (PDF) file back to the client's browser.

    # IMPORTANT MODULES FOR THIS CODE... use CGI qw(-utf8); use File::Spec::Functions qw( catfile ); sub send_file { my ($cgi, $dir, $file) = @_; # $dir = '/var/www/download/'; # $file = 'MyLaTeXDocument.pdf'; my $path = catfile($dir, $file); open my $fh, '<:raw', $path or die "Cannot open '$path': $!\n"; $cgi->charset(''); #REMOVES PRIOR UTF-8 SETTING, AS THIS IS BINARY + FILE print $cgi->header( -type => 'application/octet-stream', -attachment => $file, ); binmode STDOUT, ':raw'; print while <$fh>; close $fh or die "Cannot close '$path': $!"; return; }

    The browser console sees a string of characters returning in the response, but no download dialogue is opened.

    Response headers

    Connection Keep-Alive Content-Disposition attachment; filename="MyLaTeXDocument.pdf" Content-Type application/octet-stream Date Fri, 22 Sep 2023 11:13:27 GMT Keep-Alive timeout=5, max=100 Server Apache/2.4.52 (Ubuntu) Transfer-Encoding chunked

    Response Payload

    JVBERi0xLjUKJeTw7fg... [truncated...too lazy to type more]
    Why won't the browser just open the "Save as..." dialogue? As it stands, the browser appears to do nothing, silently dropping this activity in background. What is lacking in this code?

    Blessings,

    ~Polyglot~

How to get the TOTAL length/size of an array?
7 direct replies — Read more / Contribute
by Polyglot
on Sep 21, 2023 at 00:40
    PerlMaven has an interesting example that is short by one additional array characterization--one which I have searched for online in vain. Here is his example:

    my $one_string = "hello world"; say length $one_string; # 11 my @many_strings = ("abc", "cd", "e", "fg", "hi", "hello world"); say length @many_strings; # 1 say scalar @many_strings; # 6

    I want to know what it would take for the next one:

    say __?__ @many_strings; # 21

    Blessings,

    ~Polyglot~

How to bring in multimethods
3 direct replies — Read more / Contribute
by karlgoethebier
on Sep 20, 2023 at 09:28

    Hi all, please consider this:

    Foo.pm

    package Foo { use strict; use warnings; use Class::Tiny; 1; } __END__

    Bar.pm

    package Bar { use strict; use warnings; use Class::Tiny; 1; } __END__

    Methods.pm

    package Methods { use strict; use warnings; use feature qw(isa); use Logic::Easy; no warnings qw(redefine); sub frobnicate : Multi(frobnicate) { SIG [$o] where {$o isa 'Foo'}; qq(foo); } sub frobnicate : Multi(frobnicate) { SIG [$o] where {$o isa 'Bar'}; qq(bar); } 1; } __END__

    run.pl

    #!/usr/bin/env perl use strict; use warnings; use lib q(.); use Bar; use Foo; use Methods; use feature qw(say); my $gizmo = Foo->new(); my $mojo = Bar->new(); # dd $gizmo, $mojo; say Methods::frobnicate($_) for ($gizmo, $mojo); __END__

    As expected, frobnicate cannot be imported - "there can be only one". Also with Methods->frobnicate() it does not work. Remains only Methods::frobnicate() - as to see. Or is there something else I have overlooked?

    «The Crux of the Biscuit is the Apostrophe»


Add your question
Title:
Your question:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":