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ę).
źródło
while((x = getValue()) != null) {}
. Zamienniki będą brzydsze, ponieważ będziesz musiał albo użyćbreak
albo powtórzyćx = getValue
zadanie.Odpowiedzi:
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ą.
if (x)
zamiastif (x != null)
lub wif (x != 0)
zależności od rodzajux
.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żneb = c; a = b
i 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
a = b = c
wywołanie, którego inne odpowiedzi tak naprawdę nie wywołują .:=
do przypisania.=
to zadanie,==
to porównanie.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 awarning: 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.a = true
ma ocenić do Boolean, a zatem nie jest błędem, ale także podnosi powiązany ostrzeżenie w języku C #.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:
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
Podobnie z
być skrótem od
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.
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 .
Ten czerwony. Lub
lub nawet lepiej:
A jeszcze lepiej
Absolutnie nie ma mowy, aby którykolwiek z nich mógł zostać pomylony
x == 1
.źródło
x[⍋x←6?40]
APL wymagało własnej specjalnej klawiatury, ale był to całkiem udany język.a+b*c --> x
? To wydaje mi się dziwne.Wiele języków wybiera drogę przypisywania instrukcji zamiast wyrażenia, w tym Python:
i Golang:
Inne języki nie mają przypisania, ale raczej powiązania o zasięgu, np. OCaml:
Jednak
let
to 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:
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:
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:
Inne języki używają systemu typów, aby nie dopuszczać wyrażeń innych niż boolowskie wewnątrz warunkowego:
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:
let
powiązaniaPosortował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.
źródło
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ć:
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ć
zmuszasz programistę do napisania:
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.
źródło
:=
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).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 kodif items:
miał na myśliif len(items) != 0
, i że musiałbym się poddać, aby ograniczyć warunki do wartości logicznych.Aby odpowiedzieć na pytanie, tak, istnieje wiele zastosowań tego, choć są one nieco niszowe.
Na przykład w Javie:
Alternatywa bez użycia osadzonego przypisania wymaga
ob
zdefiniowania 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.
Tego rodzaju rzeczy są najczęstszym zastosowaniem, ale kreatywni programiści zawsze wymyślą więcej.
źródło
ob
obiekt przy każdej pętli?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:
Ale uczyń to nielegalnym:
źródło
a = b = c
wydaje 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.Na podstawie tego, że jesteś na ścieżce tworzenia dość ścisłego języka.
Mając to na uwadze, zmuszając ludzi do pisania:
zamiast:
może wydawać się ulepszeniem, aby uniemożliwić ludziom:
kiedy zamierzali zrobić:
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:
to nie znaczy, że
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
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
źródło
a = b = c
nie jesta = c; b = c
, to jestb = c; a = b
. Pozwala to uniknąć powielania działań niepożądanych, a także utrzymuje modyfikacjęa
ib
w 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.a=b=c
jest 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.b
w jednej temp, i to jest konwertowane na typa
w 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.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
lambda
formę 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życielet
konstrukcji podobnej do LISP , takiej jak,(let ((x 2) (y 3)) (+ x y))
którą może oceniać twój język5
. Korzystanie zlet
tego sposobu nie musi być technicznie wcale zadaniem w twoim języku, jeśli zdefiniujeszlet
jako tworzenie zakresu leksykalnego. Zdefiniowana w ten sposóblet
konstrukcja 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 := 1
lubx <- 1
. Porównanie:x == 1
. Syntax error:x = 1
.źródło
let
róż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 pozalet
ciał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.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
y
jest oceniane przed czy poz
: 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