Data-Munge-0.111/0000755000175000017500000000000014643047132012430 5ustar maukemaukeData-Munge-0.111/Makefile_PL_settings.plx0000644000175000017500000000071014603507170017202 0ustar maukemaukeuse strict; use warnings; return { NAME => 'Data::Munge', AUTHOR => q{Lukas Mai }, CONFIGURE_REQUIRES => {}, BUILD_REQUIRES => {}, TEST_REQUIRES => { 'Test2::V0' => 0, }, PREREQ_PM => { 'strict' => 0, 'warnings' => 0, }, DEVELOP_REQUIRES => { 'Test::Pod' => 1.22, }, REPOSITORY => [ github => 'mauke' ], }; Data-Munge-0.111/Makefile.PL0000644000175000017500000001362614603507014014406 0ustar maukemaukeuse strict; use warnings; use ExtUtils::MakeMaker; use File::Spec (); use File::Find (); sub MY::postamble { my ($self, %args) = @_; if ((my $harness_options = delete $args{HARNESS_OPTIONS}) && $self->can('is_make_type')) { my $value = join ':', @$harness_options; if ($self->is_make_type('gmake')) { $args{text} .= "export HARNESS_OPTIONS := $value\n"; } elsif ($self->is_make_type('nmake')) { $args{text} .= "!if [set HARNESS_OPTIONS=$value]\n!endif\n"; } } $args{text} || '' } sub find_tests_recursively_in { my ($dir) = @_; -d $dir or die "$dir is not a directory"; my %seen; my $wanted = sub { /\.t\z/ or return; my $directories = (File::Spec->splitpath($File::Find::name))[1]; my $depth = grep $_ ne '', File::Spec->splitdir($directories); $seen{$depth} = 1; }; File::Find::find($wanted, $dir); join ' ', map { $dir . '/*' x $_ . '.t' } sort { $a <=> $b } keys %seen } $::MAINT_MODE = !-f 'META.yml'; my $settings_file = 'Makefile_PL_settings.plx'; my %settings = %{do "./$settings_file" or die "Internal error: can't do $settings_file: ", $@ || $!}; (do './maint/eumm-fixup.pl' || die $@ || $!)->(\%settings) if $::MAINT_MODE; { $settings{depend}{Makefile} .= " $settings_file"; $settings{LICENSE} ||= 'perl'; $settings{PL_FILES} ||= {}; $settings{CONFIGURE_REQUIRES}{strict} ||= 0; $settings{CONFIGURE_REQUIRES}{warnings} ||= 0; $settings{CONFIGURE_REQUIRES}{'File::Find'} ||= 0; $settings{CONFIGURE_REQUIRES}{'File::Spec'} ||= 0; for ($settings{CONFIGURE_REQUIRES}{'ExtUtils::MakeMaker'}) { $_ = '7.0' if !$_ || $_ < 7; } my $module_file = $settings{NAME}; $module_file =~ s!::!/!g; $module_file = "lib/$module_file.pm"; $settings{VERSION_FROM} ||= $module_file; $settings{ABSTRACT_FROM} ||= $module_file; $settings{test}{TESTS} ||= do { my $extra_test_dirs = delete $settings{EXTRA_TEST_DIRS}; join ' ', map find_tests_recursively_in($_), 't', @{$extra_test_dirs || []} }; $settings{DISTNAME} ||= do { my $name = $settings{NAME}; $name =~ s!::!-!g; $name }; $settings{clean}{FILES} ||= "$settings{DISTNAME}-*"; $settings{dist}{COMPRESS} ||= 'gzip -9f'; $settings{dist}{SUFFIX} ||= '.gz'; my $version = $settings{VERSION} || MM->parse_version($settings{VERSION_FROM}); if ($version =~ s/-TRIAL[0-9]*\z//) { $settings{META_MERGE}{release_status} ||= 'unstable'; $settings{META_MERGE}{version} ||= $version; $settings{XS_VERSION} ||= $version; } $settings{META_MERGE}{'meta-spec'}{version} ||= 2; $settings{META_MERGE}{dynamic_config} ||= 0; push @{$settings{META_MERGE}{no_index}{directory}}, 'xt'; if (my $dev = delete $settings{DEVELOP_REQUIRES}) { @{$settings{META_MERGE}{prereqs}{develop}{requires}}{keys %$dev} = values %$dev; } if (my $rec = delete $settings{RECOMMENDS}) { @{$settings{META_MERGE}{prereqs}{runtime}{recommends}}{keys %$rec} = values %$rec; } if (my $sug = delete $settings{SUGGESTS}) { @{$settings{META_MERGE}{prereqs}{runtime}{suggests}}{keys %$sug} = values %$sug; } if (my $repo = delete $settings{REPOSITORY}) { if (ref($repo) eq 'ARRAY') { my ($type, @args) = @$repo; if ($type eq 'github') { my ($account, $project) = @args; $project ||= '%d'; $project =~ s{%(L?)(.)}{ my $x = $2 eq '%' ? '%' : $2 eq 'd' ? $settings{DISTNAME} : $2 eq 'm' ? $settings{NAME} : die "Internal error: unknown placeholder %$1$2"; $1 ? lc($x) : $x }seg; my $addr = "github.com/$account/$project"; $repo = { type => 'git', url => "git://$addr", web => "https://$addr", }; } else { die "Internal error: unknown REPOSITORY type '$type'"; } } ref($repo) eq 'HASH' or die "Internal error: REPOSITORY must be a hashref, not $repo"; @{$settings{META_MERGE}{resources}{repository}}{keys %$repo} = values %$repo; } if (my $harness_options = delete $settings{HARNESS_OPTIONS}) { $settings{postamble}{HARNESS_OPTIONS} = $harness_options; } } (my $mm_version = ExtUtils::MakeMaker->VERSION) =~ tr/_//d; if ($mm_version < 6.63_03) { $settings{META_MERGE}{resources}{repository} = $settings{META_MERGE}{resources}{repository}{url} if $settings{META_MERGE}{resources} && $settings{META_MERGE}{resources}{repository} && $settings{META_MERGE}{resources}{repository}{url}; delete $settings{META_MERGE}{'meta-spec'}{version}; } elsif ($mm_version < 6.67_04) { # Why? For the glory of satan, of course! no warnings qw(redefine); *ExtUtils::MM_Any::_add_requirements_to_meta_v1_4 = \&ExtUtils::MM_Any::_add_requirements_to_meta_v2; } { my $merge_key_into = sub { my ($target, $source) = @_; %{$settings{$target}} = (%{$settings{$target}}, %{delete $settings{$source}}); }; $merge_key_into->('BUILD_REQUIRES', 'TEST_REQUIRES') if $mm_version < 6.63_03; $merge_key_into->('CONFIGURE_REQUIRES', 'BUILD_REQUIRES') if $mm_version < 6.55_01; $merge_key_into->('PREREQ_PM', 'CONFIGURE_REQUIRES') if $mm_version < 6.51_03; } delete $settings{MIN_PERL_VERSION} if $mm_version < 6.47_01; delete $settings{META_MERGE} if $mm_version < 6.46; delete $settings{LICENSE} if $mm_version < 6.30_01; delete $settings{ABSTRACT_FROM} if $mm_version < 6.06_03; delete $settings{AUTHOR} if $mm_version < 6.06_03; WriteMakefile %settings; Data-Munge-0.111/META.json0000664000175000017500000000271414643047132014057 0ustar maukemauke{ "abstract" : "various utility functions", "author" : [ "Lukas Mai " ], "dynamic_config" : 0, "generated_by" : "ExtUtils::MakeMaker version 7.70, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Data-Munge", "no_index" : { "directory" : [ "t", "inc", "xt" ] }, "prereqs" : { "build" : { "requires" : {} }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "7.0", "File::Find" : "0", "File::Spec" : "0", "strict" : "0", "warnings" : "0" } }, "develop" : { "requires" : { "Pod::Markdown" : "3.005", "Pod::Text" : "4.09", "Test::Pod" : "1.22" } }, "runtime" : { "requires" : { "strict" : "0", "warnings" : "0" } }, "test" : { "requires" : { "Test2::V0" : "0" } } }, "release_status" : "stable", "resources" : { "repository" : { "type" : "git", "url" : "git://github.com/mauke/Data-Munge", "web" : "https://github.com/mauke/Data-Munge" } }, "version" : "0.111", "x_serialization_backend" : "JSON::PP version 4.16" } Data-Munge-0.111/t/0000755000175000017500000000000014643047132012673 5ustar maukemaukeData-Munge-0.111/t/is_callable.t0000644000175000017500000000254414643043774015327 0ustar maukemauke#!perl use Test2::V0; use Data::Munge; { package OverloadedFuncall; use overload ( '&{}' => sub { $_[0]->to_coderef }, fallback => 1, ); sub new { my $class = shift; bless {@_}, $class } sub to_coderef { my $self = shift; sub { $self->{value} } } } ok !is_callable(undef), "undef is not callable"; ok !is_callable(42), "a number is not callable"; ok !is_callable(\$_), "a scalar ref is not callable"; ok !is_callable([]), "an arrayref is not callable"; ok !is_callable({}), "a hashref is not callable"; ok !is_callable(*is_callable), "a glob is not callable"; ok !is_callable(\*is_callable), "a globref is not callable"; ok !is_callable(qr/./), "a regex is not callable"; ok !is_callable("a random string"), "a random string is not callable"; ok !is_callable("main::is_callable"), "a sub name is not callable"; ok is_callable(sub { die; }), "an anon sub ref is callable"; ok is_callable(\&is_callable), "a named sub ref is callable"; ok is_callable(\&no_such_sub), "an undefined sub is deemed callable"; ok is_callable(bless sub {}, 'SomeRandomClass'), "a blessed sub is callable"; { my $obj = OverloadedFuncall->new(value => 42); is $obj->(), 42, "sanity check: overloaded function call returns 42"; ok is_callable($obj), "an object with overloaded &{} is callable"; } done_testing; Data-Munge-0.111/t/torgox.t0000644000175000017500000000125014406264317014403 0ustar maukemauke#!perl use Test2::V0; use Data::Munge qw(list2re replace); my $orig = '[acabbdcacab]'; my $re1 = list2re 'a', 'b'; my $good1 = '[XYcXYXYXYdcXYcXYXY]'; is replace($orig, $re1, 'XY', 'g'), $good1; $_ = $orig; s/$re1/XY/g; is $_, $good1; my $re2 = list2re 'a'; my $good2 = '[XYcXYbbdcXYcXYb]'; is replace($orig, $re2, 'XY', 'g'), $good2; $_ = $orig; s/$re2/XY/g; is $_, $good2; my $re3 = list2re; my $good3 = $orig; is replace($orig, $re3, 'XY', 'g'), $good3; $_ = $orig; s/$re3/XY/g; is $_, $good3; my $re4 = list2re ''; my $good4 = 'XY[XYaXYcXYaXYbXYbXYdXYcXYaXYcXYaXYbXY]XY'; is replace($orig, $re4, 'XY', 'g'), $good4; $_ = $orig; s/$re4/XY/g; is $_, $good4; done_testing; Data-Munge-0.111/t/eval.t0000644000175000017500000000054314406264246014015 0ustar maukemaukeuse Test2::V0; use Data::Munge qw(eval_string); { $@ = 'xyzzy 1'; eval_string '$main::E = $@'; is $@, 'xyzzy 1'; is $main::E, 'xyzzy 1'; } { eval { $@ = 'xyzzy 2'; eval_string '$main::E = $@; die "fiddlesticks\\n"'; fail 'wtf'; }; is $@, "fiddlesticks\n"; is $main::E, 'xyzzy 2'; } done_testing; Data-Munge-0.111/t/01-compile.t0000644000175000017500000000374314643042457014742 0ustar maukemauke#!perl use Test2::V0; use Data::Munge; { my $str = "abc|bar|baz|foo|\\*\\*|ab|\\!|\\*|a"; is list2re(qw[! a abc ab foo bar baz ** *]), qr/$str/, 'list2re'; } is +(byval { s/foo/bar/ } 'foo-foo'), 'bar-foo', 'byval'; is [mapval { tr[a-d][1-4] } 'foo', 'bar', 'baz'], [qw[foo 21r 21z]], 'mapval'; is replace('Apples are round, and apples are juicy.', qr/apples/i, 'oranges', 'g'), 'oranges are round, and oranges are juicy.', 'replace g'; is replace('John Smith', qr/(\w+)\s+(\w+)/, '$2, $1'), 'Smith, John', 'replace'; is replace('97653 foo bar 42', qr/(\d)(\d)/, sub { $_[1] + $_[2] }, 'g'), '16113 foo bar 6', 'replace fun g'; "foo bar" =~ /(\w+) (\w+)/ or die; is [submatches], [qw(foo bar)]; "" =~ /^/ or die; is [submatches], []; is trim(" a b "), "a b"; is trim(""), ""; is trim(","), ","; is trim(" "), ""; is trim(" "), ""; is trim("\na"), "a"; is trim("b\t"), "b"; is trim("X\nY \n "), "X\nY"; is trim(undef), undef; { my $fac = rec { my ($rec, $n) = @_; $n < 2 ? 1 : $n * $rec->($n - 1) }; is $fac->(5), 120; is $fac->(6), 720; } is eval_string('"ab" . "cd"'), 'abcd'; like dies { eval_string('{') }, qr/Missing right curly/; like dies { eval_string '$VERSION' }, qr/Global symbol "\$VERSION"/; ok !elem 42, []; ok elem 42, [42]; ok elem "A", [undef, [], "A", "B"]; ok elem "B", [undef, [], "A", "B"]; ok elem undef, [undef, [], "A", "B"]; ok !elem [], [undef, [], "A", "B"]; ok !elem "C", [undef, [], "A", "B"]; for my $ref ([], {}, sub {}) { ok !elem $ref, []; ok !elem $ref, [undef]; ok !elem $ref, ["$ref"]; ok !elem "$ref", [$ref]; ok !elem $ref, [[], {}]; ok elem $ref, [$ref]; ok elem $ref, ["A", "B", $ref]; ok elem $ref, ["A", $ref, "B"]; ok elem $ref, [$ref, "A", $ref, $ref]; ok elem $ref, [undef, $ref]; } my $source = slurp \*DATA; like $source, qr/\AThis is the beginning\.\n/; like $source, qr/\nThis is the end\.\Z/; done_testing; __DATA__ This is the beginning. stuff etc. This is the end. Data-Munge-0.111/Changes0000644000175000017500000000366214643047123013732 0ustar maukemaukeRevision history for Data-Munge 0.111 2024-07-08 * Add is_callable. 0.101 2024-04-04 * Work around a warning in the development version of perl (thanks, Shlomi Fish). [RT#152677] 0.10 2023-03-21 * Drop dependency on 'base' and 'Test::More'; switch to 'Test2::V0' instead. * Update links in documentation. * Autogenerate README from POD. 0.097 2017-03-17 * Don't require '.' in @INC during install. 0.096 2016-03-02 * eval_string() now preserves the value of $@ both inside the eval'd code and outside (if no exception is thrown). 0.095 2015-05-08 * Extend documentation for slurp, submatches, eval_string. 0.094 2015-03-08 * elem("$ref", [$ref]) is now false. (elem($ref, ["$ref"]) had been false before.) 0.093 2014-12-23 * Fix typo in synopsis (thanks, Ivan Wills). 0.092 2014-11-25 * Work around some parser bug in perl 5.6. 0.091 2014-11-19 * Work around regex bug (#115242) in perls < 5.18 that causes spurious test failures. 0.09 2014-11-18 * Add slurp. * Don't leave $VERSION and @EXPORT in scope for eval_string. 0.08 2014-09-15 * Make trim(undef) return undef without warnings. 0.07 2013-10-22 * eval_string() now sets __FILE__ and __LINE__ for the eval'd code. This may or may not make error locations more useful. 0.06 2013-03-07 * add elem 0.05 2013-03-05 * add trim, eval_string, and rec 0.04 2011-08-04 * Fix buggy behavior for list2re('') and list2re() 0.032 2010-01-23 * Fix overly strict test that fails on < 5.10 0.031 2010-01-20 * Also remove Defaults::Mauke from tests (sigh). 0.03 2010-01-19 * Dropped dependency on Defaults::Mauke. * Added submatches/replace. 0.02 2009-12-14 First version, released on an unsuspecting world. Data-Munge-0.111/MANIFEST.SKIP0000644000175000017500000000024014603515731014323 0ustar maukemauke(?' build_requires: Test2::V0: '0' configure_requires: ExtUtils::MakeMaker: '7.0' File::Find: '0' File::Spec: '0' strict: '0' warnings: '0' dynamic_config: 0 generated_by: 'ExtUtils::MakeMaker version 7.70, CPAN::Meta::Converter version 2.150010' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Data-Munge no_index: directory: - t - inc - xt requires: strict: '0' warnings: '0' resources: repository: git://github.com/mauke/Data-Munge version: '0.111' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' Data-Munge-0.111/xt/0000755000175000017500000000000014643047132013063 5ustar maukemaukeData-Munge-0.111/xt/pod.t0000644000175000017500000000011314406257653014035 0ustar maukemauke#!perl use strict; use warnings; use Test::Pod 1.22; all_pod_files_ok(); Data-Munge-0.111/MANIFEST0000644000175000017500000000064114643047132013562 0ustar maukemaukeChanges lib/Data/Munge.pm Makefile.PL Makefile_PL_settings.plx MANIFEST MANIFEST.SKIP t/01-compile.t t/eval.t t/is_callable.t t/torgox.t xt/pod.t META.yml Module YAML meta-data (added by MakeMaker) META.json Module JSON meta-data (added by MakeMaker) README generated from Data::Munge POD (added by maint/eumm-fixup.pl) Data-Munge-0.111/README0000664000175000017500000000063014643047132013311 0ustar maukemaukeNAME Data::Munge - various utility functions COPYRIGHT & LICENSE Copyright 2009-2011, 2013-2015, 2023-2024 Lukas Mai. This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License. See for more information. Data-Munge-0.111/lib/0000755000175000017500000000000014643047132013176 5ustar maukemaukeData-Munge-0.111/lib/Data/0000755000175000017500000000000014643047132014047 5ustar maukemaukeData-Munge-0.111/lib/Data/Munge.pm0000644000175000017500000002641214643047021015462 0ustar maukemaukepackage Data::Munge; use strict; use warnings; use Exporter 5.57 qw(import); sub _eval { eval $_[0] } # empty lexical scope our $VERSION = '0.111'; our @EXPORT = qw( byval elem eval_string is_callable list2re mapval rec replace slurp submatches trim ); sub byval (&$) { my ($f, $x) = @_; local *_ = \$x; $f->($_); $x } sub elem { my ($k, $xs) = @_; if (ref $k) { for my $x (@$xs) { return 1 if ref $x && $k == $x; } } elsif (defined $k) { for my $x (@$xs) { return 1 if defined $x && !ref $x && $k eq $x; } } else { for my $x (@$xs) { return 1 if !defined $x; } } !1 } sub eval_string { my ($code) = @_; my ($package, $file, $line) = caller; local $Data::Munge::_err = $@; $code = qq{\$\@ = \$Data::Munge::_err; package $package; # eval_string()\n#line $line "$file"\n$code}; my @r = wantarray ? _eval $code : scalar _eval $code; die $@ if $@; $@ = $Data::Munge::_err; wantarray ? @r : $r[0] } sub list2re { @_ or return qr/(?!)/; my $re = join '|', map quotemeta, sort {length $b <=> length $a || $a cmp $b } @_; $re eq '' and $re = '(?#)'; qr/$re/ } sub mapval (&@) { my $f = shift; my @xs = @_; map { $f->($_); $_ } @xs } if ($^V ge v5.16.0) { eval_string <<'EOT'; sub rec (&) { my ($f) = @_; sub { $f->(CORE::__SUB__, @_) } } EOT } elsif (eval { require Scalar::Util } && defined &Scalar::Util::weaken) { *rec = sub (&) { my ($f) = @_; my $w; my $r = $w = sub { $f->($w, @_) }; Scalar::Util::weaken($w); $r }; } else { # slow but always works *rec = sub (&) { my ($f) = @_; sub { $f->(&rec($f), @_) } }; } sub replace { my ($str, $re, $x, $g) = @_; my $f = ref $x ? $x : sub { my $r = $x; $r =~ s{\$([\$&`'0-9]|\{([0-9]+)\})}{ $+ eq '$' ? '$' : $+ eq '&' ? $_[0] : $+ eq '`' ? substr($_[-1], 0, $_[-2]) : $+ eq "'" ? substr($_[-1], $_[-2] + length $_[0]) : $_[$+] }eg; $r }; if ($g) { $str =~ s{$re}{ $f->(substr($str, $-[0], $+[0] - $-[0]), submatches(), $-[0], $str) }eg; } else { $str =~ s{$re}{ $f->(substr($str, $-[0], $+[0] - $-[0]), submatches(), $-[0], $str) }e; } $str } sub slurp { local $/; scalar readline $_[0] } sub submatches { no strict 'refs'; map $$_, 1 .. $#+ } sub trim { my ($s) = @_; return undef if !defined $s; $s =~ s/^\s+//; $s =~ s/\s+\z//; $s } sub is_callable { my ($f) = @_; require Scalar::Util; my $t = Scalar::Util::reftype($f); defined $t && ($t eq 'CODE' || (require overload, overload::Method($f, '&{}'))) } 'ok' __END__ =encoding utf8 =for github-markdown [![Coverage Status](https://coveralls.io/repos/github/mauke/Data-Munge/badge.svg?branch=main)](https://coveralls.io/github/mauke/Data-Munge?branch=main) =head1 NAME Data::Munge - various utility functions =head1 SYNOPSIS =for highlighter language=perl use Data::Munge; my $re = list2re qw/f ba foo bar baz/; # $re = qr/bar|baz|foo|ba|f/; print byval { s/foo/bar/ } $text; # print do { my $tmp = $text; $tmp =~ s/foo/bar/; $tmp }; foo(mapval { chomp } @lines); # foo(map { my $tmp = $_; chomp $tmp; $tmp } @lines); print replace('Apples are round, and apples are juicy.', qr/apples/i, 'oranges', 'g'); # "oranges are round, and oranges are juicy." print replace('John Smith', qr/(\w+)\s+(\w+)/, '$2, $1'); # "Smith, John" my $trimmed = trim " a b c "; # "a b c" my $x = 'bar'; if (elem $x, [qw(foo bar baz)]) { ... } # executes: $x is an element of the arrayref my $contents = slurp $fh; # or: slurp *STDIN # reads all data from a filehandle into a scalar my $t = is_callable(sub {}); # true my $f = is_callable("hello"); # false eval_string('print "hello world\\n"'); # says hello eval_string('die'); # dies eval_string('{'); # throws a syntax error my $fac = rec { my ($rec, $n) = @_; $n < 2 ? 1 : $n * $rec->($n - 1) }; print $fac->(5); # 120 if ("hello, world!" =~ /(\w+), (\w+)/) { my @captured = submatches; # @captured = ("hello", "world") } =head1 DESCRIPTION This module defines a few generally useful utility functions. I got tired of redefining or working around them, so I wrote this module. =head2 Functions =over =item list2re LIST Converts a list of strings to a regex that matches any of the strings. Especially useful in combination with C. Example: my $re = list2re keys %hash; $str =~ s/($re)/$hash{$1}/g; This function takes special care to get several edge cases right: =over =item * Empty list: An empty argument list results in a regex that doesn't match anything. =item * Empty string: An argument list consisting of a single empty string results in a regex that matches the empty string (and nothing else). =item * Prefixes: The input strings are sorted by descending length to ensure longer matches are tried before shorter matches. Otherwise C would generate C, which (on its own) can never match C (because C is tried first, and it always succeeds where C could). =back =item byval BLOCK SCALAR Takes a code block and a value, runs the block with C<$_> set to that value, and returns the final value of C<$_>. The global value of C<$_> is not affected. C<$_> isn't aliased to the input value either, so modifying C<$_> in the block will not affect the passed in value. Example: foo(byval { s/!/?/g } $str); # Calls foo() with the value of $str, but all '!' have been replaced by '?'. # $str itself is not modified. Since perl 5.14 you can also use the C flag: foo($str =~ s/!/?/gr); But C works on all versions of perl and is not limited to C. =item mapval BLOCK LIST Works like a combination of C and C; i.e. it behaves like C, but C<$_> is a copy, not aliased to the current element, and the return value is taken from C<$_> again (it ignores the value returned by the block). Example: my @foo = mapval { chomp } @bar; # @foo contains a copy of @bar where all elements have been chomp'd. # This could also be written as chomp(my @foo = @bar); but that's not # always possible. =item submatches Returns a list of the strings captured by the last successful pattern match. Normally you don't need this function because this is exactly what C returns in list context. However, C also works in other contexts such as the RHS of C. =item replace STRING, REGEX, REPLACEMENT, FLAG =item replace STRING, REGEX, REPLACEMENT A clone of javascript's C. It works almost the same as C, but with a few important differences. REGEX can be a string or a compiled C object. REPLACEMENT can be a string or a subroutine reference. If it's a string, it can contain the following replacement patterns: =over =item C<$$> Inserts a '$'. =item C<$&> Inserts the matched substring. =item C<$`> Inserts the substring preceding the match. =item C<$'> Inserts the substring following the match. =item C<$N> (where I is a digit) Inserts the substring matched by the Ith capturing group. =item C<${N}> (where I is one or more digits) Inserts the substring matched by the Ith capturing group. =back Note that these aren't variables; they're character sequences interpreted by C. If REPLACEMENT is a subroutine reference, it's called with the following arguments: First the matched substring (like C<$&> above), then the contents of the capture buffers (as returned by C), then the offset where the pattern matched (like C<$-[0]>, see L), then the STRING. The return value will be inserted in place of the matched substring. Normally only the first occurrence of REGEX is replaced. If FLAG is present, it must be C<'g'> and causes all occurrences to be replaced. =item trim STRING Returns I with all leading and trailing whitespace removed. Like L|perlfunc/length-EXPR> it returns C if the input is C. =item elem SCALAR, ARRAYREF Returns a boolean value telling you whether I is an element of I or not. Two scalars are considered equal if they're both C, if they're both references to the same thing, or if they're both not references and C to each other. This is implemented as a linear search through I that terminates early if a match is found (i.e. C won't even look at elements C<1 .. 9999>). =item is_callable SCALAR Returns a boolean value telling you whether I can be called as a function. This is the case if I is either a reference to a subroutine (such as C<\&foo> or C) or an object with an overloaded C<'&{}'> operator (see L">). =item eval_string STRING Evals I just like C but doesn't catch exceptions. Caveat: Unlike with C the code runs in an empty lexical scope: my $foo = "Hello, world!\n"; eval_string 'print $foo'; # Dies: Global symbol "$foo" requires explicit package name That is, the eval'd code can't see variables from the scope of the C call. =item slurp FILEHANDLE Reads and returns all remaining data from I as a string, or C if it hits end-of-file. (Interaction with non-blocking filehandles is currently not well defined.) C is equivalent to C. =item rec BLOCK Creates an anonymous sub as C would, but supplies the called sub with an extra argument that can be used to recurse: my $code = rec { my ($rec, $n) = @_; $rec->($n - 1) if $n > 0; print $n, "\n"; }; $code->(4); That is, when the sub is called, an implicit first argument is passed in C<$_[0]> (all normal arguments are moved one up). This first argument is a reference to the sub itself. This reference could be used to recurse directly or to register the sub as a handler in an event system, for example. A note on defining recursive anonymous functions: Doing this right is more complicated than it may at first appear. The most straightforward solution using a lexical variable and a closure leaks memory because it creates a reference cycle. Starting with perl 5.16 there is a C<__SUB__> constant that is equivalent to C<$rec> above, and this is indeed what this module uses (if available). However, this module works even on older perls by falling back to either weak references (if available) or a "fake recursion" scheme that dynamically instantiates a new sub for each call instead of creating a cycle. This last resort is slower than weak references but works everywhere. =back =head1 AUTHOR Lukas Mai, C<< >> =head1 COPYRIGHT & LICENSE Copyright 2009-2011, 2013-2015, 2023-2024 Lukas Mai. This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License. See L for more information. =cut