W wielu językach function_name(arg1, arg2, ...)
do wywołania funkcji służy składnia . Kiedy chcemy wywołać funkcję bez żadnych argumentów, musimy to zrobić function_name()
.
Dziwne wydaje mi się, że kompilator lub interpreter skryptów wymagałby ()
skutecznego wykrycia go jako wywołania funkcji. Jeśli wiadomo, że zmienna jest wywoływalna, dlaczego nie function_name;
wystarczy?
Z drugiej strony w niektórych językach możemy zrobić: function_name 'test';
a nawet function_name 'first' 'second';
wywołać funkcję lub polecenie.
Myślę, że nawiasy byłyby lepsze, gdyby były potrzebne tylko do określenia kolejności priorytetów, a w innych miejscach byłyby opcjonalne. Na przykład wykonywanie if expression == true function_name;
powinno być tak samo ważne jak if (expression == true) function_name();
.
Moim zdaniem najbardziej irytujące jest to, że 'SOME_STRING'.toLowerCase()
funkcja prototypowa wyraźnie nie potrzebuje żadnych argumentów. Dlaczego projektanci zdecydowali się na prostsze 'SOME_STRING'.lower
?
Oświadczenie: Nie zrozum mnie źle, uwielbiam składnię podobną do C! ;) Po prostu pytam, czy mogłoby być lepiej. Czy wymaganie ()
ma jakieś zalety wydajnościowe, czy też ułatwia zrozumienie kodu? Jestem naprawdę ciekawy, jaki jest dokładnie powód.
źródło
()
, ale tym, co wyróżnia się w twoim poście, jestif (expression == true)
stwierdzenie. Martwisz się o to, co zbędne()
, a potem użyj zbędnego== true
:)Odpowiedzi:
W przypadku języków korzystających z funkcji pierwszej klasy dość często składnia odwoływania się do funkcji jest następująca:
podczas gdy akt wywołania tej funkcji to:
a
w powyższym przykładzie byłoby odwołanie do powyższej funkcji (i można ją wywołać wykonująca()
), podczas gdyb
zawierałoby wartość zwracaną funkcji.Podczas gdy niektóre języki mogą wykonywać wywołania funkcji bez nawiasów, może być mylące, czy wywołują funkcję, czy po prostu odnoszą się do funkcji.
źródło
make_list
która pakuje swoje argumenty do listy, istnieje duża różnica międzyf(make_list)
przekazywaniem funkcji dof
pustej listy.SATInstance.solve
inną, niewiarygodnie kosztowną, czystą funkcję do innej funkcji bez natychmiastowej próby jej uruchomienia. Sprawia również, że robi się naprawdę niezręcznie, jeślisomeFunction
robi coś innego w zależności od tego, czysomeFunction
jest zadeklarowane jako czyste. Na przykład, jeśli mam (Pseudokod)func f() {return g}
,func g() {doSomethingImpure}
orazfunc apply(x) {x()}
, po czymapply(f)
zwracaf
. Jeśli następnie stwierdzę, żef
jest czysty, nagleapply(f)
przechodzig
doapply
,apply
wzywag
i robi coś nieczystego.apply(f)
, jeślig
jest nieczysty (aapply
także?), To wszystko jest nieczyste i oczywiście się psuje.foo.bar()
to nie jedna operacja „wywołaj metodębar
obiektufoo
”, ale raczej dwie operacje, „dereferencyjne polebar
obiektu,foo
a następnie wywołanie obiektu zwróconego z tego pola”. Tak,.
to operator selektor pola i()
jest operator call i są niezależne.Rzeczywiście, Scala pozwala na to, chociaż obowiązuje konwencja: jeśli metoda ma skutki uboczne, i tak należy użyć nawiasów.
Jako autor kompilatora uważam, że gwarantowana obecność nawiasów jest całkiem wygodna; Zawsze wiedziałbym, że jest to wywołanie metody, i nie musiałbym budować rozwidlenia dla nieparzystego przypadku.
Jako programista i czytnik kodów obecność nawiasów nie pozostawia wątpliwości, że jest to wywołanie metody, mimo że nie są przekazywane żadne parametry.
Przekazywanie parametrów nie jest jedyną cechą charakterystyczną wywołania metody. Dlaczego miałbym traktować metodę bez parametrów inaczej niż metodę, która ma parametry?
źródło
def foo()
jest to metoda z jedną pustą listą parametrów idef foo
jest to metoda bez listy parametrów, a te dwie rzeczy są różne ! Ogólnie metoda bez listy parametrów musi być wywoływana bez listy argumentów, a metoda z pustą listą parametrów musi być wywoływana z pustą listą argumentów. Wywołanie metody bez listy parametrów z pustym argumentem jest w rzeczywistości…apply
metody obiektu zwróconego przez wywołanie metody z pustą listą argumentów, podczas gdy wywoływanie metody z pustą listą parametrów bez listy argumentów może w zależności od kontekstu być różnie interpretowane jako wywoływanie metody z pustą listą argumentów, η-rozwinięcie w częściowo zastosowaną metodę (tj. „odniesienie do metody”), w przeciwnym razie może się nie skompilować. Ponadto Scala przestrzega zasady jednolitego dostępu, nie rozróżniając (syntaktycznie) między wywołaniem metody bez listy argumentów a odwołaniem do pola.To właściwie subtelny przypadek wyboru składni. Będę mówić w językach funkcjonalnych, które są oparte na typowym rachunku lambda.
We wspomnianych językach każda funkcja ma dokładnie jeden argument . To, co często nazywamy „wieloma argumentami”, jest w rzeczywistości pojedynczym parametrem typu produktu. Na przykład funkcja, która porównuje dwie liczby całkowite:
bierze jedną parę liczb całkowitych. Nawiasy nie oznaczają parametrów funkcji; służą do dopasowania argumentu do wzorca . Aby cię przekonać, że to naprawdę jeden argument, zamiast tego możemy użyć funkcji projekcyjnych zamiast dopasowywania wzorca do zdekonstruowania pary:
Tak więc nawiasy są wygodą, która pozwala nam dopasować wzór i zaoszczędzić trochę pisania. Nie są wymagane.
Co z funkcją, która rzekomo nie ma argumentów? Taka funkcja faktycznie ma domenę
Unit
. Pojedynczy elementUnit
jest zwykle zapisywany jako()
, dlatego pojawiają się nawiasy.Aby wywołać tę funkcję, musielibyśmy zastosować ją do wartości typu
Unit
, która musi być()
, więc w końcu piszemysay_hi ()
.Tak więc naprawdę nie ma czegoś takiego jak lista argumentów!
źródło
()
przypominają łyżki minus uchwyt.leq
funkcji, która używa<
raczej niż<=
jest dość mylące!()
do reprezentowania urządzenia. Nie zdziwiłbym się, gdyby choć część tego powodu przypominała „funkcję bez parametrów”. Istnieje jednak inne możliwe wyjaśnienie składni. W algebrze typówUnit
jest tak naprawdę tożsamość typów produktów. Produkt binarny jest zapisywany jako(a,b)
, moglibyśmy napisać produkt jednoargumentowy jako(a)
, więc logicznym rozszerzeniem byłoby napisanie produktu zerowego tak prosto()
.Na przykład w języku Javascript użycie nazwy metody bez () zwraca samą funkcję bez jej wykonania. W ten sposób możesz na przykład przekazać funkcję jako argument do innej metody.
W Javie wybrano, że identyfikator, po którym następuje () lub (...), oznacza wywołanie metody, podczas gdy identyfikator bez () odnosi się do zmiennej składowej. Może to poprawić czytelność, ponieważ nie masz wątpliwości, czy masz do czynienia z metodą, czy zmienną składową. W rzeczywistości ta sama nazwa może być używana zarówno dla metody, jak i zmiennej składowej, obie będą dostępne z ich odpowiednią składnią.
źródło
Perl Best Practices
nieObject::toString
identyfikuje metodę.Składnia jest zgodna z semantyką, więc zacznijmy od semantyki:
Istnieje wiele sposobów:
Posiadanie podobnej składni do różnych zastosowań jest najlepszym sposobem na stworzenie albo dwuznacznego, albo co najmniej mylącego języka (i mamy ich dość).
W językach C i C-podobnych:
func()
&func
(C tworzenie wskaźnika funkcji)someVariable::someMethod
na przykład (ograniczony do odbiornika metody, ale nadal przydatny)Zwróć uwagę, że każde użycie ma inną składnię, co pozwala łatwo je rozróżnić.
źródło
::
Operator w Javie jest częściowym operator aplikacji, a nie operator Zmiękczanie. Częściowa aplikacja to operacja, która przyjmuje funkcję i jedną lub więcej wartości i zwraca funkcję przyjmującą mniej wartości. Curry działa tylko na funkcjach, a nie na wartościach.W języku z efektami ubocznymi IMO naprawdę pomaga rozróżnić (teoretycznie bez skutków ubocznych) odczyt zmiennej
i wywoływanie funkcji, która może powodować działania niepożądane
OTOH, jeśli nie ma efektów ubocznych (innych niż te zakodowane w systemie typów), nie ma sensu nawet rozróżniać między odczytem zmiennej a wywołaniem funkcji bez argumentów.
źródło
noun.verb
składnia może być tak przejrzysta, jaknoun.verb()
i nieco mniej zaśmiecona. Podobnie funkcjamember_
zwracająca lewe odwołanie może oferować przyjazną składnię niż typowa funkcja ustawiająca:obj.member_ = new_value
vs.obj.set_member(new_value)
(_
postfiks jest wskazówką mówiącą „odsłonięty” przypominającą_
prefiksy funkcji „ukrytych”).noun.verb
oznacza to wywołanie funkcji, jak odróżnić to od zwykłego dostępu członka?Żadna z pozostałych odpowiedzi nie próbowała odpowiedzieć na pytanie: ile powinno być nadmiarowości przy projektowaniu języka? Ponieważ nawet jeśli potrafisz zaprojektować język, który
x = sqrt y
ustawi x na pierwiastek kwadratowy z y, nie oznacza to, że koniecznie powinieneś.W języku bez nadmiarowości każda sekwencja znaków oznacza coś, co oznacza, że jeśli popełnisz jeden błąd, nie otrzymasz komunikatu o błędzie, twój program zrobi coś niewłaściwego, co może być czymś zupełnie innym niż to, co robisz zamierzone i bardzo trudne do debugowania. (Jak każdy, kto pracował z wyrażeniami regularnymi, będzie wiedział). Mała nadmiarowość jest dobra, ponieważ umożliwia wykrycie wielu błędów, a im więcej jest nadmiarowości, tym bardziej prawdopodobne jest, że diagnostyka będzie dokładna. Teraz oczywiście możesz posunąć się za daleko (obecnie nikt z nas nie chce pisać w języku COBOL), ale równowaga jest odpowiednia.
Nadmiarowość pomaga również w czytelności, ponieważ jest więcej wskazówek. Angielski bez redundancji byłby bardzo trudny do odczytania, to samo dotyczy języków programowania.
źródło
:=
i porównania==
z=
tylko jest użyteczna dla inicjalizacji kompilacji, wówczas prawdopodobieństwo literówek obracających się porównań do zadań będzie znacznie zmniejszone.()
wywołania funkcji z zerowym argumentem, ale także wymagałby tokena, aby „wziął adres” funkcji. Poprzednia akcja jest znacznie częstsza, więc zapisanie tokena w mniej powszechnym przypadku nie jest aż tak przydatne.W większości przypadków są to składniowe wybory gramatyki języka. Przydatne jest, aby gramatyka różnych indywidualnych konstruktów była (względnie) jednoznaczna, gdy zostaną wzięte razem. (Jeśli występują niejasności, jak w niektórych deklaracjach w C ++, muszą istnieć określone reguły rozwiązywania problemów.) Kompilator nie ma możliwości zgadywania; wymagane jest przestrzeganie specyfikacji języka.
Visual Basic w różnych formach rozróżnia procedury, które nie zwracają wartości, i funkcje.
Procedury muszą być wywoływane jako instrukcja i nie wymagają parens, tylko argumenty oddzielone przecinkami, jeśli występują. Funkcje muszą być wywoływane jako część wyrażenia i wymagają parens.
Jest to względnie niepotrzebne rozróżnienie, które sprawia, że ręczne refaktoryzacja między tymi dwiema formami jest bardziej bolesne niż musi być.
(Z drugiej strony Visual Basic używa tych samych parens
()
o referencje tablicy jak dla wywołań funkcji, więc odniesienia array wyglądać funkcja nazywa. A to ułatwia ręczne refaktoryzacji tablicy do wywołania funkcji! Tak, możemy rozważać inne języki użyć[]
„s dla odwołań do tablicy, ale dygresję ...)W językach C i C ++ dostęp do zawartości zmiennych jest uzyskiwany automatycznie przy użyciu ich nazw, a jeśli chcesz odwoływać się do samej zmiennej zamiast jej zawartości, zastosuj
&
operator jednoargumentowy .Ten rodzaj mechanizmu można również zastosować do nazw funkcji. Surowa nazwa funkcji mogłaby oznaczać wywołanie funkcji, podczas gdy
&
do określenia funkcji (samej) jako danych użyto by jednego operatora. Osobiście podoba mi się pomysł dostępu do pozbawionych argumentów funkcji ubocznych bez efektów o takiej samej składni jak zmienne.Jest to całkowicie prawdopodobne (podobnie jak inne wybory składniowe).
źródło
call <procedure name> (<arguments>)
? Dość pewny, że to był prawidłowy sposób korzystania z procedur, kiedy programowałem QuickBASIC w innym życiu ...Call
metody, w której prawie nigdy nie muszę tego robić w „normalnym” kodzie produkcyjnym.Spójność i czytelność.
Gdybym dowiedział się, że pan wywołać funkcję
X
tak:X(arg1, arg2, ...)
, a potem oczekiwać, że działają w ten sam sposób przez nie argumentyX()
.Teraz uczę się, że możesz zdefiniować zmienną jako jakiś symbol i używać jej w następujący sposób:
Co ja bym pomyślał, kiedy to znajdę?
Twoje domysły są równie dobre jak moje. W normalnych okolicznościach
X
jest to zmienna. Ale gdybyśmy podążyli twoją ścieżką, mogłaby to również być funkcja! Czy muszę pamiętać, czy który symbol odwzorowuje na którą grupę (zmienne / funkcje)?Możemy nałożyć sztuczne ograniczenia, np. „Funkcje zaczynają się od dużej litery. Zmienne zaczynają się od małej litery”, ale jest to niepotrzebne i komplikuje sprawy, chociaż czasami może pomóc niektórym celom projektowym.
Uwaga 1: Inne odpowiedzi całkowicie ignorują jeszcze jedną rzecz: język może pozwalać na używanie tej samej nazwy zarówno dla funkcji, jak i zmiennej, i rozróżnianie według kontekstu. Zobacz
Common Lisp
jako przykład. Funkcjax
i zmiennex
współistnieją doskonale.Side-Note 2: zaakceptowane odpowiedź pokazuje nam składnię:
object.functionname
. Po pierwsze, nie jest uniwersalny dla języków z pierwszorzędnymi funkcjami. Po drugie, jako programista traktowałbym to jako dodatkową informację:functionname
należy doobject
. To, czyobject
jest obiektem, klasą czy przestrzenią nazw, nie ma większego znaczenia, ale mówi mi, że gdzieś należy . Oznacza to, że musisz albo dodać sztuczną składnięobject.
dla każdej funkcji globalnej, albo utworzyć ją,object
by zawierała wszystkie funkcje globalne.W obu przypadkach tracisz możliwość posiadania oddzielnych przestrzeni nazw dla funkcji i zmiennych.
źródło
Aby dodać do innych odpowiedzi, weź przykład C:
Gdyby operator wywołania był opcjonalny, nie byłoby sposobu na odróżnienie przypisania funkcji od wywołania funkcji.
Jest to tym bardziej problematyczne w językach, w których nie ma systemu typów, np. JavaScript, gdzie wnioskowania o typie nie można użyć do ustalenia, co jest, a co nie jest funkcją.
źródło
function cross(a, b) { return a + b; }