2011-10-25 29 views
10

tôi đã viết mã này trong perl:Chuyển đổi kết quả ngữ cảnh danh sách thành mảng trong một dòng trong perl?

shift(@interfaces = qx'ifconfig -s'); 

Và nhận lỗi này:

Type of arg 1 to shift must be array (not list assignment) 

Khi tôi viết nó theo cách này:

@interfaces = qx'ifconfig -s'; 
shift @interfaces; 

Nó làm những gì tôi muốn, đó là để có được đầu ra của lệnh ifconfig như một mảng các dòng và loại bỏ phần tử đầu tiên trong mảng (đó là một tiêu đề, không phải là một giao diện thực tế).

Sở thích cá nhân của tôi là viết thư này làm lớp lót. Dường như với tôi các dấu ngoặc đơn trong ví dụ đầu tiên sẽ khiến cho bài tập được giải quyết đầy đủ, do đó cho phép chuyển sang xem @interfaces như một mảng, nhưng rõ ràng perl nghĩ đó là một "danh sách gán".

Đây chắc chắn là một câu hỏi dễ dàng cho các bậc thầy perl nhưng tôi đã googled và googled và đã không tìm thấy giác ngộ.

Nếu ai đó vui lòng cung cấp ngữ nghĩa cụ thể để hoàn thành những gì tôi muốn trong một dòng, tôi sẽ đánh giá cao nó. Nếu bạn cũng xin vui lòng dành thời gian để giải thích lý do tại sao phiên bản đầu tiên của tôi không hoạt động tôi sẽ mãi mãi biết ơn (dạy một người đàn ông để cá và tất cả điều đó).

Cảm ơn bạn trước sự giúp đỡ của bạn.

Trả lời

13

Như bạn đã thấy, shift đòi hỏi một mảng đen, không phải là kết quả của một bài tập. Điều này là bởi vì khi perl phân tích shift @interfaces nó thực sự là viết lại nó vào một cái gì đó như &CORE::shift(\@interfaces) và bạn không thể tham chiếu đến một nhiệm vụ và nhận được một mảng ref.

Bạn có thể phá vỡ nó thành hai dòng như bạn đã tìm thấy, bạn có thể che giấu sự phân công bên trong một dereference trong ngoặc vuông như đám đông chương trình, hoặc bạn chỉ đơn giản là có thể vứt bỏ giá trị đầu tiên:

(undef, @interfaces) = qx'ifconfig -s'; 

undef trong một vị trí lvalue là một trình giữ chỗ cho các giá trị mà bạn không cần.

(phân tích cú pháp của shift đã thay đổi một chút trong perl 5.14+, nhưng lập luận trên vẫn giữ)


một số cách khác mà bạn có lẽ không nên sử dụng, ra lệnh chỉ bằng cách tăng chiều dài :)

my @interfaces = sub {shift; @_}->(qx'ifconfig -s'); 

