2009-10-15 5 views
5

Also ich hatte eine einfache ucwords-Funktion für Perl, die ich eine Weile hatte, und wollte es erweitern, das ist, was ich mir ausgedacht habe, ist das die Art, wie ich meine Funktionen bauen sollte, um optional zu arbeiten Parameter?Ist das der Weg, um Perl Subroutinen zu erstellen?

Original:

sub ucwords{ 
    $str = @_[0]; 
    $str = lc($str); 
    $str =~ s/\b(\w)/\u$1/g; 
    return $str; 
} 

Extended:

sub ucwords{ 
    if(@_[0] ne undef){#make sure some argument was passed 
     @overloads = (0,1,2,3); 
     $str = @_[0]; 
     if(@_[1] eq undef || @_[1] eq 0){ #default is to lowercase all but first 
      $str = lc($str); 
      $str =~ s/\b(\w)/\u$1/g; 
      return $str; 
     }else{ #second parameters 
      if(!grep $_ eq @_[1], @overloads){ die("No overload method of ucwords() takes "[email protected]_[1]." as second parameter."); } 
      if(@_[1] eq 1){ $str =~ s/\b(\w)/\u$1/g;} #first letter to upper, remaining case maintained 
      if(@_[1] eq 2){ $str = lc($str); $str =~ s/(\w)\b/\u$1/g;} #last letter to upper, remaining to lower 
      if(@_[1] eq 3){ $str =~ s/(\w)\b/\u$1/g;} #last letter to upper, remaining case maintained 
      return $str; 
     } 
    }else{ 
     die("No overload method of ucwords() takes no arguments"); 
    } 
} 

Psy

+5

tl; dr, aber tut nicht Ihr Code nur das? 'join '' map {ucfirst} split/(\ s +)/$ string'? – Ether

+3

@Psytronic sollten Sie lesen "perldoc perlsub": http://perldoc.perl.org/perlsub.html –

+0

Danke für den Rat, ich war nicht besonders auf der Suche nach Kommentaren in Bezug auf die Funktion selbst obwohl, ich weiß, es gibt wahrscheinlich besser Es gab mehr Kommentare über die Art, wie ich es gemacht habe, weil ich nicht von Anfang an etwas falsch machen und es immer wieder wiederholen wollte. – Psytronic

Antwort

25

In einem Wort: NEIN!

Schauen wir uns:

sub ucwords{ 
    $str = @_[0]; 
    $str = lc($str); 
    $str =~ s/\b(\w)/\u$1/g; 
    return $str; 
} 

In erster Linie Sie verwenden nicht strict. Benutze es. Es ist zu deinem Besten.

Zweitens verwenden Sie nicht warnings. Benutze es. Es ist zu deinem Besten. Zum Beispiel sollte das erste Element von @_ unter Verwendung von $_[0] und nicht@_[0] bezeichnet werden.

Drittens sollten Sie in der Gewohnheit des Lesens der FAQ-Liste erhalten gelegentlich vor das Rad neu zu erfinden, wieder: Siehe How do I capitalize all the words on one line?

Wenn Sie denken, das ist hart, die Tatsache, dass beim Aufruf wie:

print ucwords("FRED AND BARNEY'S LODGE"), "\n"; 

Code Ausgänge

 
Fred And Barney'S Lodge 

welches das Beispiel in dieser Frage gegeben ist.

Weiter, mit einer Funktion, die mehr als eine Sache tut, wählt, was es auf der Grundlage von Mystery-Zahlen tut und nichts von diesen Dingen richtig ist, ist keine gute Design-Strategie.

Sie sollten stattdessen mehrere Funktionen haben, benannt in einer Weise, die von einem gelegentlichen Leser Ihres Codes verstanden werden kann, von denen jeder nur eine Sache tut und dieses Recht tut.

Schließlich ist die erweiterte Version Ihrer Funktion (ohne eine solche Funktion zu schreiben, etwas über die Weisheit zu sagen) besser werden kann geschrieben:

# untested code follows 

use Carp; 

