Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

Cool Uses for Perl

( #1044=superdoc: print w/replies, xml ) Need Help??

This section is the place to post your general code offerings -- everything from one-liners to full-blown frameworks and apps.

CUFP's
Moving, copying and renaming files with new tool
No replies — Read more | Post response
by siberia-man
on Aug 14, 2019 at 05:22
    Hello Monks,

    I re-invented the wheel and decided to share it. This is script that is supposed to be used as a tool for moving, copying and renaming files. In the beginning I called it as "the re-invented wheel" becuase there are a few implementations for such kind of functionality. I found 3 of them at least (all are mentioned in the documentation). While developing the script I borrowed some good ideas from those implementations and adapted for my script. And I applied my vision of the conveniency.

    Here I show some scenarios from real life I've really met:

    Removing prefixes and suffixes:

    file-rename 's/^[^.]+\.//; s/\.[^.]+$//' ...
    Enumerate files:
    file-rename 's/^/sprintf "%02d. ", $NR/' ...

    By default the script implements move files, but it is possible to copy them with the option -c, --copy.

    It is posible to include/exclude Perl modules with the option -M for those cases it you need to apply something very specific. It is similar to Perl's own option.

    With the -T or --transcode option it is possible to apply encoding over names. For example the following example works fine for filenames in Cyrillic with Perl 5.14 under Cygwin 1.7.25:

    file-rename -Tutf8 '$_ = ucfirst' -f ...

    Handling with filename component is enabled with the option -N, --filename-only. The is example (prepending filenames with some prefix):

    file-rename 's/^/old-/' -N ../*

    Verbosity, forcing and dry-run are implemented with the -v, -f and -n options, respectively. The long options are also available

    I have still never met the case of using the zero-terminated lines but implemented it with the options -z, -0, --null.

    The last thing I developed is renaming in loop with the option -r, --rename. With this option we can:

    Rotate files cyclically to left (resulting to file2 file3 file4 file1):

    file-name --rename=rotate-left file1 file2 file3 file4
    Rotate files cyclically to right (resulting to file4 file1 file2 file3):
    file-name --rename=rotate-right file1 file2 file3 file4
    Swap pair of files (swap nearest, resulting to file2 file1 file4 file3):
    file-name --rename=swap file1 file2 file3 file4
    Flip the whole list of files (swap farthest, resulting to file4 file3 file2 file1):
    file-name --rename=flip file1 file2 file3 file4

    The script lives in github. Below is the latest version to the moment of the writing.

Animated Heatmap
2 direct replies — Read more / Contribute
by Anonymous Monk
on Aug 10, 2019 at 08:52
    #!/usr/bin/perl # # 100 simultaneous supernovae collapse into a supermassive blackhole! # This makes a pretty picture by incrementing the sigma values on the # sample data resulting in an animated GIF. Please feel free to hack # it up and post your version! use strict; use warnings; use Imager::Heatmap; my $filename = 'heatmapa.gif'; my @insert = sample_data(); my @images = (); $| = 1; print "Generating GIF frame "; for my $x ('1'..'90') { my $hmap = Imager::Heatmap->new( xsize => 300, ysize => 300, xsigma => $x, ysigma => $x, ); $hmap->insert_datas(@insert); push @images, $hmap->draw; print "$x " } print "done!\n"; Imager->write_multi({ file => $filename, transp => 'none', gif_loop => 0, }, @images); print "Saved $filename\n"; sub sample_data { my @insert = (); while (<DATA>) { chomp; push @insert, [ split /\s/ ] } return @insert } __DATA__ 219.051804229367 88.9872727954212 2.72415838471935 38.8303576772919 95.6603462028621 1.72123666988041 45.1631314764216 268.812149286465 0.804643550395877 66.578264086479 1.22143971287905 2.00653228067716 163.336841980614 147.400715133453 2.87112162284165 197.320137757956 156.07341443269 1.18392267718181 129.67948373001 58.3706442953391 0.803632792287029 206.786042768876 293.853125662186 1.82149200322278 284.623912839804 236.230247601173 2.52708524563815 283.432531746859 149.101459047907 0.774716745801001 26.3156915130299 110.461384379918 2.78433454992581 116.280801427982 298.318189644877 0.889656570195449 260.600329645978 161.876773932393 1.99215632328459 26.5011306311528 256.384608393465 1.38880983326783 83.3079530587614 298.616000219482 2.13076737256037 258.819817681858 63.5060206944549 2.6169114535903 257.616254133365 130.265938008094 1.3196706466417 15.5737558914005 228.476526023456 1.71396074178939 172.958427864326 134.361279761908 0.616677842364464 96.2823219903213 91.610473931559 0.750908991035423 236.961567915958 22.5412194249984 0.733476341421284 33.0355872339999 146.982273461687 1.38645956240103 274.781681119399 277.375471960985 1.28758147461576 274.554895092906 9.64695556856619 2.52964448242347 47.7020972409246 69.3046233133827 2.50824542155272 270.695964570876 174.266969869197 2.58525980872587 150.449885151578 120.725371198669 2.18592811183062 227.822390833157 99.2721473389881 0.86853383233008 186.674904836348 263.644322352447 1.79517340633784 16.2277964745222 89.7084513241149 0.804323118898253 224.121987734913 22.8778173791731 1.4652342209894 114.801624397636 101.908404538988 1.8534394945821 186.063758052809 137.50982170009 2.11562716870688 113.880535534732 124.778518527625 1.79677188736066 256.586035989917 129.649507301872 0.0310617039138883 168.699618610333 194.088909109542 1.52441859325841 229.911780325491 293.672143213136 2.18697827539392 262.394562757206 266.18278106135 0.21784286280419 261.186309813363 279.739283305766 2.65342067631746 172.598778926091 112.915474007112 0.318370872482383 161.474786513763 262.61338263385 1.30891166163741 202.819527123733 116.302152586106 2.98949580198961 44.2752140642415 234.457814079602 0.070243098538878 149.017189910219 67.5233212581507 1.80992425309736 189.78448565856 17.8635011090094 2.84895099057987 171.337181428749 235.505818339764 1.31609788905894 298.763847848466 172.273778698757 1.07685108799202 163.575011025641 133.753537318352 1.41111693520529 185.29670088446 38.7415892388702 1.49994091839003 138.128510267827 201.604577712822 1.56174400372687 92.3971559392572 12.4387771325878 0.431789147416719 251.591916223737 175.434089411266 1.71464686909359 177.979599312939 161.925806575026 1.72004615776216 277.785929330006 250.655954304365 0.0849772369267896 224.114415392595 38.9031874142085 1.14308934159964 198.182388900462 266.666609152744 0.586216710776291 276.396738696131 295.09497084847 2.34086614235889 13.7970156878787 211.201957438183 1.86106049657808 94.0508635177014 109.722540128516 0.69720358584966 78.7046699853789 0.523458727613856 1.21298476973325 160.818115776243 210.942067962107 1.17804568045752 57.3220753369533 44.3129663991423 1.31626080663938 12.4813752912534 220.971653603782 0.999803609658354 240.516981027928 126.105233923117 2.0221446329446 97.5204378748695 258.604994675503 0.990934685034208 116.021835559067 195.835485383258 2.77921016701676 47.7045928108299 24.6872369372347 2.4858777661543 198.725524882548 68.832671189796 0.0116277984877158 263.399127668546 284.004349693855 2.40618731727658 43.294134523476 179.118577091476 0.113292797706293 65.2306402180872 143.466891495816 0.376630609668549 199.359422722217 189.301256344898 0.0399094612900583 280.805205262179 279.296274297957 1.99059111855201 132.242637643764 218.157219786795 1.23879542526741 193.69486843522 10.0438935596493 2.59132699746909 171.127396378509 150.476524305808 2.35057725061366 270.948765280388 74.7769294673688 1.27990336967754 246.381808328795 207.286740227151 2.95748717084632 165.013808799495 157.475790067752 1.12025332499351 141.11136816545 188.2304360916 2.04700091098582 51.898373508318 160.813401405059 0.945193443436867 223.898568651716 206.849446932375 1.84423575399821 236.080016999957 277.636154733142 2.81535531421177 206.507445144285 258.304165009526 0.761047333085337 3.58594137810789 100.98537021846 1.81141974417055 61.7227718623476 200.003718492554 2.32470932299239 220.724602367901 24.650801867741 2.72094047958512 161.47169997666 126.132462200077 1.89587466604485 199.545591065019 65.4269759991849 2.99310639442809 286.248742335322 147.240683827291 2.78527544762646 128.037306298255 2.00280884631425 0.239314932952265 229.465974844797 32.2943462992359 0.975591165237368 273.431292878075 221.745691421587 0.0405515706133492 69.9041882289243 288.210359210613 1.7966328523234 246.013861132753 212.537823993227 2.17469449507044 202.989337138932 233.691516639594 1.85378363737849 240.542733754127 242.813878998893 0.711644744779992 166.54872657784 58.9462742346879 0.927864512940076 277.801370967599 158.887803545861 2.92065009496683 176.548024474562 262.23946790326 0.259036525196102
New stable MCE 1.842 and MCE::Shared 1.842 releases
No replies — Read more | Post response
by marioroy
on Jul 21, 2019 at 23:13

    Update: Added results for Parallel::ForkManager 2.02. The results mentioned in the POD documentation ran slower due to an unaware background job at the time. I reran again on all 5 platforms.

    Dear fellow Monks,

    I am pleased to annouce MCE 1.842 and MCE::Shared 1.842 (both stable). MCE now includes MCE::Channel and MCE::Child recently.

    This weekend, added Parallel::ForkManager-like demonstration to the POD section in MCE::Child and MCE::Hobo. The results were captured on a Macbook Pro (late 2013 model - 2.6 GHz ~ 3.6 GHz with Turbo Boost).

    To run, one may direct standard output to :nul or /dev/null depending on the platform. Or better yet, I commented out the print line in the on_finish handler. Unfortunately, Parallel::ForkManager 2.02 suffers from memory leaks on the Windows platform ($^O eq 'MSWin32') and the reason why slower than Cygwin.

    Time to spin 2,000 workers and obtain results (in seconds).

    MCE::Hobo uses MCE::Shared to retrieve data during reaping. MCE::Child uses MCE::Channel, no shared-manager. Version Cygwin Windows Linux macOS FreeBSD MCE::Child 1.842 19.099s 17.091s 0.965s 1.534s 1.229s MCE::Hobo 1.842 20.514s 19.594s 1.246s 1.629s 1.613s P::FM 1.20 19.703s 19.235s 0.875s 1.445s 1.346s MCE::Child 1.842 20.426s 18.417s 1.116s 1.632s 1.338s Moo loaded MCE::Hobo 1.842 21.809s 20.810s 1.407s 1.759s 1.722s Moo loaded P::FM 2.02 21.668s 25.927s 1.882s 2.612s 2.483s Moo used

    Set posix_exit to avoid all END and destructor processing.

    MCE::Child->init( posix_exit => 1, ... ); MCE::Hobo->init( posix_exit => 1, ... ); Version Cygwin Windows Linux macOS FreeBSD MCE::Child 1.842 19.815s ignored 0.824s 1.284s 1.245s Moo loaded MCE::Hobo 1.842 21.029s ignored 0.953s 1.335s 1.439s Moo loaded

    Parallel::ForkManager

    use strict; use warnings; use Parallel::ForkManager; use Time::HiRes 'time'; my $start = time; my $pm = Parallel::ForkManager->new(10); $pm->set_waitpid_blocking_sleep(0); $pm->run_on_finish( sub { my ($pid, $exit_code, $ident, $exit_signal, $core_dumped, $resp) = + @_; # print "child $pid completed: $ident => ", $resp->[0], "\n"; }); DATA_LOOP: foreach my $data ( 1..2000 ) { # forks and returns the pid for the child my $pid = $pm->start($data) and next DATA_LOOP; my $ret = [ $data * 2 ]; $pm->finish(0, $ret); } $pm->wait_all_children; printf STDERR "duration: %0.03f seconds\n", time - $start;

    MCE::Child

    use strict; use warnings; use MCE::Child 1.842; use Time::HiRes 'time'; my $start = time; MCE::Child->init( max_workers => 10, on_finish => sub { my ($pid, $exit_code, $ident, $exit_signal, $error, $resp) = @ +_; # print "child $pid completed: $ident => ", $resp->[0], "\n"; } ); foreach my $data ( 1..2000 ) { MCE::Child->create( $data, sub { [ $data * 2 ]; }); } MCE::Child->wait_all; printf STDERR "duration: %0.03f seconds\n", time - $start;

    MCE::Hobo

    use strict; use warnings; use MCE::Hobo 1.842; use Time::HiRes 'time'; my $start = time; MCE::Hobo->init( max_workers => 10, on_finish => sub { my ($pid, $exit_code, $ident, $exit_signal, $error, $resp) = @ +_; # print "child $pid completed: $ident => ", $resp->[0], "\n"; } ); foreach my $data ( 1..2000 ) { MCE::Hobo->create( $data, sub { [ $data * 2 ]; }); } MCE::Hobo->wait_all; printf STDERR "duration: %0.03f seconds\n", time - $start;

    These days, there are other ways and instead have workers persist (i.e. pull items from a shared queue or channel) or perhaps via MCE's input and chunking capabilities.

    Regards, Mario

2d field of view, vision algorithm in grid (ray casting)
1 direct reply — Read more / Contribute
by Discipulus
on Jun 24, 2019 at 15:05
    Hello community!

    who knows me is aware I'm writing a game (engine?) and I asked here for the precious monks wisdom about circular area in a coordinates grid (AoA).

    But now I'm progressing and I discovererd that the above illuminate function is not enough. I found a big resource of Roguelike_Vision_Algorithms and I choosed the simplest one (second example) and I ported it to Perl ( field_of_view sub in the below code ).

    Impressed by this shiny exemple I wrapped into an interactive program to show my proof of concept:

    Chatting in the perl irc channel daxim also implemented a semi-transparency feature I'd like to add to my game. Here daxim's patch (and a big thank to him):

    have fun!

    L*

    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
Listener Crossword #4321 solitaire
No replies — Read more | Post response
by GrandFather
on Jun 21, 2019 at 06:19

    The Times newspaper features an occasional "The Listener Crossword" which is in fact a numerically based logic puzzle in the form of a crossword. A friend of mine introduced me to the genera with #4321 which is a puzzle of two parts. The first part consists of populating the playing grid with hexadecimal numbers. The second part consist of using the populated grid to play a game of solitaire which, when played correctly on a correctly constructed grid ends up spelling out three words. There is a certain amount of trial and error involved in finding the solution!

    So to aid playing the game in the second part of the puzzle I wrote the following script. The gameGrid is configured for a partial solution of the game. A feature of the code is that you can "save" the game state at any point then paste the saved gameGrid in place of the current grid to explore possibilities from that point.

    As far as I can tell developing tools of this sort is all part of the solution domain for the puzzle. They are very much one off puzzles as each "crossword" is a puzzle of a completely different nature, so it is very unlikely that this tool will be useful for another "Crossword Puzzle". But it is a cool use for Perl!

    Note that a few shortcuts have been taken in the code. In particular global variables are used, which I usually avoid. The rendered grid is not very pretty and the layout generally is rough, but good enough for the task at hand.

    Play consists of clicking on a "peg" (piece to be moved) then an "empty" cell ("_") skipping over one intervening piece. The skipped piece is removed and added to the "skipped" string. Moves can be undone back to the starting state. For instructions beyond these you will need to find the original puzzle instructions and create the starting grid.

    use strict; use warnings; use Tk; my @gameGrid = ( # Word search start [qw(- - _ C C - -)], [qw(- - _ 0 _ - -)], [qw(D D 1 1 _ C A)], [qw(B _ 0 0 _ B F)], [qw(_ E _ C _ _ 0)], [qw(- - _ _ _ - -)], [qw(- - _ _ _ - -)], ); my $main = Tk::MainWindow->new(-title => "X4321"); my @rows; for my $rowIdx (0 .. 6) { for my $colIdx (0 .. 6) { next if ($rowIdx < 2 || $rowIdx > 4) && ($colIdx < 2 || $colId +x > 4); $rows[$rowIdx][$colIdx] = $main->Button( -textvariable => \$gameGrid[$rowIdx][$colIdx], -command => sub {onClick($rowIdx, $colIdx)} )->form( -top => $rowIdx * 20 + 2, -bottom => $rowIdx * 20 + 19, -left => $colIdx * 75 + 2, -right => $colIdx * 75 + 73 ); } } my @fromValue = ''; my $overValue = ''; my $lastToValue = ''; my $skipped = ''; my @stack; my $topPix = 7 * 20 + 2; my $botPix = 7 * 20 + 19; my $fromField = $main->Label(-textvariable => \$fromValue[0]) ->form( -top => $topPix, -bottom => $botPix, -left => 4, -right => 70 ); my $overField = $main->Label(-textvariable => \$overValue) ->form( -top => $topPix, -bottom => $botPix, -left => 74, -right => 140 ); my $toField = $main->Label(-textvariable => \$lastToValue) ->form( -top => $topPix, -bottom => $botPix, -left => 144, -right => 210 ); my $skippedField = $main->Label(-textvariable => \$skipped) ->form( -top => $topPix, -bottom => $botPix, -left => 214, -right => 460 ); my $undo = $main->Button(-text => 'undo', -command => sub {onUndo()}) ->form( -top => $topPix, -bottom => $botPix, -left => 464, -right => 510 ); my $save = $main->Button(-text => 'save', -command => sub {onSave()}) ->form( -top => $topPix, -bottom => $botPix, -left => 514, -right => 560 ); Tk::MainLoop(); sub onClick { my ($rowIdx, $colIdx) = @_; if (!$fromValue[0]) { $fromValue[0] = sprintf "%d, %d: %s", $rowIdx, $colIdx, $gameG +rid[$rowIdx][$colIdx]; $fromValue[1] = $rowIdx; $fromValue[2] = $colIdx; $lastToValue = ''; return; } if ($rowIdx == $fromValue[1] && $colIdx == $fromValue[2]) { $fromValue[0] = ''; return; } if ( !( ($rowIdx == $fromValue[1] && abs($colIdx - $fromValue[2 +]) == 2) || ($colIdx == $fromValue[2] && abs($rowIdx - $fromValue[1 +]) == 2) ) || $gameGrid[$rowIdx][$colIdx] ne '_' ) { $lastToValue = 'Invalid'; return; } my $rowMid = ($rowIdx + $fromValue[1]) / 2; my $colMid = ($colIdx + $fromValue[2]) / 2; push @stack, [$gameGrid[$rowMid][$colMid], @fromValue[1, 2], $rowI +dx, $colIdx]; $skipped = join ' ', map {$_->[0]} @stack; $gameGrid[$rowIdx][$colIdx] = $gameGrid[$fromValue[1]][$fromValue[ +2]]; $gameGrid[$rowMid][$colMid] = '_'; $gameGrid[$fromValue[1]][$fromValue[2]] = '_'; $fromValue[0] = ''; } sub onUndo { return if !@stack; my $move = pop @stack; my ($delChar, $fromRow, $fromCol, $toRow, $toCol) = @$move; my $rowMid = ($fromRow + $toRow) / 2; my $colMid = ($fromCol + $toCol) / 2; $gameGrid[$fromRow][$fromCol] = $gameGrid[$toRow][$toCol]; $gameGrid[$rowMid][$colMid] = $delChar; $gameGrid[$toRow][$toCol] = '_'; $skipped = join ' ', map {$_->[0]} @stack; $fromValue[0] = ''; } sub onSave { print "my \@gameGrid = (\n"; for my $rowIdx (0 .. 6) { print " [qw("; print join ' ', @{$gameGrid[$rowIdx]}; print ")],\n"; } print " );\n"; }
    Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
Tk ASCII Draw on Canvas
4 direct replies — Read more / Contribute
by Discipulus
on Jun 03, 2019 at 06:57
    Hello folks!,

    another Tk CUFP from my part! It is working and is also a draft for a bigger project (you all know what I'm working on nowadays.. ;)

    This is an ASCII drawing program expoiting the best I'm able to from Canvas and their precious feature: tags.

    I left some commented code because I have some question in case some Tk expert has answers:

    -1 about binding modifiers: I planned the draw action when <Button1-Motion> is on: ie. when button 1 is pressed (modifier) and the pointer is moveing over the Canvas. No luck. The program below now uses <Control-Motion> and perhaps is even better (less mouse->less pain)

    -2 I noticed some strange behaviours with some key: £ aka sterling in Tk world and ° aka degree if I use one of them (now commented in the code) I get back a multichar: uppercase A with caret above plus the degree symbol, for instance.

    -3 I wonder how can I implement an export coordinates range in my Canvas: something like: when SHIFT modifier is on and Motion is on too I should tag tiles with something like selceted use a different color for them and haveing two buttons for export corrdinates and select nothing Seems this the right way?

    PS now you can draw fancy things like:

    L*

    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
Coordinate validator
2 direct replies — Read more / Contribute
by timpoiko
on May 17, 2019 at 05:41
    Dear monks. Some years ago I write small program which can validate coordinates (locations on the Earth) from user input. I didn't to restrict format of input and I wanted support English as well as Finnish.
    #!/usr/bin/perl use strict; sub conv { my $in = shift; $in =~ s/[NWSEIPL]//g; my $sign; if ($in =~ s/([-]+)//) { $sign = $1; } my $dd = $in; if ( $in =~ /([\d+-]+)\s+(\d+)\s+([\d\.]+)/ ) { $dd = $1 + $2/60 + $3/3600; } elsif ( $in =~ /([\d+-]+)\s+(\d+[\d\.]+)/ ) { $dd = $1 + $2/60; } if ($sign ) { $dd = -1 * $dd } return $dd; } sub bar { $_ = shift; print "\n$_\n"; my $comma_count =0; my $space_count =0; my $dot_count =0; my %H; my ($FIN, $ENG); my $err = 0; my @lst; my $latc = 1; my $lonc=1; my ($lat, $lon); #strip all unneeded tr/a-z/A-Z/; s/[^0-9NWSEIPL,.+-]/ /g; s/\s+/ /g; s/([NS])\s*([0-9. ]+)\s*,?\s*([EW])\s*([0-9. ]+)/$2$1,$4$3/; s/([EW])\s*([0-9. ]+)\s*,?\s*([NS])\s*([0-9. ]+)/$4$3,$2$1/; s/([PE])\s*([0-9. ]+)\s*,?\s*([IL])\s*([0-9. ]+)/$2$1,$4$3/; s/([IL])\s*([0-9. ]+)\s*,?\s*([PE])\s*([0-9. ]+)/$4$3,$2$1/; s/([0-9. ]+)\s*([NS])\s*,?\s*([0-9. ]+)\s*([EW])/$2$1,$4$3/; s/([0-9. ]+)\s*([EW])\s*,?\s*([0-9. ]+)\s*([NS])/$4$3,$2$1/; s/([0-9. ]+)\s*([PE])\s*,?\s*([0-9. ]+)\s*([IL])/$2$1,$4$3/; s/([0-9. ]+)\s*([IL])\s*,?\s*([0-9. ]+)\s*([PE])/$4$3,$2$1/; s/([0-9+-]+)\s+(\d+)\s+([0-9.]+)\s+([0-9+-]+)\s+(\d+)\s+([0-9.]+)/ +$1 $2 $3, $4 $5 $6/; s/([0-9+-]+)\s+([0-9.]+)\s+([0-9+-]+)\s+([0-9.]+)/$1 $2, $3 $4/; s/\s+/ /g; print "$_\n"; $comma_count = ($_ =~ tr/,//); $space_count = ($_ =~ tr/ //); $dot_count = ($_ =~ tr/.//); $H{E} = ($_ =~ tr/E//); $H{I} = ($_ =~ tr/I//); $H{P} = ($_ =~ tr/P//); $H{L} = ($_ =~ tr/L//); $H{W} = ($_ =~ tr/W//); $H{N} = ($_ =~ tr/N//); $H{S} = ($_ =~ tr/S//); $ENG = $H{W} + $H{N} + $H{S}; $FIN = $H{L} + $H{P} + $H{I}; if(($ENG >0) && ($FIN > 0)) { print "mixed languages\n"; $err++ } elsif (($FIN>0) && ( ($H{I}+$H{L})>1 || ($H{E}+$H{P})>1 )) { print "too many lats or lons\n"; $err++ } elsif (($ENG>0) && ( ($H{N}+$H{S})>1 || ($H{W}+$H{E})>1 )) { print "too many lats or lons\n"; $err++ } elsif ($H{E}>1) { print "too many E\n"; $err++ } elsif ($H{I}>1) { print "too many I\n"; $err++ } elsif ($H{L}>1) { print "too many L\n"; $err++ } elsif ($H{P}>1) { print "too many P\n"; $err++ } elsif ($H{W}>1) { print "too many W\n"; $err++ } elsif ($H{S}>1) { print "too many S\n"; $err++ } elsif ($H{N}>1) { print "too many N\n"; $err++ } elsif ($comma_count == 3) { my $n = 1; s/(,)/!--$n ? '.' : $1/ge; $n = 2; s/(,)/!--$n ? '.' : $1/ge; @lst = (split(/,/,$_)); } elsif ($comma_count == 2 && $space_count == 1) { s/ /./g; @lst = (split(/ /,$_)); } elsif ($comma_count == 2 && $space_count != 1) { s/,/./g; @lst = (split(/ /,$_)); } elsif ($comma_count == 1) { @lst = (split(/,/,$_)); } elsif ($comma_count > 3) { print "too many commas\n"; $err++ } elsif ($dot_count > 2) { print "too many dots\n"; $err++; } if ($ENG >0) { if (s/S//) { $latc=-1; } if (s/W//) { $lonc=-1; } s/[NE]//g; } elsif ($FIN >0) { if (s/E//) { $latc=-1; } if (s/L//) { $lonc=-1; } s/[IP]//g; } if($err > 0) { print "Err: $err\n"; $err=0; } else { $lst[0] =~ s/^\s+|\s+$//; $lst[1] =~ s/^\s+|\s+$//; $lat = conv($lst[0]); $lon = conv($lst[1]); $lat *= $latc; $lon *= $lonc; my $coords = "$lat, $lon"; if ($coords =~ /^[-+]?([1-8]?\d(\.\d+)?|90(\.0+)?),\s*[-+]?(18 +0(\.0+)?|((1[0-7]\d)|([1-9]?\d))(\.\d+)?)$/) { print "all ok\n"; } else { print "too big numbers\n"; } #Print even there is too big numbers in lat or lon print "LAT: $lat LON: $lon\n" } } #OK my @test =( 'N65, E25', '65P, I25', '25 E, 32 L', '65° 0\' 45.414" N 25° 28\' 17.231" E', '65° 0\' 45.414" N, 25° 28\' 17.231" E', 'S 65° 0\' 45.414" W 25° 28\' 17.231" ', 'S65° 0\' 45.414" E, 25° 28\' 17.231"', '47.9805, -116.5586', '47 58.8300 -116 33.5160', '47 58 49 -116 33 30', '50 58.8300 70 33.5160', '50 58.8300 -70 33.5160', '+50 58 83.00 70 33 77.44' ); for(@test) { bar($_); } undef(@test); #NOT OK print "\nNot ok cases\n"; my @test =( 'N65, N25', '65P, E25', '91, 47', '65P, W25', '100 E, 91 N', '47.9805, -186.5586', ); for(@test) { bar($_); }
perl 2.01 on Cygwin
3 direct replies — Read more / Contribute
by rje
on Mar 26, 2019 at 19:02

    Even though I haven't programmed in C for a long time, I could get Perl2 compiled and running (a 387k binary!) in just a couple hours this afternoon on Cygwin.

    .Can I throw out the in-house malloc and other hand-rolled memory management code? What else can I do away with? How about all of the variant hardware #defines Larry had to make? Can't I simplify the code by aiming for one modern OS (Linux?) and Dockerizing it? And how about all that K&R C? Boy does that take me back... Can I shrink the binary by modernizing the code?

    $ perl2 -v This is perl 2, subversion 1 (v2.0.1) Copyright 1987-2019, Larry Wall Perl may be copied only under the terms of either the Artistic License or the GNU GPL (https://www.gnu.org/licenses/gpl.html). Documentation for Perl should be found on this system via "man perl". Point your browser at http://www.perl.org/, the Perl Home Page. Patch level: 0

    I needed GCC, make, and byacc (softlinked to 'yacc'). Then, I had to make a few edits:

    1. stab.c: commented out extern errno and replaced it with: int errno;

    2. perl.h: commented out the #ifdef that declared sprintf().

    3. perl.h: commented out the declaration of times().

    4. perly.c: I changed the -v message to look more Perl-like.

    I might have made two earlier edits, but they were along the same lines of removing conflicting or redundant declarations.

    And now, as a reward, I've got perl 2 running on Cygwin on my laptop! I have to say, it was worth the effort!

    Onward to hack!

    -rwxrwxr-x+ 1 rje None 387058 Mar 26 17:52 perl2.exe
Multiplication digit persistence
4 direct replies — Read more / Contribute
by tobyink
on Mar 21, 2019 at 09:17

    Try to find a number that takes more than eleven steps.

    use v5.10; use strict; use warnings; use List::Util qw(product); sub per { my ($n) = @_; return if $n < 10; my $p = product split //, $n; return $p, per($p); } my @steps = per 277777788888899; my $steps = @steps; say "$steps steps"; say for @steps;
Conversions of SI Units
1 direct reply — Read more / Contribute
by choroba
on Feb 16, 2019 at 16:57
    My son (11) has just learned SI units and their prefixes at school. They started just with metres and litres, only some of the prefixes (mili, centi, deci, hecto, kilo) and they don't know the floating point yet. Because of his broken arm, he missed several days at school and needed to practice. So, I've written a Tk application for him to practice. It takes one parameter, the number of formulas to generate.

    Feel free to localize it using the constants at the beginning. Adding the floating point left as an exercise for the reader.

    It's been a practice for me, too, because of Function::Parameters.

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
IPC::Msg Fork queue
1 direct reply — Read more / Contribute
by trippledubs
on Feb 01, 2019 at 09:11

    IPC::Msg as a fork safe queue. Couple caveats that make it less cool, the by-default maximum queue size seems to be very small. Configurable though by changing /proc/sys/kernel/msgmnb which is the max size in bytes of an individual queue up to 2^31 - 1 on my system at least. Check with  ipcs -l . You also have to delete the queue when finished. Or not if you want to use it later. You can also find the same queue in different scripts with ftok, not shown. You can also see queue sizes / number of messages with ipcs command, add flags to pull off different messages from the queue. You don't always have to do FIFO, you can add different kinds of messages to the same queue, and you can block or not block if you want to have multiple queues being worked at the same time.

    For complex data structures you would need to serialize or pack / unpack which other cpan modules may already address and better. Message queues are already documented in perlipc, but a little sugar show show versatile it can be. Basically add or remove from the queue with any child process not worrying about blocking / synchronization issues, but you can accidentally destroy the queue before it is empty, or leave an empty or not-empty queue hanging around that you would have to delete with icprm command.

    #!/usr/bin/env perl use strict; use warnings; use feature 'say'; use IPC::SysV qw/IPC_PRIVATE S_IRUSR S_IWUSR IPC_NOWAIT/; use IPC::Msg; $|++; use constant MSG_BYTES => 1024; my $q = IPC::Msg->new(IPC_PRIVATE,S_IRUSR|S_IWUSR); sub quit { $q->remove; } $SIG{INT} = \&quit; sub enqueue { my $msg = shift; my $msg_type = shift // 1; # Used for message selection, see msgsn +d(2) $q->snd($msg_type,$msg,IPC_NOWAIT) || die $!; } sub dequeue { my $type = shift // 1; # See msgop(2) my $msg; $q->rcv($msg,MSG_BYTES,$type,IPC_NOWAIT); #if ($!) { warn "$$ - $!" } return $msg // undef; } sub dequeue_block { my $msg; my $type = shift // 1; # See msgop(2) $q->rcv($msg,MSG_BYTES,$type); #if ($!) { warn "$$ - $!" } return $msg // undef; } sub isEmpty { my $stat = $q->stat; return $stat->qnum == 0; } sub isPrime { my $num = shift; return 1 if ($num < 4); return 0 if ($num %2 == 0); for (my $i=3; $i <= sqrt($num); $i+=2) { return 0 if $num % $i == 0; } return 1; } my $n_workers = shift // die ; enqueue($_) for (1..2_000_000); my $parent = $$; for (my $i=1; $i<$n_workers && $$ == $parent; $i++) { fork // die; } while (my $data = dequeue()) { if (isPrime($data)) { print "Prime: $data\n"; } } END { if ($$ == $parent) { 1 until wait == -1; quit; } }
    • Num Procs: Time(seconds)
    • 2^0 : 55.171
    • 2^1 : 35.073
    • 2^2 : 21.455
    • 2^3 : 15.121
    • 2^4 : 12.380
    • 2^5 : 11.080

    Update 2019-02-04 - You can't wait on grandchildren, changed code to only fork from parent. Fixed spelling of empty.

Exploring Type::Tiny Part 7: Creating a Type Library with Type::Library
No replies — Read more | Post response
by tobyink
on Jan 28, 2019 at 09:52

    Type::Tiny is probably best known as a way of having Moose-like type constraints in Moo, but it can be used for so much more. This is the seventh in a series of posts showing other things you can use Type::Tiny for. This article along with the earlier ones in the series can be found on my blog and in the Cool Uses for Perl section of PerlMonks.

    For small projects, the type constraints in Types::Standard and other CPAN type libraries are probably enough to satisfy your needs. You can do things like:

    use Types::Common::Numeric qw(PositiveInt); has user_id => ( is => 'ro', isa => PositiveInt, );

    However for larger apps, say you need to check user identity numbers in an handful of places throughout your code and you use PositiveInt everywhere, then if you ever feel the need to change the constraint for them, you'll need to hunt through your code to look for every use of PositiveInt, make sure it's not being used for some other reason (like to check an age or a counter), and update it.

    So it is helpful to make your own application-specific type library. You can define your own UserId type constraint, and use that everywhere. If the format of your identifiers ever changes, you only need to change the definition of the type constraint.

    Moose-Like Syntax

    package MyApp::Types { use Type::Library -base, -declare => qw( UserId UserIdList ); use Type::Utils -all; BEGIN { extends qw( Types::Standard Types::Common::Numeric Types::Common::String ); }; declare UserId, as PositiveInt, where { $_ > 1000 }; declare UserIdList, as ArrayRef[UserId]; ...; }

    Using -base from Type::Library sets your package up as an exporter that inherits from Type::Library. Using -declare allows the type constraints there to be written as barewords in the rest of the package. Importing from Type::Utils gives you a bunch of helpful keywords that can be useful for defining your type constraints. (These keywords will be pretty familiar to people who have defined their own type constraints in Moose or MooseX::Types, but personally I prefer not to use them. I'll show you how to write this type library without the keywords from Type::Utils later.)

    The extends statement imports all the type constraints from the given type libraries, so all those types are added to this library. Putting it in a BEGIN block allows them to be written as barewords too.

    And then we define a couple of type constraints. Hopefully that part is pretty self-explanatory. The declare, as, and where keywords are some of the things exported by Type::Utils.

    Now your application code can just do:

       use MyApp::Types qw( UserId UserIdList HashRef NonEmptyStr );

    Your type library is also the perfect place to define any application-wide type coercions. For example:

    declare User, as InstanceOf['MyApp::User']; coerce User, from UserId, via { MyApp::Utils::find_user_by_id($_) }; coerce UserId, from User, via { $_->user_id };

    Bare Bones Syntax

    Although Type::Tiny supports this Moose-like syntax for defining type constraints, I personally find the Type::Utils DSL a little unnecessary. Here's another way you can write the same type library:

    package MyApp::Types { use Type::Library -base; use Type::Utils (); # don't import any keywords BEGIN { # Type::Utils is still the easiest way to do this part! Type::Utils::extends(qw( Types::Standard Types::Common::Numeric Types::Common::String )); }; my $userid = __PACKAGE__->add_type({ name => 'UserId', parent => PositiveInt, constraint => '$_ > 1000', }); my $user = __PACKAGE__->add_type({ name => 'User', parent => InstanceOf['MyApp::User'], }); $userid->coercion->add_type_coercions( $user => '$_->user_id' ); $user->coercion->add_type_coercions( $userid => 'MyApp::Utils::find_user_by_id($_)', ); __PACKAGE__->add_type({ name => 'UserIdList', parent => ArrayRef[$userid], coercion => 1, }); ...; __PACKAGE__->make_immutable; }

    Defining types this way exposes some parts of Type::Tiny which are subtly different from Moose. For example, coercions and contraints can be expressed as strings of Perl code. This allows Type::Tiny to optimize some of the Perl code it generates, avoiding the overhead of a function call. Notice also the coerce => 1 when defining UserIdList. This allows UserIdList to inherit ArrayRef's automatic ability to coerce one level deep.

    Calling make_immutable on the package allows Type::Coercion to further optimize coercions for all the types in the library and prevents code outside the library from changing the global coercions you've defined.

    # Imagine this is some code in a class... # use MyApp::Types qw( UserId Str ); # This will die because UserId is immutable now. UserId->coercion->add_type_coercions(Str, sub { ... }); # This will work, and only affect this one attribute. has user_id => ( is => 'ro', isa => UserId->plus_coercions(Str, sub { ... }), coerce => 1, );

    So this method of defining type libraries might look a little less clean, but it has advantages. And as I said, it's how I prefer to do things.

    Defining Utility Functions

    All Type::Library-based type libraries automatically inherit from Exporter::Tiny and can also be used to define utility functions. Just define a normal Perl sub in the package and add:

       our @EXPORT_OK = qw( my_function_name );

    I recommend using lower-case function names with underscores to separate words to make them visually distinct from camel-case type constraint names.

    To avoid creating a confusing package with a mishmash of unrelated functions, this feature should probably only be used to export functions which are vaguely related to types — validation functions, coercion functions, etc.

Exploring Type::Tiny Part 6: Some Interesting Type Libraries
No replies — Read more | Post response
by tobyink
on Jan 20, 2019 at 08:40

    Type::Tiny is probably best known as a way of having Moose-like type constraints in Moo, but it can be used for so much more. This is the sixth in a series of posts showing other things you can use Type::Tiny for. This article along with the earlier ones in the series can be found on my blog and in the Cool Uses for Perl section of PerlMonks.

    While Types::Standard provides all the type constraints Moose users will be familiar with (and a few more) there are other type libraries you can use instead of or as well as Types::Standard.

    Types::Path::Tiny

    If your attribute or parameter needs to accept a file or directory name, I'd strongly recommend using Types::Path::Tiny. It provides Path, File, and Dir types, plus Abs* versions of them which coerce given filenames into absolute paths. The Path::Tiny objects it coerces strings into provide a bunch of helpful methods for manipulating files.

    package MyApp::Config { use Moo; use Types::Path::Tiny qw(AbsFile); use JSON::MaybeXS qw(decode_json); has config_file => ( is => 'ro', isa => AbsFile->where(q{ $_->basename =~ q/\.json$/ }), coerce => 1, ); sub get_hash { my $self = shift; decode_json( $self->config_file->slurp_utf8 ); } }

    Nice? Types::Path::Tiny is my personal favourite third-party type library. If you're writing an application that needs to deal with files, use it.

    Types::Common::String and Types::Common::Numeric

    Types::Common::String provides a bunch of type constraints more specific than the standard Str type. If you have indicated that an attribute or parameter should be a string, it's pretty rare that you really want to allow any string. You might want to constrain it more. This type library has types like NonEmptyStr and UpperCaseStr.

    Types::Common::Numeric does the same for numbers, giving you type constraints like PositiveInt and IntRange[1,10].

    Both of these libraries come bundled with Type::Tiny, so if you're already using Types::Standard, won't add any extra dependencies to your code.

    Types::TypeTiny

    This is a type library created for Type::Tiny's internal use and gives you types like ArrayLike, HashLike, and CodeLike which allow overloaded objects.

    Again it's bundled with Type::Tiny, so won't add any extra dependencies.

    Types::DateTime

    A type library for DateTime objects, allowing them to be coerced from strings.

    has start_date => ( is => 'ro', isa => DateTimeUTC, coerce => 1, );

    The above will not only coerce the attribute to a DateTime object, but coerce it to the correct timezone.

Streaming Market Quotes from Ally Invest
1 direct reply — Read more / Contribute
by Your Mother
on Jan 04, 2019 at 14:03

    This service requires you to have an account with Ally. Their investment API is new. I don't work for them or have any professional relationship but they are often mentioned as the best online bank in the US, I've been a customer since they were GMAC (20 years), and they really came through for me once so I have no reservations in recommending them and sharing some code to use their services.

    I had trouble getting streaming to work which was embarassing because it's supposed to be the part of Perl I'm good at. :P pmqs, haukex bliako, kschwab, and vr all stepped up to help and solved my trouble. My new trouble is a websocket client—a cat to skin later—which led me back to Mojolicious. I knew it had some server support so I figured the example code might show client code. I was pleasantly shocked to see it supports it completely via its own client Mojo::UserAgent. All hail sri!

    I was even happier to see the client supports gzip content, even in streams. Might be more confusing code for some but for me it was much easier to follow. I have hesitated to move some of my personal code/practices to Mojo but this is probably the shove I needed.

    Since the problem is now solved with both libraries, I figured I should share some of the code here. Both rely on the terrific WWW::OAuth. There is a fair bit of identical boilerplate for the arguments and environment.

    WWW::Mechanize (LWP::UserAgent)

    #!/usr/bin/env perl use 5.10.0; use strictures; use WWW::Mechanize; # LWP::UserAgent is almost the same here. use WWW::OAuth; use Compress::Zlib; my @symbols = grep /\A[A-Z.]+\z/, @ARGV; die "Give a list of symbols; e.g., AAPL GHII AMZN XOM DIS PBF BABA JD +AMD VOO\n" unless @symbols; my $sym = join ",", @symbols; die "Missing ENV values: ALLY_CLIENT_ID ALLY_CLIENT_SECRET ALLY_TOKEN +ALLY_TOKEN_SECRET\n" unless $ENV{ALLY_CLIENT_ID} and $ENV{ALLY_CLIENT_SECRET} and $ENV{ALLY_TOKEN} and $ENV{ALLY_TOKEN_SECRET}; my $oauth = WWW::OAuth->new( client_id => $ENV{ALLY_CLIENT_ID}, client_secret => $ENV{ALLY_CLIENT_SECRET}, token => $ENV{ALLY_TOKEN}, token_secret => $ENV{ALLY_TOKEN_SECRET} ); my $mech = WWW::Mechanize->new( autocheck => undef ); $mech->add_handler( request_prepare => sub { $oauth->authenticate($_[0 +]) } ); my $gunzip = inflateInit( WindowBits => 16 + MAX_WBITS ) or die "Cannot create a inflation stream\n"; $mech->add_handler ( response_data => sub { my ( $response, $ua, $h, $data ) = @_; $response->content(undef); # Else will append. my ( $buffer, $status ) = $gunzip->inflate($data); die "zlib error: $status" if length $status; say $buffer; }); $mech->get("https://stream.tradeking.com/v1/market/quotes?symbols=$sym +"); __END__

    Mojo::UserAgent

    #!/usr/bin/env perl use 5.10.0; use strictures; use Mojo::UserAgent; use WWW::OAuth; my @symbols = grep /\A[A-Z.]+\z/, @ARGV; die "Give a list of symbols; e.g., AAPL GHII AMZN XOM DIS PBF BABA JD +AMD VOO\n" unless @symbols; my $sym = join ",", @symbols; die "Missing ENV values: ALLY_CLIENT_ID ALLY_CLIENT_SECRET ALLY_TOKEN +ALLY_TOKEN_SECRET\n" unless $ENV{ALLY_CLIENT_ID} and $ENV{ALLY_CLIENT_SECRET} and $ENV{ALLY_TOKEN} and $ENV{ALLY_TOKEN_SECRET}; my $oauth = WWW::OAuth->new( client_id => $ENV{ALLY_CLIENT_ID}, client_secret => $ENV{ALLY_CLIENT_SECRET}, token => $ENV{ALLY_TOKEN}, token_secret => $ENV{ALLY_TOKEN_SECRET} ); my $ua = Mojo::UserAgent->new( max_response_size => 0 ); # Stream mean +s no max. $ua->on( start => sub { $oauth->authenticate( $_[1]->req ) } ); # OAut +h all requests. my $tx = $ua->build_tx( GET => "https://stream.tradeking.com/v1/market +/quotes?symbols=$sym" ); $tx->res->content ->unsubscribe("read") ->on( read => sub { my ( $content, $bytes ) = @_; say $bytes; }); $tx = $ua->start($tx); __END__
Encrypting Source Filter
No replies — Read more | Post response
by kschwab
on Dec 28, 2018 at 14:48

    After reading bliako's post, I was curious.

    I wanted to experiment a bit with his "I want to be asked for a password/key at encryption stage and then asked just once" requirement. I didn't get it working with PAR, or any ability to also encrypt used modules, so it's not acceptable as an answer there. Might be useful to play with though. Works in a way that's similar to Acme::Bleach, other than I don't overwrite the original file. You'll need to have a working openssl binary in your PATH somewhere.

    Feedback welcome.

    Save this as "AESFilter.pm"...
    package AESFilter; use IPC::Open2; our $openssl="openssl enc -aes-256-cbc -a"; our $marker = '#AESFilter'; sub encrypt { $_[0]=~s/$marker//gs; my $pid=open2(my $rdr,my $wrt,"$openssl 2>>/dev/null"); print $wrt $_[0]; close $wrt; my $output; while(<$rdr>) {$output.=$_}; close $rdr; waitpid($pid,0); my $status=$?>>8; if ($status !=0) { die("Exit status $status from openssl, encryption failed\n"); } return $output; } sub decrypt { my $pid=open2(my $rdr,my $wrt,"$openssl -d 2>>/dev/null"); print $wrt $_[0]."\n"; close $wrt; my $output; while(<$rdr>) {$output.=$_}; close $rdr; waitpid($pid,0); my $status=$?>>8; if ($status != 0) { die("Exit status $status from openssl, decryption failed\n"); } return $output; } open(IN,$0) or die "Can't open [$0]: $!\n"; my $prior=''; my $code=''; my $seen=0; while(<IN>) { if ($seen) { chomp; $code .= $_; next; } $prior .= $_; if (/use AESFilter;/) { $seen=1} } close IN; if ($code =~ s/^$marker//gm) { my $clear=decrypt($code); eval($prior.$clear); print STDERR $@ if $@; exit; } my $outfile=$0.".enc"; die "Encrypted file [$outfile] already exists\n" if (-e $outfile); my $encrypted=encrypt($code); open(OUT,">$outfile") or die "Can't open [$outfile] for write: $!\n"; printf OUT "%s%s\n%s",$prior,$marker,$encrypted; close OUT; exit; 1;

    To play with it, create a script like what's below. The first time you run it, it will create an encrypted script, with an extension of ".enc". So, if your script is called "foo", it creates a new file called "foo.enc" that's encrypted. It's calling openssl to get a password, so you'll be prompted for a password.

    #!/usr/bin/perl # so that you don't have to install AESFilter.pm, just # have it in your current dir use lib "."; # anything before the next line will be in the output in cleartext # ...anything after will be encrypted use AESFilter; print "test123\n"; print "again\n"; for ("one","two","three","four") { print $_."\n"; }
    If you save the code in a file called "foo", and run it once (with a password of '0'), it will produce a file called "foo.enc", that looks like this:
    #!/usr/bin/perl # so that you don't have to install AESFilter.pm, just # have it in your current dir use lib "."; # anything before the next line will be in the output in cleartext # anything after will be encrypted use AESFilter; #AESFilter U2FsdGVkX1/CjxWDKOh4Xdw/7c0PoKnkUFQsf5gxo3F7RXqcEtmdsAgeEmb1g/QO qd82hklpUxP/SNzbs34Z2NdzEStaDpeTlke1unf18gAw/2hlu78CIIItHVuAZlrH ovJhqCBhP0Rck1RwXt3cJw==
    And, if you run that code, it will prompt for the password.

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


  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.
  • Log In?
    Username:
    Password:

    What's my password?
    Create A New User
    Chatterbox?
    and the web crawler heard nothing...

    How do I use this? | Other CB clients
    Other Users?
    Others examining the Monastery: (6)
    As of 2019-08-23 17:54 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?

      No recent polls found

      Notices?