my @interfaces = sub {@_[1..$#_]}->(qx'ifconfig -s'); 

my @interfaces = map {@$_[1..$#$_]} [qx'ifconfig -s']; 

my @interfaces = map {shift @$_; @$_} [qx'ifconfig -s']; 

our @interfaces; shift @{*interfaces = [qx'ifconfig -s']}; 

my @interfaces = sub {*_ = [qx'ifconfig -s']; shift; @_}->(); 
+0

đây là một giải pháp tuyệt vời nhưng từ quan điểm bảo trì, tôi muốn sử dụng dịch chuyển để nhấn mạnh đến một nhà phát triển trong tương lai mà tôi đang ném đi phần tử đầu tiên của một mảng. Tôi chắc chắn cho câu trả lời của bạn một upvote nhưng nó không hoàn toàn những gì tôi muốn bởi vì tôi tin rằng không sử dụng thay đổi làm giảm mã rõ ràng (YMMV tất nhiên). Câu trả lời tuyệt vời mặc dù. – par

+0

Tôi đã quyết định cuối cùng rằng điều này sẽ được rõ ràng đủ một khi thành ngữ chuyển nhượng undef được hiểu (là tôi và những người cuối cùng phải duy trì những gì tôi đang làm việc trên). Luôn luôn có một sự cân bằng mà phải được ấn tượng giữa mã rõ ràng và dễ sử dụng xuống dòng. Câu trả lời của Mob là tuyệt vời nhưng tôi nghĩ rằng kết thúc với một mảng không phải là một tham chiếu mảng cuối cùng sẽ làm cho mã dễ dàng hơn để làm theo. – par

+1

Ok, bạn nhận được một upvote cho rằng craziness về phía cuối, ** và ** để phân loại chúng theo chiều dài. =) – TLP

7

shift @{EXPR} là cú pháp hợp lệ, vì vậy

shift @{$interfaces = [qx'ifconfig -s']} 

sẽ cung cấp cho bạn một tài liệu tham khảo mảng có phần tử đầu tiên bị loại bỏ.

tôi phát hiện ra điều này từ diagnostics đầu ra về gọi shift từ danh sách chuyển nhượng:

$ perl -Mdiagnostics -e 'print shift(@a = (2,3,4))' 
 
Type of arg 1 to shift must be array (not list assignment) at -e line 1, at end of line 
Execution of -e aborted due to compilation errors (#1) 
    (F) This function requires the argument in that position to be of a 
    certain type. Arrays must be @NAME or @{EXPR}. Hashes must be 
    %NAME or %{EXPR}. No implicit dereferencing is allowed--use the 
    {EXPR} forms as an explicit dereference. See perlref. 

Perl thực thi Hành vi này trên bất kỳ người dùng định nghĩa chương trình con hoặc BUILTIN được prototyped với \@ hoặc \% nhân vật. Nguyên mẫu là một đầu mối cho trình thông dịch mà Perl nên xử lý một đối số mảng hoặc hàm băm dưới dạng một mảng hoặc kiểu băm, và không cố gắng hủy bỏ danh sách thành nhiều đối số.

Một cách để suy nghĩ về nó (mặc dù tôi không chắc liệu điều này có chính xác đối với nội trang) không Perl sẽ đọc biến mảng hoặc biến băm từ danh sách đối số của bạn cho cuộc gọi hàm nhưng thực sự chuyển tham chiếu cho biến đó với hàm nguyên mẫu. Vì vậy, thông dịch viên cần phải xác định một mảng hoặc băm trong danh sách đối số của bạn, và nó cần để có thể có được một tham chiếu đến mảng đó hoặc băm. Perl không hoặc không thể (tay vẫy ở đây) làm điều đó với một biểu thức gán danh sách - lưu ý rằng kết quả của

\(@a = (1,2,3)) 

là danh sách các tài liệu tham khảo từ 3 đến vô hướng, không phải là một tham chiếu đến một danh sách với 3 vô hướng.

Bạn có thể xem nguyên mẫu (nếu có) đối với hầu hết các Perl builtins với prototype chức năng:

$ perl -e 'print prototype("CORE::shift")'  ===> \@ 
$ perl -e 'print prototype("CORE::each")'  ===> \% 
$ perl -e 'print prototype("CORE::push")'  ===> \@@ 
+0

Tôi đã thử ví dụ đầu tiên bạn đã sử dụng ký hiệu {EXPR} và mảng giao diện bị trống sau khi lệnh rõ ràng sẽ không hoạt động. – par

+1

@par nó sẽ hoạt động. Tuy nhiên, mảng bây giờ sẽ nằm trong '@ $ interfaces', không nằm trong' @ interfaces'. – mercator

+0

'@ $ interfaces' thực tế hoạt động. Tôi hiểu '@ {EXPR}' là một cấu trúc mảng, các dấu ngoặc vuông xung quanh lệnh qx là gì? – par

3

gần nhất tôi có thể nhận được để nhận được này vào một liner:

perl -e '@interfaces = (qx|ifconfig -s|)[1 .. 1000]; print @interfaces' 

Điều này đang lấy một lát từ chỉ mục 1 đến chỉ số 1000 và giả sử đầu ra ifconfig của bạn không quá 1000 dòng. Rõ ràng đây là thực hành lập trình khủng khiếp, nhưng không hoạt động đối với hầu hết các trường hợp và thực hiện những gì câu hỏi yêu cầu.

+0

Một nỗ lực dũng cảm để chắc chắn nhưng bạn nói đúng, từ một quan điểm thực hành lập trình xem ai đó chắc chắn sẽ ném trứng vào tôi. – par

3

Perl có thể không phải là cách dễ nhất (hoặc hầu hết có thể đọc được cách, tôi nên nói) để làm điều đó, nếu bạn muốn giới hạn bản thân chỉ sử dụng một dòng. Dưới đây là một giải pháp mà sửa lệnh shell của bạn thay vì:

@interfaces = qx(ifconfig -s | tail -n +2); 
+0

Một câu trả lời hay khác nhưng không phải là perl-y như tôi muốn;) – par

1

Hãy thử điều này:

shift qw(this that the other); 

Bạn sẽ nhận được thông báo lỗi tương tự. Lệnh shiftphải lấy biến danh sách chứ không phải danh sách. Xét cho cùng, có hai ảnh hưởng lớn với lệnh shift.

  1. Nó trả về giá trị của phần tử đầu tiên của danh sách
  2. Nó cũng loại bỏ giá trị của phần tử đầu tiên từ biến danh sách. Nếu không có biến danh sách, shift sẽ không có ý nghĩa gì.

Trong ví dụ của bạn, (@interfaces = qx 'ifconfig -s') được thiết @interfaces và đang trở lại giá trị của danh sách @interfaces và không biến bản thân để lệnh shift.

Mob's câu trả lời sẽ giúp bạn một chút.Bạn sẽ nhận được một danh sách tham khảo, nhưng sau đó, bạn sẽ phải giữ dereferencing nó, hoặc thiết lập một biến danh sách thực tế:

shift @{$interfaces = [qx'ifconfig -s']} 
foreach my $entry (@{$interfaces}) { #Have to dereference 
    say "Interface: $entry"; 
} 
@interfaces = @{$interfaces};   #This will also work. 

Nếu toàn bộ mục đích là để lưu một số gõ, thiết lập một biến danh sách thực tế từ biến tham chiếu dereferece không lưu bất cứ thứ gì. Và, bằng cách sử dụng một tham chiếu thay vì một danh sách thực tế trong phần còn lại của chương trình của bạn chỉ là sẽ thêm một lớp phức tạp và obfication.

Nếu bạn đang thực sự chỉ là thiết @interfaces không bao gồm phần tử đầu tiên của danh sách trở về, bạn có thể làm một cái gì đó như thế này:

(my $garbage, @interfaces) = qw(ifconfig -s); 

Giá trị đầu tiên của danh sách sẽ được trả lại vào $garbage, một ném biến đi. Phần còn lại của danh sách sẽ được slurped lên bởi @interfaces. Điều này là sạch sẽ, và khá dễ dàng để hiểu những gì đang xảy ra.

Eric Strom bây giờ tôi thấy đã làm tốt hơn nữa:

(undef, @interfaces) = qw(ifconfig -s); 

Bạn thậm chí không có một biến để ném ra ngoài.

Bây giờ tôi sẽ thức suốt đêm lo lắng về những thay đổi mà Perl 5.14 đã thực hiện trong phân tích cú pháp lệnh shift.

2

Bắt đầu với phiên bản Perl 5.10, bạn có thể sử dụng khai báo biến state để quản lý độ bền của biến mà không phải đặt trước với số my ngoài vòng lặp.

use 5.10.0; 
my @interfaces = grep {state $i++} qx'ifconfig -s'; 

có bạn có thể làm điều đó mà không có tiểu bang, nhưng đây là trường hợp sử dụng hoàn hảo cho nó. Đây là mã tương tự mà không có state và cùng từ vựng w.r.t $i.

my @interfaces; 
{ 
    my $i; 
    @interfaces = grep {$i++} qx'ifconfig -s'; 
} 

hoặc

my @interfaces = do { my $i; grep {$i++} qx'ifconfig -s' }; 

tất nhiên bạn không thể quan tâm đến $i 's lexicality và chỉ cần làm

my $i; 
my @interfaces = grep {$i++} qx'ifconfig -s'; 

hoặc bạn có thể lừa gạt

my @interfaces = grep {$|++} qx'ifconfig -s' 

nhưng điều đó phá vỡ nếu bạn dựa vào $| ở một nơi khác. Đừng bận tâm, chỉ cần sử dụng state.

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