2009-08-31 14 views
12

Mi piacerebbe eseguire alcune variazioni contorte del compito @a = @b || @c, con l'intento di prendere @b se non vuoto (quindi vero in senso booleano), @c altrimenti. La documentazione mi dice esplicitamente che non posso. (Ed è proprio per il fatto, troppo!)Perché non posso assegnare @b || da @c a @a in Perl?

Il "||", "//" e "& &" operatori restituiscono l'ultimo valore valutato (a differenza di C "||" e "& & ", che restituisce 0 o 1).

[...]

In particolare, ciò significa che non si dovrebbe usare questo per la selezione tra due aggregati per l'assegnazione:

@a = @b || @c;    # this is wrong 
@a = scalar(@b) || @c;  # really meant this 
@a = @b ? @b : @c;   # this works fine, though 

Purtroppo, in realtà non Dimmi perchè.

Quello che mi aspettavo che sarebbe successo era questo:

  • @a = è un compito array, inducendo un contesto di lista sul lato destro della strada.
  • @b || @c è il lato destro, da valutare nel contesto dell'elenco.
  • || è di tipo C in cortocircuito logico o. Valuta da sinistra a destra (se necessario) e propaga il contesto.
  • @b viene valutato nel contesto dell'elenco. Se true (, ovvero, non vuoto), viene restituito.
  • in caso contrario, @c viene valutato anche nel contesto dell'elenco e restituito.

Ovviamente, la mia penultima affermazione è errata. Perché? E, cosa più importante, quale parte della documentazione o delle fonti spiega questo comportamento?

PS: fuori dal campo di applicazione della domanda, il motivo per cui mi astengo dal suggerire la documentazione di utilizzare l'operatore ternario è che il mio @b è in realtà un temporaneo (un risultato di chiamata di funzione).

+2

La riga che dice "intendeva veramente questo" ti dice perché. –

+0

È più che un motivo per me. Ma sono di parte. –

+1

Penso piuttosto che il commento "intendeva veramente questo" dovrebbe essere cambiato in "davvero significa questo". In questo modo, mi aspettavo che il cambiamento scalare() impostasse @a uguale a @b - cioè che fosse quello che l'AUTORE realmente DEVE usare - che ovviamente non è il caso. Invece, la prima riga in realtà significa la seconda linea; entrambi ottengono lo stesso risultato, che non è la risposta desiderata. – Rini

risposta

7

L'operatore logico o ("||") valuta l'argomento della mano sinistra nel contesto scalare.

Il motivo per cui questo è capire se l'argomento è vero. Il contesto booleano, essendo un caso speciale di contesto scalare, lo costringe in un contesto scalare.


Da perldoc perlop"C-style-Logical-Or"

binaria "||" esegue un corto circuito OR logico. Cioè, se l'operando di sinistra è true, l'operando di destra non viene nemmeno valutato. ...


Da perldoc perldata"Scalar values":

.... Il contesto booleano è solo un particolare tipo di scalare contesto in cui non viene convertito in una stringa o un numero viene mai eseguita.

+0

Hai la risposta che stavo cercando. Ho trovato questo in perldata: "Il contesto booleano è solo un tipo speciale di contesto scalare in cui non viene mai eseguita alcuna conversione a una stringa oa un numero." Anche se la ragione ("ha bisogno di capire se è vera") mi sembra un po 'discutibile. –

+0

I valori possono essere vere o false, ma le aggregazioni non possono. Per gli aggregati "true" di solito significa "non vuoto". Quando si valuta un aggregato in un contesto scalare si ottiene un valore falso se l'aggregato è vuoto e vero altrimenti. –

+0

@ Michael: anche se quello che dici concorda con le spiegazioni tutt'intorno, mi sembra sia un po 'magico (cioè non spiegato) e contraddittorio al paragrafo di perlsyn sulla verità/falsità, che dice principalmente: "la lista vuota è falsa, qualsiasi cosa altrimenti è vero ". Le informazioni rilevanti di cui avevo bisogno per accettare il fatto sono state portate da Brad: "la valutazione booleana costringe a scalare". Deduzione non banale da perlsyn, IMO. –

7

In perlop, solo pochi paragrafi prima della parte che citano:

 
Binary "||" performs a short-circuit logical OR operation. That is, 
if the left operand is true, the right operand is not even evaluated. 
Scalar or list context propagates down to the right operand if it is 
evaluated. 

Questo non afferma esplicitamente che un contesto di lista non si propaga per l'operando sinistro, ma la parte superiore degli stati perlop:

 
With very few exceptions, these all operate on scalar values 
only, not array values. 

così si può supporre che un contesto di lista di moltiplicazione per l'operando di destra è l'eccezione alla regola , e la mancanza di qualsiasi dichiarazione circa il contesto del sinistra l'operando implica che si applichi la regola generale.

2

È perché || valuta il lato sinistro in contesto scalare, come fa l'argomento del test di?:. Se non è possibile utilizzare il ternario, utilizzare una funzione:

sub or_array (\@\@) { 
    return @{$_[0]} if (scalar @{$_[0]}); 
    return @{$_[1]}; 
} 

@a = or_array(@b, @c); 
+0

Indubbiamente c'è un operatore criptico in perl6 che si occupa di questo. :) – Ether

0

Se non è possibile utilizzare direttamente l'operatore condizionale, si può facilmente utilizzare il leggermente meno laconico:

my $ref_b = [ @b ]; # Ideally, just return an arrayref from your function 
my @a = @$ref_b ? @$ref_b : @c; 

Come sopra, il codice non funziona poiché il contesto logico in cui viene valutato il lato sinistro di || è un contesto scalare e pertanto @b diventa effettivamente scalar(@b) ed è ciò che viene assegnato a @a.

Problemi correlati