Cosa significa “0 ma true” in Perl?

Qualcuno può spiegare cosa significa esattamente la stringa “0 ma true” in Perl? Per quanto ho capito, è uguale a zero in un confronto tra interi, ma viene valutato come true se usato come booleano. È corretto? È un comportamento normale della lingua o è una stringa speciale trattata come un caso speciale nell’interprete?

È un comportamento normale della lingua. Citando la manpage di perlsyn :

Il numero 0 , le stringhe '0' e "" , la lista vuota () e undef sono tutti falsi in un contesto booleano. Tutti gli altri valori sono veri. Negazione di un valore reale di ! o not restituisce un valore falso speciale. Quando viene valutato come una stringa, viene trattato come "" , ma come numero viene considerato come 0 .

Per questo motivo, ci deve essere un modo per restituire 0 da una chiamata di sistema che si aspetta di restituire 0 come valore di ritorno (riuscito) e lasciare un modo per segnalare un caso di errore restituendo effettivamente un valore falso. "0 but true" serve a questo scopo.

Perché è hardcoded nel core Perl per trattarlo come un numero. Questo è un trucco per far funzionare le convenzioni di Perl e le convenzioni di ioctl ; da perldoc -f ioctl :

Il valore di ritorno di ioctl (e fcntl ) è il seguente:

 if OS returns: then Perl returns: -1 undefined value 0 string "0 but true" anything else that number 

Quindi Perl restituisce true in caso di successo e false in caso di fallimento, tuttavia è ancora ansible determinare facilmente il valore effettivo restituito dal sistema operativo:

 $retval = ioctl(...) || -1; printf "System returned %d\n", $retval; 

La stringa speciale "0 but true" è esente da -w reclami relativi a conversioni numeriche improprie.

In aggiunta a ciò che altri hanno detto, "0 but true" è un caso speciale in quanto non avverte in contesto numerico:

 $ perl -wle 'print "0 but true" + 3' 3 $ perl -wle 'print "0 but crazy" + 3' Argument "0 but crazy" isn't numeric in addition (+) at -e line 1. 3 

Il valore 0 but true è un caso speciale in Perl. Anche se ai tuoi semplici occhi mortali, non sembra un numero, saggio e sapiente, Perl capisce che è davvero un numero.

Ha a che fare con il fatto che quando una subroutine Perl restituisce un valore 0, si presume che la routine abbia fallito o restituito un valore falso.

Immagina di avere una subroutine che restituisce la sum di due numeri:

 die "You can only add two numbers\n" if (not add(3, -2)); die "You can only add two numbers\n" if (not add("cow", "dog")); die "You can only add two numbers\n" if (not add(3, -3)); 

La prima istruzione non morirà perché la subroutine restituirà un 1 . Quello è buono. La seconda affermazione morirà perché la subroutine non sarà in grado di aggiungere la mucca al cane .

E la terza affermazione?

Hmmm, posso aggiungere 3 a -3 . Ho appena ottenuto 0 , ma il mio programma morirà anche se la subroutine add funzionato!

Per aggirare questo, Perl considera 0 but true per essere un numero. Se la mia subroutine di aggiunta non restituisce solo 0 , ma 0 ma vero , la mia terza istruzione funzionerà.

Ma è 0 ma vero zero numerico? Prova questi:

 my $value = "0 but true"; print qq(Add 1,000,000 to it: ) . (1_000_000 + $value) . "\n"; print "Multiply it by 1,000,000: " . 1_000_000 * $value . "\n"; 

Sì, è zero!

La subroutine dell’indice è un pezzo molto vecchio di Perl ed esisteva prima del concetto di 0 ma il vero era in circolazione. Si supponga di restituire la posizione della sottostringa che si trova nella stringa:

 index("barfoo", "foo"); #This returns 3 index("barfoo", "bar"); #This returns 0 index("barfoo", "fu"); #This returns ...uh... 

L’ultima statistica restituisce -1 . Il che significa se l’ho fatto:

 if ($position = index($string, $substring)) { print "It worked!\n"; } else { print "If failed!\n"; } 

Come faccio normalmente con le funzioni standard, non funzionerebbe. Se avessi usato “barfoo” e “bar” come facevo nella seconda istruzione, la clausola else sarebbe stata eseguita, ma se avessi usato “barfoo” e “fu” come nel terzo, la clausola if sarebbe stata eseguita. Non quello che voglio.

Tuttavia, se la subroutine index restituito 0 ma true per la seconda istruzione e undef per la terza istruzione, la mia clausola if / else avrebbe funzionato.

Si può anche vedere la stringa “0E0” usata nel codice Perl e significa la stessa cosa, dove 0E0 significa semplicemente 0 scritto in notazione esponenziale. Tuttavia, poiché Perl considera solo “0”, “” o undef come falso, valuta true in un contesto booleano.

È hardcoded nel codice sorgente di Perl, in particolare in Perl_grok_number_flags in numeric.c .

Leggendo quel codice ho scoperto che la stringa “infinity” (case insensitive) passa anche il test looks_like_number. Non lo sapevo.

In un contesto intero, viene valutato a 0 (la parte numerica all’inizio della stringa) ed è zero. In un contesto scalare, è un valore non vuoto, quindi è vero.

  • if (int("0 but true")) { print "zero"; }

    (nessuna uscita)

  • if ("0 but true") { print "true"; }

    (stampa vera)