{ 
    my %modes = map {$_ => undef} 0 .. 3; 
    sub ucwords{ 
     croak 'No arguments passed' unless @_; 

     my ($str, $mode) = @_; 
     $mode = 0 unless defined $mode; 

     croak "Invalid mode: '$mode'" unless exists $modes{$mode}; 

     if ($mode == 0) { 
      $str = lc($str); 
      $str =~ s/\b(\w)/\u$1/g; 
     } 
     elsif ($mode == 1) { 
      $str =~ s/\b(\w)/\u$1/g;   
     } 
     elsif ($mode == 2) { 
      $str = lc($str); 
      $str =~ s/(\w)\b/\u$1/g;   
     } 
     else { 
      $str =~ s/(\w)\b/\u$1/g; 
     } 

     return $str; 
    } 
} 
auch

Siehe Sie Why use if-else if in C++?

+4

Ich denke, du hättest in diesem Fall zweimal antworten sollen. Einmal mit Ihrer ursprünglichen Antwort und einmal mit der aktuellen Version. Sie hätten in diesem Fall zwei Upvotes von mir bekommen. – innaM

+0

Ich stimme zu, es sollte eine andere Antwort sein, aber ich weiß nicht, ob ich die erste upvoted hätte, wie nur zu sagen strikte und Warnungen zu verwenden, zeigt nicht wirklich die anderen Probleme mit der Funktion. So oder so, +1 für diesen. :) – NateDSaint

+1

Es sollte selbstverständlich sein ... aber als die Person, die Skripte in meinem Job unterhält, werde ich es sagen ... Wenn Sie etwas wie die erweiterte Funktion oben verwenden - DOKUMENTIEREN Sie es. Benenne deine Modi, nur in den Kommentaren. – Rini

4

werden können, finden Params::Validate nützlich. Es kann verwendet werden, um Parameter nach verschiedenen Regeln zu validieren. Hier ist, wie es in Ihrem Fall aussehen:

## somewhere is the start of the module 
use Params::Validate qw(:all); 

sub ucwords { 
    ## this line helps to undestand which parameter should be passed to function 
    my ($string, $algorithm_id) = @_; 

    ## make sure that 2 scalar parameters passed 
    validate_pos(@_, {'type' => SCALAR}, {'type' => SCALAR}); 

    ## main code here 
} 
11

Sie nicht das $foo ne undef Konstrukt verwenden. Operatoren in Perl sind als "kontextsensitiv" bekannt. Indem Sie bestimmte Operatoren verwenden, führen Sie bestimmte Kontexte ein.ne, eq, lt, gt, le, ge sind alle "string" Operatoren, die auf beiden Seiten als Strings die Skalare Behandlung, während ==, !=, <, >, <= sind >= numerischen Operatoren, die auf beiden Seiten des Objekts behandeln, als eine Zahl.

Wenn Sie jedoch für undef sind testen, es ist wirklich nicht Sinn machen, dass etwas nicht definiert eine Zahl oder eine Zeichenfolge, so dass sie einen Operator nur für diese Art von Test: defined

können Sie Test, wenn etwas definiert wird einfach durch

if (defined $foo) { 
    # my cool logic on $foo here 
} 
5

Dies kann nur meine Meinung zu tun sein, und Ihre Codierung Stil ist völlig bis zu Ihnen, aber ich persönlich finde viel Wert in die Argumente auf Variablen rechts von der Fledermaus Zuordnung und anstatt den "Geschäfts" -Teil Ihrer Unterroutine in einen if-Block zu hüllen, würde ich die Funktion vor mir krachen lassen e das. Zum Beispiel:

use Carp; 

sub ucwords { 
    my $str = shift; 
    defined($str) 
     or croak 'No overload method of ucwords() takes no arguments'; 
    #the rest of your logic 
} 
+0

Das ist ein toller Punkt. Ich würde das Original bearbeiten, aber das lässt deinen Kommentar sinnlos erscheinen. Ich werde eine Bearbeitung hinzufügen. – NateDSaint

+1

