2009-08-31 13 views
12

Chciałbym wykonać pewne zawiłe wariacje zadania @a = @b || @c, z zamiarem podjęcia @b, jeśli nie jest pusty (stąd prawda w sensie boolean), @c w przeciwnym razie. Dokumentacja wyraźnie mówi mi, że nie mogę. (I to jest prawda o tym, też!)Dlaczego nie mogę przypisać @b || @ c do @a w Perlu?

W "||", "//" i "& &" podmioty zwróci ostatnią wartość oceniana (w przeciwieństwie do C w "||" i "& & ", które zwracają 0 lub 1).

[...]

W szczególności oznacza to, że nie należy korzystać z tego wyboru między dwa agregaty do przypisania:

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

Niestety, to nie naprawdę robi Powiedz mi dlaczego.

Czego spodziewać by się stało, było to:

  • @a = jest przypisanie tablicy, wywołując kontekst listy po prawej stronie.
  • @b || @c to prawa strona, do oceny w kontekście listy.
  • || jest logicznym lub logicznym zwarciem w stylu C. Ocenia od lewej do prawej (w razie potrzeby) i propaguje kontekst.
  • @b jest oceniany w kontekście listy. Jeśli wartość true (, tj., niepusta), jest zwracana.
  • jeśli nie, @c jest również obliczany w kontekście listowym i zwracany.

Oczywiście, moje przedostatnie stwierdzenie jest błędne. Czemu? I, co ważniejsze, która część dokumentacji lub źródeł uwzględnia to zachowanie?

PS: poza zakresem pytania, powód, dla którego rezygnuję z sugestii dokumentacji dotyczącej korzystania z operatora trójargumentowego, jest taki, że mój @b jest rzeczywiście tymczasowy (wynik wywołania funkcji).

+2

Linia, która mówi "naprawdę znaczy to", wyjaśnia dlaczego. –

+0

To więcej niż dlaczego dla mnie. Ale jestem stronniczy. –

+1

Raczej uważam, że komentarz "naprawdę tak" powinien zostać zmieniony na "naprawdę znaczy to". Tak jak było, spodziewałem się, że zmiana skalarna() zmieni się na @a równą @b - czyli tym, co autor naprawdę MUSI użyć - co oczywiście nie jest prawdą. Zamiast tego pierwsza linia naprawdę oznacza drugą linię; obaj uzyskują taki sam wynik, co nie jest pożądaną odpowiedzią. – Rini

Odpowiedz

7

Operator logiczny lub "(||") analizuje lewy argument w kontekście skalarnym.

Powodem tego jest ustalenie, czy argument jest prawdziwy. Kontekst Boolean, będący szczególnym przypadkiem kontekstu skalarnego, wymusza na nim kontekst skalarny.


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

Binary "||" dokonuje logicznej lub operacji. Oznacza to, że jeśli lewy operand jest true, prawy operand nie zostanie nawet oceniony. ...


Od perldoc perldata"Scalar values":

.... Boolean kontekstu jest tylko szczególnym rodzajem skalarnego kontekście, w którym żadna konwersja na łańcuch lub numer kiedykolwiek przeprowadzono.

+0

Masz odpowiedź, której szukałem. Znalazłem to w perldata: "Kontekst Boolean jest po prostu szczególnym rodzajem kontekstu skalarnego, w którym nigdy nie jest dokonywana żadna konwersja ciągu lub liczby". Chociaż powód ("musi się dowiedzieć, czy to prawda") wydaje mi się nieco wątpliwy. –

+0

Wartości mogą być prawdziwe lub fałszywe, ale agregaty nie mogą. Dla agregatów "prawda" zwykle oznacza "niepusty". Kiedy ocenisz zagregowany w kontekście skalarnym, otrzymasz wartość, która jest fałszem, jeśli agregacja jest pusta i prawda w przeciwnym razie. –

+0

@Michael: chociaż to, co mówisz, zgadza się z wyjaśnieniami dookoła, wydaje mi się, że jest ono trochę magiczne (tj. Niewyjaśnione) i sprzeczne z paragrafem perlsyn na temat prawdy/fałszu, który głosi najczęściej: "pusta lista jest nieprawdziwa, cokolwiek inaczej jest prawdą ". Odpowiednie informacje, które musiałem zaakceptować, zostały przytoczone przez Brada: "Boarowska ocena wymusza na skalar". Nietrudna dedukcja od perlsyn, IMO. –

7

W perlop, zaledwie kilka akapitów wcześniej sekcji cytujesz:

 
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. 

nie wyraźnie stwierdzić, że kontekst lista nie rozprzestrzeniać do lewego argumentu, ale szczyt państw perlop:

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

więc możemy założyć, że lista kontekst rozmnożeniowego do prawego operandu jest wyjątek od reguły, a brak jakiegokolwiek oświadczenia o kontekście z lewej operand oznacza, że ​​obowiązuje zasada ogólna.

2

To dlatego, że || ocenia lewą stronę w kontekście skalarnym, podobnie jak argument testowy z?:. Jeśli nie można użyć trójskładnikowych użyć funkcji:

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

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

Nie ma wątpliwości, że w perl6 jest tajemniczy operator, który się tym zajmuje. :) – Ether

0

Jeśli nie można użyć operatora warunkowego bezpośrednio, można łatwo użyć nieco mniej-lakoniczny:

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

Zgodnie z powyższą odpowiedzią Twój kod nie działa, ponieważ kontekst logiczny, w którym jest oceniana lewa strona ||, jest kontekstem skalarnym, a zatem @b faktycznie staje się scalar(@b) i to właśnie zostaje przypisane do @a.

Powiązane problemy