Jakie są korzyści z tego, że operator przypisania zwraca wartość?

27

Tworzę język, który zamierzam zastąpić zarówno JavaScript, jak i PHP. (Nie widzę w tym żadnego problemu. To nie tak, że żaden z tych języków ma dużą bazę instalacyjną.)

Jedną z rzeczy, które chciałem zmienić, było przekształcenie operatora przypisania w polecenie przypisania, usuwając możliwość korzystania ze zwróconej wartości.

x=1;          /* Assignment. */
if (x==1) {}  /* Comparison. */
x==1;         /* Error or warning, I've not decided yet. */
if (x=1) {}   /* Error. */

Wiem, że oznaczałoby to, że funkcje jednowierszowe, które ludzie C tak bardzo kochają, przestałyby działać. Doszedłem do wniosku (z niewielkimi dowodami poza moim osobistym doświadczeniem), że w zdecydowanej większości przypadków tak się działo, to naprawdę była to operacja porównania.

Albo to jest? Czy istnieją praktyczne zastosowania wartości zwracanej przez operatora przypisania, których nie można w prosty sposób przepisać? (Dla każdego języka, który ma taką koncepcję).

billpg
źródło
12
JS i PHP nie mają dużej „bazy instalacyjnej”?
mrs
47
@mri Podejrzewam sarkazm.
Andy Hunt
12
Jedynym przydatnym przypadkiem, jaki pamiętam, jest while((x = getValue()) != null) {}. Zamienniki będą brzydsze, ponieważ będziesz musiał albo użyć breakalbo powtórzyć x = getValuezadanie.
CodesInChaos
12
@mri Ooh nie, słyszę, że te dwa języki to tylko trywialne rzeczy bez żadnych znaczących inwestycji. Gdy tylko kilka osób, które nalegają na używanie JS, zobaczy mój język, przełączy się na mój i nigdy więcej nie będzie musiał pisać ===. Jestem równie pewien, że twórcy przeglądarek natychmiast wprowadzą aktualizację, która zawiera mój język obok JS. :)
billpg
4
Proponuję wam, aby jeśli zamierzaliście ulepszyć istniejący język i zamierzacie go przyjąć na szeroką skalę, to 100% wsteczna kompatybilność z istniejącym językiem jest dobra. Zobacz TypeScript jako przykład. Jeśli Twoim celem jest zapewnienie lepszej alternatywy dla istniejącego języka, masz o wiele trudniejszy problem. Nowy język musi rozwiązać istniejący realistyczny problem znacznie lepiej niż istniejący język, aby pokryć koszty zmiany. Nauka języka jest inwestycją i musi się opłacić.
Eric Lippert,

Odpowiedzi:

25

Technicznie rzecz biorąc, pewien cukier syntaktyczny może być wart zachowania, nawet jeśli można go w prosty sposób zastąpić, jeśli poprawi on czytelność niektórych typowych operacji. Ale przypisanie jako wyrażenie nie podlega temu. Niebezpieczeństwo literowania go zamiast porównania oznacza, że ​​jest on rzadko używany (czasem nawet zabroniony przez przewodniki po stylach) i wywołuje podwójne ujęcie przy każdym użyciu. Innymi słowy, korzyści związane z czytelnością są niewielkie i niewielkie.

Warto spojrzeć na istniejące języki, które to robią.

  • Java i C # zachowują przypisanie wyrażenia, ale usuwają pułapkę, o której wspomniałeś, wymagając warunków do oceny wartości logicznych. Wydaje się, że to działa dobrze, chociaż ludzie czasami narzekają, że uniemożliwia to warunki takie jak if (x)zamiast if (x != null)lub w if (x != 0)zależności od rodzaju x.
  • Python sprawia, że ​​przypisanie jest prawidłową instrukcją zamiast wyrażenia. Propozycje zmiany tego czasami trafiają na listę mailingową python-ideas, ale moje subiektywne wrażenie jest takie, że zdarza się to rzadziej i generuje mniej hałasu za każdym razem w porównaniu do innych „brakujących” funkcji, takich jak pętle „do-while”, instrukcje przełączania, lambda wieloliniowe, itp.