@NateDSaint: Ich habe mir die Freiheit genommen, deinen Beitrag zu bearbeiten und meinen Kommentar zu löschen. Fühlen Sie sich frei, Rollback, wenn Sie die Änderungen nicht mögen. –

+0

Wahrscheinlich weniger irreführend auf diese Weise. Vielen Dank! – NateDSaint

4

die

die, wie andere Perl builtins, braucht nicht, und in der Regel nicht Klammern haben sollte. Allerdings hat die einen großen Bruder, dass die meisten Menschen in diesen Tagen verwenden, genannt

croak

Do:

use Carp; 

und dann

croak "My error here!"; 

Krächzen wie sterben funktioniert, aber im Allgemeinen fügt der Fehlermeldung weitere nützliche Informationen hinzu als die, z. B. die Zeile, in der der Fehler bei rel aufgetreten ist dem Anrufer

3

Array Indexing

Array-Zugang, wie andere Dinge in Perl, ist kontextsensitiv. Denken Sie an das Siegel, das an den Namen angehängt wird, als eine "Erinnerung" an Sie, was Sie gerade erreichen oder verwenden möchten. Wann immer Sie die $ sehen, bedeutet das, dass Sie versuchen, einen einzelnen skalaren Wert zu erhalten. Wann immer Sie eine @ sehen, bedeutet das, dass Sie auf eine Liste zugreifen, und % bedeutet natürlich ein Schlüssel/Wert-Hash-Paar. Also, wenn Sie auf Ihr Array wie folgt zugreifen:

@_[1] 

Sie fragen nach einer Liste, die ein einzelnes Element enthält. Mit dieser Funktion können Sie mehrere Werte gleichzeitig aus einem Array abrufen. Wenn Sie jedoch nur auf einen Wert zugreifen, führt dies in einigen Kontexten, z. B. bei der Zuweisung, zu Problemen. Also, wenn ein einzelnes Array-Element zugreifen, sollten Sie immer die skalaren Kontext verwenden:

$_[1] 
5

Perl switch-Anweisung: gegeben/wenn

Perl, wie von 5,10 und höher, hat einen fantastischen Schalter Anweisung eingebaut, genannt [given].Dies entspricht in etwa der switch Aussage in C, ist aber vielseitiger. Um diese Funktion zu aktivieren, müssen Sie eine Zeile am Anfang des Skripts hinzuzufügen:

use 5.010; 

