2009-10-15 7 views
5

Vì vậy, tôi đã có một chức năng ucwords đơn giản cho Perl mà tôi đã có một thời gian, và muốn mở rộng nó, đây là những gì tôi đã đưa ra, đây có phải là cách tôi nên xây dựng các chức năng của tôi để xử lý tùy chọn thông số?Đây có phải là cách để xây dựng các chương trình con Perl không?

gốc:

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, nhưng mã của bạn có làm việc này không? 'join '' map {ucfirst} chia/(\ s +)/$ string'? – Ether

+3

@Psytronic bạn nên đọc «perldoc perlsub': http://perldoc.perl.org/perlsub.html –

+0

Cảm ơn lời khuyên, tôi đã không tìm kiếm ý kiến ​​về chính chức năng này, tôi biết có lẽ tốt hơn cách thực hiện công việc, đó là nhiều ý kiến ​​về cách tôi xây dựng nó, vì tôi không muốn làm những điều sai trái ngay từ đầu và lặp đi lặp lại nó nhiều lần. – Psytronic

Trả lời

25

Trong một từ: NO!

Hãy xem tại địa chỉ:

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

Trước hết, bạn không sử dụng strict. Sử dụng nó. Nó là vì lợi ích của riêng bạn.

Thứ hai, bạn không sử dụng warnings. Sử dụng nó. Nó là vì lợi ích của riêng bạn. Ví dụ: thành phần đầu tiên của @_ phải được gọi bằng cách sử dụng $_[0]không@_[0].

Thứ ba, bạn nên có thói quen đọc danh sách FAQ thỉnh thoảng trước khi tái phát minh ra bánh xe một lần nữa: Xem How do I capitalize all the words on one line?

Nếu bạn nghĩ rằng đây là khắc nghiệt, xem xét thực tế rằng khi gọi là:

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

đầu ra mã của bạn

 
Fred And Barney'S Lodge 

đó là ví dụ được đưa ra trong câu hỏi đó.

Hơn nữa, có chức năng làm nhiều việc, chọn những gì nó làm trên cơ sở các số bí ẩn và không một trong những điều đó đúng không phải là một chiến lược thiết kế tốt.

Thay vào đó, bạn nên có nhiều hàm, được đặt tên theo cách có thể được đọc bởi một trình đọc thông thường của mã của bạn, mỗi mã chỉ thực hiện một việc và thực hiện đúng.

Cuối cùng, phiên bản mở rộng chức năng của mình (mà không nói bất cứ điều gì về sự khôn ngoan của việc viết một chức năng như vậy) có thể được viết tốt hơn như:

# 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; 
    } 
} 

Xem thêm Why use if-else if in C++?

+4

Tôi nghĩ bạn nên trả lời hai lần trong trường hợp này. Một lần với câu trả lời ban đầu của bạn và một lần với phiên bản hiện tại. Bạn sẽ kiếm được hai upvotes từ tôi trong trường hợp đó. – innaM

+0

Tôi đồng ý nó phải là một câu trả lời khác, nhưng tôi không biết liệu tôi có nên upvoted đầu tiên, như chỉ nói để sử dụng nghiêm ngặt và cảnh báo không thực sự chỉ ra các vấn đề khác với chức năng. Dù bằng cách nào, +1 cho cái này. :) – NateDSaint

+1

Nó sẽ đi mà không nói ... nhưng là người duy trì kịch bản trong công việc của tôi, tôi sẽ nói nó ... Nếu bạn đã sử dụng một cái gì đó giống như chức năng mở rộng ở trên - DOCUMENT nó. Đặt tên cho chế độ của bạn, nếu chỉ trong phần bình luận. – Rini

4

Có thể bạn sẽ tìm thấy Params::Validate hữu ích. Nó có thể được sử dụng để xác nhận các thông số theo các quy tắc khác nhau. Đây là cách nó có thể trông như thế nào trong trường hợp của bạn:

## 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