Jednakże, Python umożliwia jeden szczególny przypadek, przypisanie do wielu imion na raz: a = b = c. Jest to uważane za wyrażenie równoważne b = c; a = bi jest czasami używane, więc warto dodać je również do twojego języka (ale nie przejmowałbym się nim, ponieważ ten dodatek powinien być kompatybilny wstecz).


źródło
5
+1 za a = b = cwywołanie, którego inne odpowiedzi tak naprawdę nie wywołują .
Leo
6
Trzecią rozdzielczością jest użycie innego symbolu do przypisania. Pascal używa :=do przypisania.
Brian
5
@Brian: Rzeczywiście, podobnie jak C #. =to zadanie, ==to porównanie.
Marjan Venema
3
W języku C # coś w rodzaju if (a = true)spowoduje wyświetlenie ostrzeżenia C4706 ( The test value in a conditional expression was the result of an assignment.). GCC z C również rzuci a warning: suggest parentheses around assignment used as truth value [-Wparentheses]. Ostrzeżenia te można wyciszyć za pomocą dodatkowego zestawu nawiasów, ale służą one wyraźnemu wskazaniu, że przypisanie było zamierzone.
Bob
2
@delnan Tylko nieco rodzajowy komentarz, ale zapoczątkowały „usunąć pułapki można wymienić wymagając warunki do oceny do wartości logiczne” - a = true ma ocenić do Boolean, a zatem nie jest błędem, ale także podnosi powiązany ostrzeżenie w języku C #.
Bob
11

Czy istnieją praktyczne zastosowania wartości zwracanej przez operatora przypisania, których nie można w prosty sposób przepisać?

Ogólnie rzecz biorąc, nie. Pomysł, aby wartość wyrażenia przypisania była wartością, która została przypisana, oznacza, że ​​mamy wyrażenie, które może być użyte zarówno ze względu na efekt uboczny, jak i jego wartość , i przez wielu jest to mylące.

Typowe zastosowania to zazwyczaj kompaktowe wyrażenia:

x = y = z;

ma semantykę w C # z „konwersja z na typ y, przypisanie przekonwertowanej wartości do y, przekonwertowana wartość jest wartością wyrażenia, konwersja na typ x, przypisanie do x”.

Ale jesteśmy już w królestwie imperatywnych skutków ubocznych w kontekście oświadczeń, więc naprawdę bardzo mało przekonujących korzyści z tego

y = z;
x = y;

Podobnie z

M(x = 123);

być skrótem od

x = 123;
M(x);

Ponownie w oryginalnym kodzie używamy wyrażenia zarówno dla jego skutków ubocznych, jak i dla jego wartości, i robimy oświadczenie, które ma dwa skutki uboczne zamiast jednego. Oba są śmierdzące; staraj się mieć jeden efekt uboczny na instrukcję i używaj wyrażeń dla ich wartości, a nie dla efektów ubocznych.

Tworzę język, który zamierzam zastąpić zarówno JavaScript, jak i PHP.

Jeśli naprawdę chcesz być odważny i podkreślać, że przypisanie jest stwierdzeniem, a nie równością, moja rada brzmi: wyraź wyraźnie przypisanie .

let x be 1;

Ten czerwony. Lub

x <-- 1;

lub nawet lepiej:

1 --> x;

A jeszcze lepiej

1 → x;

Absolutnie nie ma mowy, aby którykolwiek z nich mógł zostać pomylony x == 1.

