‎Was hat join mit map zu tun? Welche Freunde hat \Q?

2012 hatte ich einen Vortrag ausgearbeitet, der hieß "Vom Spaghetti-Code zur wartbaren Software". Ich hatte viel zu viel Material gesammelt. Den 2. Teil der Sammlung beinhaltet dieser Vortrag. Und einige neu gesammelte Dinge sind natürlich auch dabei. ‎‎

Boolean ist etwas Besonderes (1/5)

   perl -e 'use Devel::Peek; Dump 1 == 1'

und

   perl -e 'use Devel::Peek; Dump 0 == 1'

Devel::Peek schaut in den Bauch einer Perl-Datenstruktur.

Boolean ist etwas Besonderes (2/5)

SV - Scalar value
Flags
 IOK      -> int gültig
 NOK      -> numeric gültig
 POK      -> pointer auf String gültig
 READONLY -> 0 == 1 = 1 geht eben nicht zu schreiben
 IV = 1   -> int ist 1
 NV = 1   -> numeric ist 1
 PV       -> es liegt ein string auf Adresse 0x...
             mit dem Wert "1" mit \0 abgeschlossen
CUR = 1   -> Der string hat eine Länge von 1
LEN = 1   -> Die gesamte Datenstruktur hat eine
             Länge von 8 Bytes

Boolean ist etwas Besonderes (3/5)

Für viele, die aus anderen Programmiersprachen kommen, ist schon mal völlig unklar, wie ein Scalar gleichzeitig mehrere gültige Werte (String, Numeric, Integer) beinhalten kann.

"https://metacpan.org/module/Scalar::MultiValue" ist ein Modul, mit dem man das aktiv machen kann.

Boolean ist etwas Besonderes (4/5)

|| && and or

macht das nicht. Hier wird der rechte Teil der Anweisung nicht boolsch evaluiert.

Deswegen ergibt

50 && 100

100 und nicht nur einen wahren Boolean.

Boolean ist etwas Besonderes (5/5)

Dual-Value numerisch verwenden

$boolean = !! $any;
$numeric = 0 + $boolean;
$string  = q{} . $boolean;

0 + !! 'bla'
0 + !! ( $result && $result->event )

Verstehen von: "Warum hier"

 my $is_y = $x eq 'y' ? 1 : 0;
 print $is_y;

An der Schnittstelle "Numeric output" konvertieren.

 my $is_y = $x eq 'y';
 print 0 + $is_y;

map verstanden? (1/2)

Jedes Element kommt als $_ (Alias) der Reihe nach im map-Block an.
Das Ergebnis der letzten Anweisung ist der Wert, welcher weitergereicht wird.

@destination
    = map {
        my $name = "${_}suffix";
        $name;
    } @source;

@destination = map { "${_}suffix" } @source;

map verstanden? (2/2)

@destination
    = map {
        $_ if defined;
    } undef, 2;
# is: q{}, 2

grep verstanden?

Das Ergebnis der letzten Anweisung wird boolsch verarbeitet
- wahr: Element wird weitergeleiet.
- falsch: Element wird nicht weitergeleitet.

@destination
    = grep {
        $_ < 10;
    } @source;

grep als map geschrieben

@destination
    = map {
        $_ < 10
        ? $_    # 1 Element weiterleiten
        : ();   # 0 Elemente weiterleiten
    } @source;

aus 1 mach 2 oder 0

%destination
    = map {
        defined
        ? ( $_ => undef ) # 2 Elemente weiterleiten
        : ();             # 0 Elemente weiterleiten
    } @source;

Was hat join mit map zu tun?

Es sammelt wieder ein.

$string
    = join
        ', ',
        map {
            m{ ( \S+ ) }xmsg;
        } (
            'foo bar',
            'baz',
        );
# foo, bar, baz

Wann for(each), map, while?

map => for(each)

map {
    print "$_\n";
} @array

for my $element (@array) {
    print "$element\n";
}

for(each) + push => map

my @destination;
for my $element (@source) {
    push @destination, $element x 2;
}

my @destination
    = map {
        $element x 2;
    } @source;

for(each) passt hier nicht

my @destination;
for my $element (@source) {
    push @destination, { $element => shift @source };
}

my @destination;
while ( my ($key, $value) = splice @source, 0, 2 ) {
    push @destination, {$key, $value};
}

grep als map Beispiel

@destination
    = map {
        "$_\n";
    }
    grep {
        length;
    }
    grep {
        defined;
    } @source;
 
@destination
    = map {
        defined && length
        ? "$_\n"
        : ();
    } @source;

