2010-11-01 9 views
12

Quindi questo mi ha fatto impazzire per l'ultima mezz'ora. C'è un modo per me di prendere una fetta di matrice alla fine di un array anonimo? Ho provato:

(split(' ',$test_line))[1..$#_]

e ho provato: (split(' ',$test_line))[1..-1]

ma aggravatingly, nessuno di questi lavori. Davvero non voglio avere un'ulteriore variabile temporanea istanziata per l'array intermedio (che non ho bisogno). E davvero non voglio usare un rivestimento brutto e illeggibile (ne ho trovati alcuni online). Non c'è davvero un modo semplice per farlo?C'è qualche modo per prendere una fetta alla fine di un array anonimo in Perl?

+2

ho il sospetto la migliore risposta sarà l'extra variabile temporanea. Sulla prima riga, il motivo '$ # _' non funziona è che '@ _' non è la lista dall'espressione. – aschepler

risposta

15

Un elenco, che è quello che hai nel tuo esempio, non può essere affettato alla fine. Ciò è principalmente dovuto al fatto che le liste non sono strutture dati appropriate in Perl, ma più un costrutto che l'interprete usa per spostare i dati. Quindi, sapendo che puoi solo dividere un elenco dall'inizio, le tue opzioni sono o metterlo in una variabile di matrice e poi tagliare, cambiare l'algoritmo per restituire quello che vuoi, o il seguente:

Se stai assegnando questo il valore a qualcosa, è possibile utilizzare undef in ogni slot non volete:

my (undef, @list) = split ' ' => $test_line; 

Se pubblichi un po 'di codice, posso rivedere.

In alternativa, è possibile utilizzare alcuni strumenti dalla programmazione funzionale. La coppia di funzioni drop e take può essere utile per ridimensionare una lista senza ulteriori variabili:

sub take { 
    my $n = shift; 
    @_[0..$n-1] 
} 
sub drop { 
    my $n = shift; 
    @_[$n..$#_] 
} 

e quindi il vostro esempio diventa

drop 1, split ' ' => $test_line; 

drop 1 è anche comunemente chiamato tail

sub tail {drop 1, @_} 

e, naturalmente, dal momento che tutti questi sono così brevi, se si vuole in linea:

sub {shift; @_}->(split ' ' => ...) 
+0

La mia idea è sulla falsariga di: my% test_hash = (split ('', $ test_line)) [1 .. $ # _]; In questo caso particolare, il tuo approccio funziona bene, ma speravo in qualcosa di un po 'più generico, così che quando non desidero i primi 10 elementi, non sarò bloccato a scrivere undef 10 volte ... – Eli

+0

@Eli => purtroppo non è una funzionalità delle sezioni di lista in perl5. La soluzione generica è qualcosa come 'drop' o' take'. A seconda del contenuto, potresti anche essere in grado di utilizzare grep per rimuovere gli elementi che non desideri. –

+3

Bella risposta. Ci * sono * soluzioni one-liner, ma in realtà non appartengono al codice di produzione. – tchrist

2

Non credo è possibile specificare un indice per l'ultimo elemento di un'espressione elenco arbitrario, ma che ne dici:

split(' ', (split ' ', $test_line, 2)[1]) 

A proposito, non ci sono schiere anonime qui (o nella tua domanda originale), solo elenchi.

+0

Non è il più bello, chiama 'split' due volte. Puoi anche usare 'split" "=> ($ test_line = ~ /\s+(.*)/s && $ 1)', ma non lo difenderei. – tchrist

+0

Forse non è il più bello, ma almeno è breve e relativamente semplice. – Sean

6

Quando l'OP ha detto fetta, ho pensato di splice:

@allTheWordsExceptTheFirstTwo = splice @{[split' ', $test_line]}, 2; 
@allExceptTheFirstAndLastTwo = splice @{[split' ', $test_line]}, 2, -2; 
0

se siete aperti alla scissione due volte:

my @g = (split ' ', $test_str)[1..split(' ', $test_str)]; 

o più correttamente (in quanto diviso restituisce il numero di campi trovati (uno in più rispetto all'ultimo indice del campo poiché è a base 0):

my @g = (split ' ', $test_str)[1..split(' ', $test_str)-1]; 

sfortunatamente questi lanciano un avviso deprecato sotto il pragma "warnings" e nascondono il contenuto di @_ (a meno che non stiate usando 5.12, quindi sei bravo, altrimenti vai con una variabile temporanea, inline sub o un ciclo).

3

È possibile utilizzare intervalli negativi nel indice di matrice per risolvere un numero arbitrario di elementi dalla fine:

my $x = join ' ' => 'a' .. 'z'; 
my @x = (split ' ', $x)[-13 .. -1]; 

Tuttavia, ciò richiede di conoscere il numero totale di elementi nel risultato di split eliminare solo il primo elemento.

Se questo accade in un solo luogo, utilizzando un blocco do dovrebbe funzionare:

my $x = join ' ', 'a' .. 'z'; 
my @x = do { my @y = (split ' ', $x); @y[1 .. $#y] }; 

Nel tuo caso, vorrei scomporre l'intera operazione ad una subroutine se si suppone per essere utilizzato di frequente, passando la stringa piuttosto che il risultato del split alla subroutine (può essere ulteriormente generalizzato consentendo all'utente di passare il modello di divisione così:

my $x = join ' ', 'a' .. 'g'; 
my @x = skip_first_n_from_split(3, $x); 

print Dump \@x; 

sub skip_first_n_from_split { 
    my ($n, $x) = @_; 
    my @y = split ' ', $x; 
    return @y[$n .. $#y]; 
} 

Having fun:

#!/usr/bin/perl 

use strict; use warnings; 

my $x = join ' ', 1 .. 8; 
my @skippers = map make_skipper(' ', $_), 0 .. 7; 

print "@$_\n" for map $_->($x), @skippers; 

sub make_skipper { 
    my ($pattern, $n) = @_; 

    return sub { 
     my $string = shift; 
     my $i = 0; 
     return [ grep $i++ >= $n, split $pattern, $string ]; 
    } 
} 

uscita:

1 2 3 4 5 6 7 8 
2 3 4 5 6 7 8 
3 4 5 6 7 8 
4 5 6 7 8 
5 6 7 8 
6 7 8 
7 8 
8
2

Questo appena ricevuto answered 'ordinatamente' sul PerlMonks da BrowserUK, questo è quello che ci fa:

my @slice = sub{ @_[1..$#_] }->(split ' ', $test_line); 
Problemi correlati