Eric Lippert
źródło
1
Czy świat jest gotowy na symbole Unicode inne niż ASCII w językach programowania?
billpg
Tak bardzo, jak chciałbym, co sugerujesz, jednym z moich celów jest to, że większość „dobrze napisanych” skryptów JavaScript można przenosić z niewielkimi modyfikacjami lub bez nich.
billpg
2
@billpg: Czy świat jest gotowy ? Nie wiem - czy świat był gotowy na APL w 1964 roku, dekady przed wynalezieniem Unicode? Oto program w APL, który wybiera losową kombinację sześciu liczb z pierwszych 40: x[⍋x←6?40] APL wymagało własnej specjalnej klawiatury, ale był to całkiem udany język.
Eric Lippert
@billpg: Macintosh Programmer's Workshop używał symboli spoza ASCII do takich rzeczy jak tagi regularne lub przekierowanie stderr. Z drugiej strony MPW miało tę zaletę, że Macintosh ułatwiał pisanie znaków spoza ASCII. Muszę wyznać zdziwienie, dlaczego amerykański sterownik klawiatury nie zapewnia żadnego przyzwoitego sposobu wpisywania znaków spoza ASCII. Wpisywanie Alt-number wymaga nie tylko wyszukiwania kodów znaków - w wielu aplikacjach nawet nie działa.
supercat
Hm, dlaczego wolisz przypisywać „na prawo” jak a+b*c --> x? To wydaje mi się dziwne.
Ruslan
9

Wiele języków wybiera drogę przypisywania instrukcji zamiast wyrażenia, w tym Python:

foo = 42 # works
if foo = 42: print "hi" # dies
bar(foo = 42) # keyword arg

i Golang:

var foo int
foo = 42 # works
if foo = 42 { fmt.Printn("hi") } # dies

Inne języki nie mają przypisania, ale raczej powiązania o zasięgu, np. OCaml:

let foo = 42 in
  if foo = 42 then
    print_string "hi"

Jednak letto samo wyrażenie.

Zaletą zezwolenia na przypisanie jest to, że możemy bezpośrednio sprawdzić wartość zwracaną funkcji w warunku, np. W tym fragmencie Perla:

if (my $result = some_computation()) {
  say "We succeeded, and the result is $result";
}
else {
  warn "Failed with $result";
}

Perl dodatkowo ogranicza deklarację tylko do tego warunku, co czyni go bardzo przydatnym. Ostrzeże również, jeśli przypiszesz wewnątrz warunku bez zadeklarowania nowej zmiennej - if ($foo = $bar)ostrzeże, if (my $foo = $bar)nie zrobi tego.

Wykonanie przypisania w innej instrukcji jest zwykle wystarczające, ale może powodować problemy z określaniem zakresu:

my $result = some_computation()
if ($result) {
  say "We succeeded, and the result is $result";
}
else {
  warn "Failed with $result";
}
# $result is still visible here - eek!

Golang w dużym stopniu opiera się na wartościach zwracanych przy sprawdzaniu błędów. W związku z tym umożliwia warunkowe przyjęcie instrukcji inicjalizacji:

if result, err := some_computation(); err != nil {
  fmt.Printf("Failed with %d", result)
}
fmt.Printf("We succeeded, and the result is %d\n", result)

Inne języki używają systemu typów, aby nie dopuszczać wyrażeń innych niż boolowskie wewnątrz warunkowego:

int foo;
if (foo = bar()) // Java does not like this

Oczywiście kończy się to niepowodzeniem, gdy używana jest funkcja zwracająca wartość logiczną.

Widzieliśmy teraz różne mechanizmy obrony przed przypadkowym przydzieleniem:

  • Nie zezwalaj na przypisanie jako wyrażenie
  • Użyj sprawdzania typu statycznego
  • Przypisanie nie istnieje, mamy tylko letpowiązania
  • Zezwalaj na instrukcję inicjalizacji, w przeciwnym razie nie zezwalaj na przypisanie
  • Nie zezwalaj na przypisywanie wewnątrz warunkowego bez deklaracji