Die Listenreferenz

gibt es nicht.

Der Begriff taucht im Netz meist in Verbindung mit Perl auf, wie z.B. hier: http://www.ims.uni-stuttgart.de/~zinsmeis/Perl/material/ho_referenzen_040123.pdf

$ref = \@array;      # arrayref
$ref = [ 1 .. 10 ];
$ref = \( 1 .. 10 ); # \10

Code in Strings ausführen

"code ${ \( $obj->method ) } in string"
"code @{ [ $obj->method ]  } in string"

Methoden aufrufen

$obj->method
$obj->$string
$obj->$coderef
$obj->${ \"get_$name" }
$obj->${ \( call_a_sub(...) ) }

\Q und Freunde

my $chars   = '.,[{';
my $string = "Weiter mit $chars foo.";
$is_match = $string =~ m{ \QWeiter mit\E }xms;
$is_match = $string =~ m{ \s+ \Q$chars\E \s+ }xms;
\Q => quotemeta
\L => lc
\l => lcfist
\U => uc
\u => ucfirst
\E => Ende

Multiplizieren

'abc' x 2;    # abcabc
( 'abc' ) x 2 # ( 'abc', 'abc');

Die Regex ist zu schnell zur Hand

index, rindex, substr

$index  = index  'aa bb cc', 'bb';  # 3
$index  = rindex 'aa bb cc', 'bb';  # 3
$substr = substr 'aa bb cc',  3, 2; # 'bb'
$substr = substr 'aa bb cc', -5, 2; # 'bb'
$string ~~ [ qw( a b c ) ];

no warnings qw(numeric);
$numeric = -'123.456-'; # -123.456

Gleich und völlig unterschiedlich

require, do, shift, eval

require 5.010; # version check
               # not use 5.010 for features
require PackageName;

do { expr1; expr2; };
do 'include.pl';

shift
shift @ARGV
shift @_

eval { expr1; expr2; }
eval $string;

Wann die oder confess? (1/2)

die

die 'Error';       # Error at [file] line [line].
die "Error\n";     # Error
die 'Error', "\n"; # Error
die;               # Died at [file] line [line].
die "\n";          # \n

Wann die oder doch confess? (2/2)

confess

confess 'Error', 123;
# Error123 at [file] line [line].
# \tPackage::sub(parameterlist) at [file] line [line]
confess 'Error', "\n";
# Error
#  at [file] line [line].

Woher kommt confess?

use Carp qw(confess);
use Moose; # imports confess

Die Krankheit von File::Slurp

binmode

# ok
my $bytes = read_file(
    'foo/bar/filename',
    binmode => ':raw',
);
# not ok
my $unicode = read_file(
    'foo/bar/filename',
    binmode => ':encoding(cp1252)',
);

Die Heilung (1/2)

Path::Class

use Path::Class qw(file);
my $unicode
    = file( qw( foo bar filename ) )
        ->slurp( iomode => '< :encoding(cp1252)' );

Die Heilung (2/2)

Path::Class und nicht File::Spec

use Path::Class qw(dir file);
my $unicode
    = file(
        dir('./')->subdir( qw( foo bar ) )->absolute,
        'filename',
    )
    ->slurp( iomode => '< :encoding(cp1252)' );

anstatt File::Find

Path::Class::Rule

use Path::Class::Rule;

my @html_filenames
    = Path::Class::Rule
        ->new
        ->file
        ->name( qw( *.htm *.html ) )
        ->all( $dir );

method und function

als Syntax

use syntax qw(method function);

fun foo ($parm) {
    return $parm;
}

method bar ($param) {
    return $self->baz($param);
}

Web ohne Template (1/4)

oder das HTML-File selbst ist es.

Designertauglich und mit W3C-Validator validierbar.

    ...
    <title></title>
    ...
    <tr class="z-result">
        <td class="z-name">dummy name</td>

Web ohne Template (2/4)

Laden

    use HTML::Zoom;

    my $zoom
        = HTML::Zoom
        ->from_file('example.html');

Web ohne Template (3/4)

Verarbeiten

    $zoom = $zoom
      ->replace_content( title => 'Example' )
      ->select('.z-result')
      ->repeat([
        map {
          my $result_ref = $_;
          sub {
            return $_->replace_content(
              '.z-name' => $result_ref->{name},
            )
          }
        } @{$results_ref}
      ]);

Web ohne Template (4/4)

Ausgeben

    print $zoom->to_html;

siehe Verzeichnis "example" in https://metacpan.org/release/Data-Page-Pagination

Ende