Không sử dụng cấu trúc $foo ne undef. Các toán tử trong Perl là những gì được gọi là "bối cảnh nhạy cảm". Bằng cách sử dụng một số toán tử nhất định, bạn giới thiệu một số ngữ cảnh nhất định.ne, eq, lt, gt, le, ge là tất cả "chuỗi" các nhà khai thác, xử lý các vô hướng ở hai bên như dây đàn, trong khi ==, !=, <, >, <=, >= là nhà khai thác số, xử lý các đối tượng ở hai bên như một số.

Tuy nhiên, nếu bạn đang thử nghiệm cho undef, nó thực sự không có ý nghĩa rằng một cái gì đó không xác định là một số hoặc một chuỗi, vì vậy họ có một nhà điều hành chỉ cho rằng loại xét nghiệm: defined

Bạn có thể kiểm tra nếu có điều gì được xác định đơn giản bằng cách làm

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

này có thể chỉ là quan điểm của tôi, và phong cách mã hóa của bạn là hoàn toàn tùy thuộc vào bạn, nhưng cá nhân tôi thấy rất nhiều giá trị trong việc phân công các đối số cho các biến ngay lập tức , và thay vì gói phần "kinh doanh" của chương trình con của bạn trong một khối nếu tôi muốn có hàm croak befor e đó. Ví dụ:

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

Đó là một điểm tuyệt vời. Tôi muốn chỉnh sửa bản gốc nhưng điều đó làm cho bình luận của bạn dường như vô nghĩa. Tôi sẽ thêm bản chỉnh sửa vào nó. – NateDSaint

+1

@NateDSaint: Tôi đã tự do chỉnh sửa bài đăng của bạn và xóa nhận xét của tôi. Vui lòng quay lại nếu bạn không thích thay đổi. –

+0

Có lẽ ít gây hiểu lầm theo cách này. Cảm ơn! – NateDSaint

4

die

die, như builtins perl khác, không cần, và nói chung không nên có dấu ngoặc đơn. Tuy nhiên, die có một người anh lớn mà hầu hết mọi người sử dụng những ngày này, được gọi là

croak

Đỗ:

use Carp; 

và sau đó

croak "My error here!"; 

croak hoạt động giống như chết, nhưng nói chung thêm thông tin hữu ích hơn vào thông báo lỗi hơn die, chẳng hạn như dòng xảy ra lỗi trên rel thụ động cho người gọi.

3

Mảng Indexing

truy cập mảng, giống như những thứ khác trong Perl, là bối cảnh nhạy cảm. Hãy suy nghĩ về sigil được gắn vào tên như một 'lời nhắc' cho bạn về những gì bạn đang cố gắng truy cập hoặc sử dụng vào lúc này. Bất cứ khi nào bạn nhìn thấy $, điều đó có nghĩa là bạn đang cố gắng để có được một giá trị vô hướng duy nhất. Bất cứ khi nào bạn nhìn thấy @, điều đó có nghĩa là bạn đang truy cập danh sách và % tất nhiên có nghĩa là cặp băm khóa/giá trị. Vì vậy, khi bạn truy cập vào mảng của mình như thế này:

@_[1] 

Bạn đang yêu cầu danh sách, chứa một phần tử duy nhất. Tính năng này cho phép bạn nhận được nhiều giá trị từ một mảng cùng một lúc, nhưng khi chỉ truy cập một giá trị, nó gây ra các vấn đề trong một số ngữ cảnh, chẳng hạn như gán. Vì vậy, khi truy cập vào một phần tử mảng duy nhất, bạn muốn luôn luôn sử dụng bối cảnh vô hướng:

$_[1] 
5

lệnh switch Perl: cho/khi

Perl, tính đến 5,10 trở lên, có một công tắc tuyệt vời tuyên bố được xây dựng trong, được gọi là [given].Điều này tương đương với câu lệnh switch trong C, nhưng linh hoạt hơn nhiều. Để bật tính năng này, bạn cần phải thêm một dòng ở phía trên cùng của kịch bản của bạn:

use 5.010; 