Posortowałem je w kolejności rosnącej preferencji - przydziały w wyrażeniach mogą być przydatne (i łatwo jest ominąć problemy Pythona poprzez jawną składnię deklaracji i inną nazwaną składnię argumentów). Ale można je zabronić, ponieważ istnieje wiele innych opcji tego samego efektu.

Kod wolny od błędów jest ważniejszy niż kod zwięzły.

amon
źródło
+1 dla „Nie zezwalaj na przypisanie jako wyrażenia”. Przypadki użycia przypisania jako wyrażenia nie uzasadniają potencjalnych błędów i problemów z czytelnością.
poke
7

Powiedziałeś „Uznałem (z niewielkimi dowodami poza moim osobistym doświadczeniem), że w zdecydowanej większości przypadków tak się działo, to naprawdę miało być operacją porównania”.

Dlaczego NIE USUWAĆ PROBLEMU?

Zamiast = dla przypisania i == dla testu równości, dlaczego nie użyć: = dla przypisania i = (lub nawet ==) dla równości?

Przestrzegać:

if (a=foo(bar)) {}  // obviously equality
if (a := foo(bar)) { do something with a } // obviously assignment

Jeśli chcesz utrudnić programiście pomyłkę przypisania z równością, utrudnij.

W tym samym czasie, jeśli NAPRAWDĘ chciałbyś rozwiązać problem, usunąłbyś garnek C, który twierdził, że booleany są tylko liczbami całkowitymi z predefiniowanymi symbolicznymi nazwami cukru. Zrób z nich zupełnie inny typ. Potem zamiast mówić

int a = some_value();
if (a) {}

zmuszasz programistę do napisania:

int a = some_value();
if (a /= 0) {} // Note that /= means 'not equal'.  This is your Ada lesson for today.

Faktem jest, że przypisanie jako operatora jest bardzo przydatną konstrukcją. Nie wyeliminowaliśmy żyletek, ponieważ niektórzy ludzie się skaleczą. Zamiast tego król Gillette wynalazł maszynkę do golenia.

John R. Strohm
źródło
2
(1) :=dla zadań i =dla równości może rozwiązać ten problem, ale kosztem wyobcowania każdego programisty, który nie dorastał przy użyciu małego zestawu języków innych niż główny nurt. (2) Typy inne niż dozwolone boole w warunkach nie zawsze są wynikiem mieszania booli i liczb całkowitych, wystarczy podać prawdziwą / fałszywą interpretację dla innych typów. Nowszy język, który nie boi się odejść od C, zrobił to dla wielu typów innych niż liczby całkowite (np. Python uważa puste kolekcje za fałszywe).
1
A jeśli chodzi o żyletki: służą one do użycia, który wymaga ostrości. Z drugiej strony nie jestem przekonany, czy dobre programowanie wymaga przypisania do zmiennych w trakcie oceny wyrażenia. Gdyby istniał prosty, mało zaawansowany technologicznie, bezpieczny i opłacalny sposób usuwania włosów na ciele bez ostrych krawędzi, jestem pewien, że żyletki zostałyby przesunięte lub przynajmniej znacznie rzadsze.
1
@delnan: Mądry człowiek powiedział kiedyś: „Uprość to, jak to możliwe, ale nie prościej”. Jeśli Twoim celem jest wyeliminowanie ogromnej większości błędów a = b vs. a == b, ograniczenie dziedziny testów warunkowych do boolanów i wyeliminowanie domyślnych reguł konwersji typów dla <other> -> boolean prowadzi do końca. tam. W tym momencie, jeśli (a = b) {} jest syntaktycznie legalny tylko wtedy, gdy aib są jednocześnie wartościami logicznymi, a a jest legalną wartością.
John R. Strohm
Dokonanie przypisania oświadczenia jest co najmniej tak proste, jak - prawdopodobnie nawet prostsze niż - proponowane zmiany i osiąga co najmniej tyle samo - prawdopodobnie jeszcze więcej (nawet nie pozwala if (a = b)na wartość l, wartość boolean a, b). W języku bez pisania statycznego daje także znacznie lepsze komunikaty o błędach (w czasie analizy w porównaniu do czasu wykonywania). Ponadto zapobieganie błędom „a = b vs. a == b” może nie być jedynym istotnym celem. Na przykład chciałbym również pozwolić, aby kod if items:miał na myśli if len(items) != 0, i że musiałbym się poddać, aby ograniczyć warunki do wartości logicznych.
1
@delnan Pascal to język spoza głównego nurtu? Miliony ludzi nauczyło się programowania przy użyciu Pascala (i / lub Moduli, która pochodzi od Pascala). I Delphi jest nadal powszechnie stosowane w wielu krajach (może nie tak bardzo w twoim).
jwenting
5