Dies ermöglicht es, alle Perl 5.10 Funktionen, einschließlich Schalter (und say, die wie print funktioniert, aber fügt automatisch ein „\ n "am Ende) Sie es wie folgt verwendet werden:.

my $foo = get_foo(); 
my $nothing = 0; 
given($foo) { 
    when (undef) { say "got an undefined value!"; } 
    when ([1,3,5,6,8]) { say "was 1, 3, 5, 6, or 8"; } 
    when (/^abc/) { say "was a string starting with abc"; } 
    when ($_ == 4) { say "It was 4!!!"; } 
    when ($_ > 100) { say "Greater than 100"; } 
    default { $nothing = 1; } 
} 

die variablen automatisch wird in $_ innerhalb des gegebenen Code setzt gegebene geben, so dass Sie dagegen vergleichen. Dann wird das when Konstrukt macht ein intelligentes Spiel gegen $_. also in Ihrem Fall würde es so aussehen (das @ Festsetzung [] bis $ [] Ausgabe):

given ($_[1]) { 
    when (1) { $str =~ s/\b(\w)/\u$1/g } 
    when (2) { $str = lc($str); $str =~ s/(\w)\b/\u$1/g } 
    when (3) { $str =~ s/(\w)\b/\u$1/g; } 
    default { croak "No overloaded method of ucwords() takes '$_'." } 
} 
5

@_ Auspacken

Im Allgemeinen möchten Sie immer @_ entpacken, bevor Sie eine andere Verarbeitung in Ihrem Unterprogramm durchführen. Dies macht es für Benutzer, andere Betreuer und Sie selbst in Zukunft viel klarer, wie Sie Ihr Sub verwenden können. Durch die direkte Verwendung von @_ ist es sehr schwer herauszufinden, was zu übergeben ist, nur aus den gegebenen Argumenten. Sie haben keine bedeutungsvollen Namen, was es noch schwieriger macht, ihren Zweck zu bestimmen, und du hast überall magische Konstanten - normalerweise eine schlechte Sache insgesamt!

Ihre beste Wette ist, die Variablen sofort in sinnvoll benannte Skalare zu bringen, bevor Sie irgendetwas anderes tun.

Für ein Argument Unterroutinen ist eine gemeinsame Lösung shift zu verwenden. Dies zieht das erste Element eines Arrays aus und gibt es zurück (ähnlich wie das Gegenteil von pop). Wenn kein Array angegeben wurde und Sie sich in einer Subroutine befinden, wird es aus dem Array @_ abgerufen. Sie können also

sub mysub { 
    my $foo = shift; 
} 

für ein beliebiges Argument-Unterprogramm tun.

Wie auch immer, wenn Sie mehr haben? Listen Kontext Zuordnung, zur Rettung! Es ist möglich, viele Variablen auf einmal zuzuordnen, indem Sie eine Listenzuweisung verwenden. Sie können

sub myothersub { 
    my ($foo, $bar, $baz) = @_; 
} 

tun Und $foo, $bar und $baz den Wert in dem 0, 1 und 2-Indizes von @_ jeweils zugeordnet werden. Nun, was passiert, wenn es im Index 0, 1 oder 2 nichts gibt? Sie werden immer noch zugewiesen - sie werden undef! Dann können Sie nach undef suchen, wie an anderer Stelle in dieser Frage erwähnt.

2

Ich mag sehr übermäßig intelligente Funktionen stark. Eine übermäßig intelligente Funktion ist eine Funktion, deren Verhalten sich durch ihre Parameter völlig ändert. Schau dir deine an, sie teilen fast keinen Code außer der Parameterbehandlung. Wie auch immer, wenn ich dies etwas ähnliches tun würde ich so etwas schreiben würde:

use Carp; 

{ 
    my %ucwords = (
     0 => sub { 
      my $str = lc(shift()); 
      $str =~ s/\b(\w)/\u$1/g; 
      return $str; 
     }, 
     1 => sub { 
      my $str = shift; 
      $str =~ s/\b(\w)/\u$1/g; 
      return $str; 
     }, 
     2 => sub { 
      $str = lc(shift()); 
      $str =~ s/(\w)\b/\u$1/g; 
      return $str; 
     }, 
     3 => sub { 
      my $str = shift; 
      $str =~ s/(\w)\b/\u$1/g; 
      return $str; 
     } 
    ); 

    sub ucwords { 
     my ($str, $mode) = @_; 
     croak "No overload method of ucwords() takes no arguments" 
      unless defined $str; 
     $mode = 0 unless defined $mode; 
     my $code = $ucwords{$mode}; 
     croak "Invalid mode: '$mode'" unless defined $code; 
     goto \&$code; 
    } 
} 
2

Etwas, das in anderen Antworten adressierte an, aber nicht direkt angedeutet ist schon ist die Verwendung von numerischen Modi, eine Konvention fremd Perl vertagt von C Schnell, ohne auf den Code zu schauen Was macht Modus # 3? Hölle, schau auf den Code was macht Modus # 3?

Perl hat effiziente und einfach zu bedienende Strings. Benutze sie.Geben Sie Ihren Modi Namen, die etwas damit zu tun haben. Etwas wie ... zuerst, zuletzt, recase_first, recase_last. Sie müssen nicht unbedingt beschreibend sein, low_case_then_uc_last_letter wäre zu lang, um zu tippen, aber genug, um dem menschlichen Gehirn etwas zu geben, mit dem es sich verbinden und assoziieren kann.

Aber das sind wirklich vier Subroutinen. Modus-Flags sind rote Flags, besonders wenn der meiste Code in einer if/else-Anweisung steht.

Verwandte Themen