Điều này cho phép tất cả các perl 5.10 tính năng, bao gồm cả chuyển đổi (và say, mà làm việc như print nhưng sẽ tự động thêm một "\ n "ở cuối) bạn có thể sử dụng nó như thế này:.

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; } 
} 

biến truyền cho được tự động được đưa vào bên trong $_ mã nhất định, cho phép bạn so sánh với nó. Sau đó, when xây dựng hiện một trận đấu thông minh chống lại $_. Vì vậy, trong trường hợp của bạn, nó sẽ giống như thế này (sửa chữa các @[] để $ [] vấn đề):

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

@_ giải nén

Nói chung, bạn luôn muốn giải nén @_ trước khi thực hiện bất kỳ quá trình xử lý nào khác trong chương trình con của bạn. Điều này làm cho nó nhiều, rõ ràng hơn nhiều đối với người dùng, những người bảo trì khác và bản thân bạn trong tương lai về cách sử dụng phụ của bạn. Bằng cách sử dụng trực tiếp @_, rất khó để tìm ra những gì cần phải được thông qua, chỉ từ các đối số đã cho. Họ không có bất kỳ cái tên có ý nghĩa nào, làm cho việc xác định mục đích của họ thậm chí còn khó hơn, và bạn có hằng số phép thuật ở mọi nơi - thường là một điều xấu tổng thể!

Đặt cược tốt nhất của bạn là chuyển các biến số thành các vô hướng được đặt tên có ý nghĩa ngay lập tức, trước khi bạn làm bất cứ điều gì khác.

Đối với một đối tượng con, một giải pháp phổ biến là sử dụng shift. Điều này kéo phần tử đầu tiên của một mảng ra, và trả về nó (loại giống như đối diện của pop.) Nếu không được đưa ra một mảng, và bạn đang ở trong một chương trình con, nó kéo nó từ mảng @_. Vì vậy, bạn có thể làm

sub mysub { 
    my $foo = shift; 
} 

cho bất kỳ chương trình con đối số nào.

Tuy nhiên, điều gì xảy ra nếu bạn có nhiều hơn? Liệt kê chuyển nhượng ngữ cảnh, để giải cứu! Có thể gán nhiều biến cùng một lúc, sử dụng gán danh sách. Bạn có thể làm

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

$foo, $bar, và $baz sẽ được gán giá trị trong 0, 1, 2 và chỉ số của @_, tương ứng. Vâng, điều gì sẽ xảy ra nếu không có gì trong chỉ mục 0, 1 hoặc 2? Họ vẫn được chỉ định - họ trở thành undef! Sau đó, bạn có thể kiểm tra undef như đã đề cập ở những nơi khác trong câu hỏi này.

2

Tôi rất không thích các chức năng quá thông minh. Một hàm quá thông minh là một hàm có hành vi hoàn toàn thay đổi bởi các tham số của nó. Nhìn vào bạn, họ hầu như không chia sẻ bất kỳ mã nào ngoại trừ xử lý tham số. Dù sao nếu tôi sẽ làm một số tương tự như tôi này sẽ viết một cái gì đó như thế này:

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

cái gì đó là được ám chỉ nhưng không trực tiếp đề cập đến trong câu trả lời khác là việc sử dụng các chế độ số, một quy ước xa lạ với Perl tổ chức trên từ C Nhanh chóng, không nhìn vào mã, chế độ # 3 làm gì? Địa ngục, nhìn vào mã những gì hiện chế độ # 3 làm gì?

Perl có chuỗi hiệu quả và dễ sử dụng. Sử dụng chúng.Đặt tên chế độ của bạn có liên quan đến những gì nó đang làm. Một cái gì đó như ... đầu tiên, cuối cùng, recase_first, recase_last. Chúng không cần phải mô tả đầy đủ, lower_case_then_uc_last_letter sẽ quá dài để gõ, nhưng đủ cho một cái gì đó cho bộ não con người nối vào và kết hợp.

Nhưng thực sự đây là bốn chương trình con. Cờ chế độ là cờ đỏ, đặc biệt là khi hầu hết mã của bạn gió lên bên trong câu lệnh if/else.