2012-01-16 18 views
8

Trên (nhiều đánh giá cao) perlmonks trang web, tôi thấy following snippet that trims các không gian từ cả hai phía của một chuỗi:Trong Perl, một hàm có nên thực hiện điệu nhảy mong muốn hay chúng ta có thể mong đợi người gọi sử dụng bản đồ không?

sub trim { 
    @_ = $_ if not @_ and defined wantarray; 
    @_ = @_ if defined wantarray; 
    for (@_ ? @_ : $_) { s/^\s+//, s/\s+$// } 
    return wantarray ? @_ : $_[0] if defined wantarray; 
} 

Tôi không hiểu tại sao tác giả đi đến tất cả những rắc rối của việc kiểm tra wantarray hầu hết các dòng . Tại sao không chỉ cắt xâu chuỗi, và lập trình viên sử dụng map khi anh ta đi qua một mảng?

là gì sự khác biệt giữa cắt đó, được gọi là như thế này:

my @test_array = ('string1', ' string2', 'string3 ', ' string4 '); 
my @result = trim(@test_array); 

Hoặc một cắt đơn giản, được gọi như thế này khi người ta cần để cắt một mảng:

my @test_array = ('string1', ' string2', 'string3 ', ' string4 '); 
my @result = map { trim($_) } @test_array; 
+0

tại sao bạn muốn để bản đồ cho người dùng trong khi bạn có thể tóm tắt bản đồ? –

+1

Liên quan: [trim in Perl] (https://plus.google.com/105725977711317285348/posts/ienzxqHJmRe) – daxim

+0

wantarray và shit phép thuật tương tự là một blight. Đừng lập trình thông minh trừ khi bạn phải làm. API không bao giờ thông minh. – tsee

Trả lời

11

Trước hết đó là tốt hơn nếu bạn trừu tượng mà bản đồ đi:

#e.1. 
sub trim 
{ 
    my @ar = @_; 
    for (@ar) { s/^\s+//, s/\s+$// }; 
    return wantarray ? @ar : $ar[0]; 
} 

Thứ hai, xem xét ví dụ ở trên và so sánh nó với:

#e.2. 
sub trim 
{ 
    for (@_) { s/^\s+//, s/\s+$// }; 
} 

những gì là Sự khác biệt?

e.1. trả về một mảng được cắt mới, trong khi e.2. sửa đổi mảng ban đầu.

Ok bây giờ, chương trình con khó hiểu ban đầu làm gì?

Nó tự động một cách kỳ diệu (yeaah, nó Perl) làm thay đổi mảng gốc nếu bạn không gán giá trị trả về bất cứ điều gì HOẶC rời mảng gốc hoang sơ và trả về một tỉa mảng mới nếu bạn đang gán giá trị trả về sang một biến khác.

Làm cách nào?

Bằng cách kiểm tra xem ifarray có được xác định hay không. miễn là hàm nằm ở phía bên tay phải và giá trị trả về được gán cho một biến "hàm xác định được xác định" là đúng (bất kể ngữ cảnh vô hướng/mảng).

+2

Tác giả cũng muốn hàm hoạt động trên '$ _' hoàn toàn nếu không có đối số nào được truyền cho hàm. – mob

+0

Vì vậy, bạn nên giới thiệu cho mỗi chức năng đề nghị chấp nhận cả DANH SÁCH và SCALAR, vì vậy người gọi không phải tự mình viết bản đồ? Tôi thích ý tưởng này, nhưng có hai điều khiến tôi phải lòng: 1. chi phí khi không cần thiết; 2. sao chép mã (4 dòng thay vì 1) chỉ để xử lý void/vô hướng/danh sách ngữ cảnh. – Konerak

+0

không chấp nhận (dù sao các thông số của bạn sẽ chuyển sang @_ trong chương trình con) nhưng để trả lại. và nó có thể đạt được với sự trở lại mong muốn? @ar: $ ar [0]; tuyên bố. bản gốc 4-liner đang làm nhiều hơn nữa mà chăm sóc của vô hướng/danh sách như tôi đã cố gắng giải thích ở trên. –

5

Có lẽ tác giả muốn để bắt chước hành vi của hàm chuẩn chomp. Không cần phải làm điều này trong chức năng của riêng bạn.

man perlfunc 
chomp VARIABLE 
chomp(LIST) 
chomp 

[...] Nếu bạn chomp một danh sách, mỗi phần tử được chomped. [...]

+0

Tôi đoán là không có gì để làm với loại hành vi này trong chomp –

+0

chomp đang sửa đổi mảng ban đầu để nó có thể đơn giản như: sub chomp {for (@_) s/blah/blah /} –

+0

Yup, bạn là đúng. Thật vậy 'wantarray' trở nên được định nghĩa khi người gọi mong đợi một vô hướng. – kubanczyk

1

Lưu ý rằng đây chính là cách thực hiện Text::Trim. Xem mô tả của nó về các trường hợp sử dụng khác nhau. Chơi với wantarray cho phép phân biệt các ngữ cảnh khác nhau và thực hiện các ngữ nghĩa khác nhau cho mỗi ngữ cảnh.

Cá nhân tôi chỉ thích ngữ nghĩa duy nhất vì dễ hiểu và dễ sử dụng hơn. Tôi sẽ tránh sử dụng $_ biến mặc định hoặc sửa đổi tại chỗ, phù hợp với ví dụ của Nylon Smile 1.

7

Breaking dòng xuống này bằng đường vì nó đã không được:

sub trim { 
    @_ = $_ if not @_ and defined wantarray; 
    # if there are no arguments, but a return value is requested 
    # then place a copy of $_ into @_ to work on 

    @_ = @_ if defined wantarray; 
    # if the caller expects a return value, copy the values in @_ into itself 
    # (this breaks the aliasing to the caller's variables) 

    for (@_ ? @_ : $_) { s/^\s+//, s/\s+$// } 
    # perform the substitution, in place, on either @_ or $_ depending on 
    # if arguments were passed to the function 

    return wantarray ? @_ : $_[0] if defined wantarray; 
    # if called in list context, return @_, otherwise $_[0] 
} 

Tôi đồng ý rằng mã được một chút tẻ nhạt với tất cả các wantarray séc, nhưng kết quả là một chức năng mà chia sẻ một mức độ linh hoạt với các hàm dựng sẵn của Perl. Kết quả thực hiện chức năng "thông minh" là dọn dẹp trang web cuộc gọi (tránh các cấu trúc vòng lặp, biến tạm thời, lặp lại, ...) tùy theo tần suất mà hàm được sử dụng có thể cải thiện khả năng đọc mã một cách có ý nghĩa.

Chức năng có thể được đơn giản hóa một chút:

sub trim { 
    @_ = @_ ? @_ : $_ if defined wantarray; 

    s/^\s+//, s/\s+$// for @_ ? @_ : $_; 

    wantarray ? @_ : shift 
} 

Hai dòng đầu tiên có thể được cuộn thành một, vì họ đang làm điều tương tự (gán để @_) chỉ với giá trị nguồn khác nhau. Và không cần kiểm tra bên ngoài return ... if defined wantarray ở cuối, vì trả về một giá trị trong ngữ cảnh void không làm bất cứ điều gì.

Nhưng tôi có thể thay đổi dòng cuối cùng thành wantarray ? @_ : pop vì điều đó làm cho nó hoạt động giống như một danh sách (phần tử cuối cùng trong ngữ cảnh vô hướng).

Khi tất cả được nói và làm, điều này cho phép phong cách gọi sau đây sẽ được sử dụng:

my @test_array = ('string1', ' string2', 'string3 ', ' string4 '); 

my @result = trim @test_array; 
my $result = trim $test_array[0]; 
trim @test_array; # in place trim 

và thậm chí vẫn hỗ trợ vòng lặp chỗ gọi:

my @result = map trim, @test_array; 

hoặc một cách chi tiết hơn như:

my @result = map trim($_), @test_array; 

và nó có thể được sử dụng trong vòng lặp tương tự như chomp

while (<$file_handle>) { 
    trim; 
    # do something 
} 

Ý kiến ​​về sự lúng túng trong Perl được trộn lẫn. Cá nhân tôi thích nó khi chức năng cho tôi sự linh hoạt để mã người gọi theo cách có ý nghĩa, thay vì làm việc xung quanh giao diện cứng nhắc của một chức năng.

Các vấn đề liên quan