0 significa falso in Perl (e in altri linguaggi correlati a C). Per la maggior parte, è un comportamento ragionevole. Altre lingue (Lua per esempio) considerano 0 come vero e forniscono un altro token (spesso nullo o falso ) per rappresentare un valore non vero.

Un caso in cui il modo Perl non funziona così bene è quando si desidera restituire un numero o, se la funzione non riesce per qualche motivo, un valore falso. Ad esempio, se si scrive una funzione che legge una riga da un file e restituisce il numero di caratteri sulla linea. Un uso comune della funzione potrebbe essere qualcosa come:

 while($c = characters_in_line($file)){ ... }; 

Si noti che se il numero di caratteri su una linea specifica è 0, il ciclo while termina prima della fine del file. Quindi la funzione characters_in_line dovrebbe contenere caratteri maiuscoli 0 e restituire ‘0 ma true’ invece. In questo modo la funzione funzionerà come previsto nel ciclo while , ma restituirà anche la risposta corretta se dovesse essere usata come un numero.

Si noti che questa non è una parte incorporata della lingua. Piuttosto, sfrutta l’abilità di Perl di interpretare una stringa come un numero. Quindi a volte vengono usate altre punture. DBI usa “0E0” , per esempio. Quando vengono valutati in contesto numerico, restituiscono 0 , ma in contesto booleano, false .

Cose che sono false:

  • "" .
  • "0" .
  • Cose che si uniscono a quelle.

"0 but true" non è uno di quelli, quindi non è falso.

Inoltre, Perl restituisce "0 but true" dove è previsto un numero per segnalare che una funzione ha avuto successo anche se ha restituito zero. sysseek è un esempio di tale funzione. Poiché il valore dovrebbe essere usato come un numero, Perl è codificato in modo da considerarlo un numero. Di conseguenza, non viene emesso alcun avviso quando viene utilizzato come numero e looks_like_number("0 but true") restituisce true.

Altri “veri zero” possono essere trovati su http://www.perlmonks.org/?node_id=464548 .

Un altro esempio di “0 ma vero”:

Il modulo DBI utilizza “0E0” come valore di ritorno per le query UPDATE o DELETE che non hanno interessato alcun record. Viene valutato su true in un contesto booleano (che indica che la query è stata eseguita correttamente) e su 0 in un contesto numerico che indica che nessun record è stato modificato dalla query.

Ho appena trovato la prova che la stringa “0 ma true” è effettivamente integrata nell’interprete, come alcune persone hanno già risposto:

 $ strings /usr/lib/perl5/5.10.0/linux/CORE/libperl.so | grep -i true Perl_sv_true %-p did not return a true value 0 but true 0 but true 

“0 ma true” è una stringa come qualsiasi altra, ma a causa della syntax di perl può servire a uno scopo utile, ovvero restituire zero intero da una funzione senza che il risultato sia “falso” (negli occhi di Perl).

E la stringa non deve essere “0 ma vera”. “0 ma false” è ancora “vero” nel senso booleano.

prendere in considerazione:

 if(x) for x: yields: 1 -> true 0 -> false -1 -> true "true" -> true "false" -> true "0 but true" -> true int("0 but true") ->false 

Il risultato di tutto questo è che puoi avere:

 sub find_x() 

e avere questo codice essere in grado di stampare “0” come output:

 if($x = find_x) { print int($x) . "\n"; } 

Quando si desidera scrivere una funzione che restituisce un valore intero o falso o undef (cioè per il caso di errore), è necessario prestare attenzione al valore zero. Restituirlo è falso e non dovrebbe indicare la condizione di errore, quindi restituire “0 ma true” rende il valore restituito dalla funzione true mentre ancora restituisce il valore zero quando la matematica viene eseguita su di esso.

La stringa “ 0 ma true ” è ancora un caso speciale:

 for arg in "'0 but true'" "1.0*('0 but true')" \ "1.0*('0 but false')" 0 1 "''" "0.0" \ "'false'" "'Ja'" "'Nein'" "'Oui'" \ "'Non'" "'Yes'" "'No'" ;do printf "%-32s: %s\n" "$arg" "$( perl -we ' my $ans=eval $ARGV[0]; $ans=~s/^(Non?|Nein)$//; if ($ans) { printf "true: |%s|\n",$ans } else { printf "false: |%s|", $ans };' "$arg" )" done 

dare il seguente: (notare il “ warning ”!)

 '0 but true' : true: |0 but true| 1.0*('0 but true') : false: |0| Argument "0 but false" isn't numeric in multiplication (*) at (eval 1) line 1. 1.0*('0 but false') : false: |0| 0 : false: |0| 1 : true: |1| '' : false: || 0.0 : false: |0| 'false' : true: |false| 'Ja' : true: |Ja| 'Nein' : false: || 'Oui' : true: |Oui| 'Non' : false: || 'Yes' : true: |Yes| 'No' : false: || 

… e non dimenticare di RTFM!

 man -P'less +"/0 but [az]*"' perlfunc ... "fcntl". Like "ioctl", it maps a 0 return from the system call into "0 but true" in Perl. This string is true in boolean context and 0 in numeric context. It is also exempt from the normal -w warnings on improper numeric conversions. ...