Aby odpowiedzieć na pytanie, tak, istnieje wiele zastosowań tego, choć są one nieco niszowe.

Na przykład w Javie:

while ((Object ob = x.next()) != null) {
    // This will loop through calling next() until it returns null
    // The value of the returned object is available as ob within the loop
}

Alternatywa bez użycia osadzonego przypisania wymaga obzdefiniowania poza zakresem pętli i dwóch oddzielnych lokalizacji kodu, które wywołują x.next ().

Wspomniano już, że można przypisać wiele zmiennych w jednym kroku.

x = y = z = 3;

Tego rodzaju rzeczy są najczęstszym zastosowaniem, ale kreatywni programiści zawsze wymyślą więcej.

Tim B.
źródło
Czy warunek pętli while zwalnia i tworzy nowy obobiekt przy każdej pętli?
użytkownik3932000,
@ user3932000 W takim przypadku prawdopodobnie nie, zwykle x.next () iteruje nad czymś. Z pewnością jest to możliwe.
Tim B
1

Skoro musisz wymyślić wszystkie reguły, dlaczego teraz pozwalasz przypisaniu na zmianę wartości i po prostu nie zezwalasz na przypisania w ramach kroków warunkowych? Daje to cukier syntaktyczny ułatwiający inicjalizację, a jednocześnie zapobiega częstemu błędowi kodowania.

Innymi słowy, ustaw to jako legalne:

a=b=c=0;

Ale uczyń to nielegalnym:

if (a=b) ...
Bryan Oakley
źródło
2
To wydaje się raczej regułą ad hoc. Dokonanie przypisania instrukcji i rozszerzenie go tak, by zezwalało, a = b = cwydaje się bardziej ortogonalne i łatwiejsze do wdrożenia. Te dwa podejścia nie zgadzają się co do przypisania w wyrażeniach ( a + (b = c)), ale nie opowiedziałeś się po ich stronie, więc zakładam, że nie mają one znaczenia.
„łatwe do wdrożenia” nie powinno stanowić wielkiego problemu. Definiujesz interfejs użytkownika - na pierwszym miejscu stawiaj potrzeby użytkowników. Musisz po prostu zadać sobie pytanie, czy to zachowanie pomaga, czy przeszkadza użytkownikowi.
Bryan Oakley
jeśli nie zgadzasz się na niejawną konwersję na bool, nie musisz się martwić o przypisanie w warunkach
maniak ratchet
Łatwiejszy do wdrożenia był tylko jednym z moich argumentów. Co z resztą? Z punktu widzenia interfejsu użytkownika mogę dodać, że niespójne wzornictwo i wyjątki ad-hoc IMHO generalnie utrudniają użytkownikowi grokking i internalizację zasad.
@ ratchetfreak nadal możesz mieć problem z przypisywaniem rzeczywistych wartości bool
jk.
0

Na podstawie tego, że jesteś na ścieżce tworzenia dość ścisłego języka.

Mając to na uwadze, zmuszając ludzi do pisania:

a=c;
b=c;

zamiast:

a=b=c;

może wydawać się ulepszeniem, aby uniemożliwić ludziom:

if (a=b) {

kiedy zamierzali zrobić:

if (a==b) {

ale ostatecznie tego rodzaju błędy są łatwe do wykrycia i ostrzeżenia o tym, czy są one zgodne z prawem.

Są jednak sytuacje, w których:

a=c;
b=c;

to nie znaczy, że

if (a==b) {

będzie prawdą.

Jeśli c jest faktycznie funkcją c (), może zwracać różne wyniki za każdym razem, gdy zostanie wywołane. (może to być również kosztowne obliczeniowo ...)

Podobnie, jeśli c jest wskaźnikiem do sprzętu zmapowanego w pamięci, to

a=*c;
b=*c;

oba mogą być różne, a także mogą mieć wpływ elektroniczny na sprzęt przy każdym odczycie.

Istnieje wiele innych permutacji ze sprzętem, w których musisz dokładnie określić, z których adresów pamięci są odczytywane, zapisywane i pod określonymi ograniczeniami czasowymi, gdzie wykonywanie wielu przypisań w tym samym wierszu jest szybkie, proste i oczywiste, bez ryzyka czasowego, że wprowadzają zmienne tymczasowe

Michael Shaw
źródło
4
Odpowiednikiem a = b = cnie jest a = c; b = c, to jest b = c; a = b. Pozwala to uniknąć powielania działań niepożądanych, a także utrzymuje modyfikację ai bw tej samej kolejności. Ponadto wszystkie te argumenty związane ze sprzętem są trochę głupie: większość języków nie jest językami systemowymi i nie są przeznaczone do rozwiązywania tych problemów, ani nie są używane w sytuacjach, w których problemy te występują. Dzieje się tak podwójnie w przypadku języka, który próbuje zastąpić JavaScript i / lub PHP.
Delnan, problemem nie były te wymyślone przykłady. Nadal chodzi o to, że pokazują one rodzaje miejsc, w których powszechne jest pisanie a = b = c, aw przypadku sprzętu uważane za dobrą praktykę, o co poprosił PO. Jestem pewien, że będą w stanie rozważyć ich znaczenie dla oczekiwanego środowiska
Michael Shaw
Patrząc wstecz, mój problem z tą odpowiedzią nie polega przede wszystkim na tym, że koncentruje się na przypadkach użycia programowania systemowego (choć byłoby to wystarczająco złe, sposób pisania), ale że opiera się na założeniu niepoprawnego przepisywania. Przykłady nie są przykładami miejsc, w których a=b=cjest powszechne / użyteczne, są to przykłady miejsc, w których należy zadbać o porządek i liczbę skutków ubocznych. To jest całkowicie niezależne. Przepisz poprawnie przypisane połączenie, a oba warianty są jednakowo poprawne.
@delnan: Wartość jest konwertowana na typ bw jednej temp, i to jest konwertowane na typ aw innej temp. Względny czas, w którym te wartości są faktycznie przechowywane, nie jest określony. Z punktu widzenia projektowania językowego uważam za rozsądne wymaganie, aby wszystkie wartości lv w instrukcji wielokrotnego przypisania były zgodne, i być może wymagać również, aby żadna z nich nie była zmienna.
supercat
0

Największą korzyścią dla mnie, że zadanie jest wyrażeniem, jest to, że pozwala na uproszczenie gramatyki, jeśli jednym z twoich celów jest to, że „wszystko jest wyrażeniem” - szczególnie cel LISP.

Python tego nie ma; ma wyrażenia i wyrażenia, przy czym przypisanie jest wyrażeniem. Ale ponieważ Python definiuje lambdaformę jako pojedyncze sparametryzowane wyrażenie , oznacza to, że nie można przypisywać zmiennych wewnątrz lambda. Czasami jest to niewygodne, ale nie jest to problem krytyczny, i jest to jedyny minus z mojego doświadczenia, że ​​przypisanie go jest instrukcją w Pythonie.

Jednym ze sposobów, aby przypisanie, a raczej efekt przypisania, było wyrażeniem bez wprowadzania ryzyka if(x=1)wypadków, jakie ma C, jest użycie letkonstrukcji podobnej do LISP , takiej jak, (let ((x 2) (y 3)) (+ x y))którą może oceniać twój język 5. Korzystanie z lettego sposobu nie musi być technicznie wcale zadaniem w twoim języku, jeśli zdefiniujesz letjako tworzenie zakresu leksykalnego. Zdefiniowana w ten sposób letkonstrukcja może zostać skompilowana w taki sam sposób, jak konstrukcja i wywołanie zagnieżdżonej funkcji zamknięcia z argumentami.

Z drugiej strony, jeśli po prostu zajmujesz się if(x=1)sprawą, ale chcesz, aby przypisanie było wyrażeniem jak w C, być może wystarczy wybrać inne tokeny. Przydział: x := 1lub x <- 1. Porównanie: x == 1. Syntax error: x = 1.

jagoda
źródło
1
letróżni się od przypisania w większym stopniu niż technicznie wprowadzenie nowej zmiennej w nowym zakresie. Po pierwsze, nie ma to wpływu na kod poza letciałem, a zatem wymaga dalszego zagnieżdżania całego kodu (co powinno być używane ze zmienną), co jest znaczącym minusem w kodzie wymagającym dużego przypisania. Gdyby ktoś poszedł tą drogą, set!byłby lepszym analogiem Lisp - zupełnie niepodobnym do porównania, ale nie wymagającym zagnieżdżania lub nowego zakresu.
@delnan: Chciałbym zobaczyć kombinację składni deklaruj i przydziel ) redeklaracja „cofnąłaby deklarację” zmiennej we wszystkich obejmujących zakresach. Zatem wartość dowolnego ważnego identyfikatora byłaby taka sama, jak w poprzedniej deklaracji tej nazwy. Wydawałoby się to nieco przyjemniejsze niż dodawanie bloków zakresu dla zmiennych, które są używane tylko dla kilku wierszy, lub formułowanie nowych nazw dla każdej zmiennej temp.
supercat
0

Wiem, że oznaczałoby to, że funkcje jednowierszowe, które ludzie C tak bardzo kochają, przestałyby działać. Doszedłem do wniosku (z niewielkimi dowodami poza moim osobistym doświadczeniem), że w zdecydowanej większości przypadków tak się działo, to naprawdę była to operacja porównania.

W rzeczy samej. To nie jest nic nowego, wszystkie bezpieczne podzbiory języka C już wyciągnęły taki wniosek.

MISRA-C, CERT-C itd. Na wszystkich zakazach przypisywania w warunkach wewnętrznych, po prostu dlatego, że jest to niebezpieczne.

Nie ma przypadku, w którym kod oparty na przypisaniu warunków wewnętrznych nie może zostać przepisany.


Ponadto takie standardy ostrzegają również przed pisaniem kodu, który opiera się na kolejności oceny. Tak x=y=z;jest w przypadku wielu zadań w jednym wierszu . Jeśli wiersz z wieloma przypisaniami zawiera efekty uboczne (wywoływanie funkcji, dostęp do zmiennych niestabilnych itp.), Nie możesz wiedzieć, który efekt uboczny wystąpi jako pierwszy.

Pomiędzy operandami nie ma punktów sekwencji. Nie możemy więc wiedzieć, czy podwyrażenie yjest oceniane przed czy po z: jest to zachowanie nieokreślone w C. Zatem taki kod jest potencjalnie zawodny, nieprzenośny i niezgodny z wymienionymi bezpiecznymi podzbiorami C.

Rozwiązaniem byłoby zastąpienie kodu y=z; x=y;. To dodaje punkt sekwencyjny i gwarantuje kolejność oceny.


Opierając się na wszystkich problemach spowodowanych w C, każdy współczesny język dobrze zrobiłby zarówno zakazanie przypisywania w warunkach wewnętrznych, jak i wiele zadań w jednym wierszu.


źródło