Dalam banyak bahasa, sintaks function_name(arg1, arg2, ...)
digunakan untuk memanggil suatu fungsi. Ketika kita ingin memanggil fungsi tanpa argumen, kita harus melakukannya function_name()
.
Saya merasa aneh bahwa kompiler atau juru bahasa naskah harus ()
berhasil mendeteksinya sebagai panggilan fungsi. Jika suatu variabel diketahui bisa dipanggil, mengapa tidak function_name;
cukup?
Di sisi lain, dalam beberapa bahasa kita dapat melakukan: function_name 'test';
atau bahkan function_name 'first' 'second';
untuk memanggil fungsi atau perintah.
Saya pikir kurung akan lebih baik jika mereka hanya diperlukan untuk menyatakan urutan prioritas, dan di tempat lain adalah opsional. Misalnya, melakukan if expression == true function_name;
harus sama validnya dengan if (expression == true) function_name();
.
Hal yang paling menyebalkan menurut saya adalah melakukan 'SOME_STRING'.toLowerCase()
ketika jelas tidak ada argumen yang diperlukan oleh fungsi prototipe. Mengapa para desainer memutuskan menentang yang lebih sederhana 'SOME_STRING'.lower
?
Penafian: Jangan salah, saya suka sintaksis C-like! ;) Saya hanya bertanya apakah itu bisa lebih baik. Apakah memerlukan ()
memiliki keunggulan kinerja, atau apakah membuatnya lebih mudah memahami kode? Saya benar-benar ingin tahu apa alasan sebenarnya.
sumber
()
, namun hal yang menonjol dalam posting Anda adalahif (expression == true)
pernyataan. Anda khawatir tentang berlebihan()
, tetapi kemudian menggunakan== true
:) berlebihanJawaban:
Untuk bahasa yang menggunakan fungsi kelas satu , cukup umum bahwa sintaks yang merujuk ke suatu fungsi adalah:
sedangkan tindakan memanggil fungsi itu adalah:
a
dalam contoh di atas akan merujuk ke fungsi di atas (dan Anda bisa menyebutnya dengan melakukana()
), sementarab
akan berisi nilai pengembalian fungsi.Sementara beberapa bahasa dapat melakukan panggilan fungsi tanpa tanda kurung, itu bisa membingungkan apakah mereka memanggil fungsi, atau hanya merujuk ke fungsi.
sumber
make_list
yang mengemas argumennya ke dalam daftar, ada perbedaan besar antaraf(make_list)
meneruskan fungsi kef
atau melewati daftar kosong.SATInstance.solve
atau fungsi murni luar biasa mahal lainnya ke fungsi lain tanpa segera berusaha menjalankannya. Itu juga membuat segalanya benar-benar canggung jikasomeFunction
melakukan sesuatu yang berbeda tergantung pada apakahsomeFunction
dinyatakan murni. Sebagai contoh, jika saya memiliki (pseudo)func f() {return g}
,func g() {doSomethingImpure}
danfunc apply(x) {x()}
, kemudianapply(f)
panggilanf
. Jika saya kemudian menyatakan ituf
murni, maka tiba-tibaapply(f)
beralihg
keapply
, danapply
memanggilg
dan melakukan sesuatu yang tidak murni.apply(f)
, jikag
tidak murni (danapply
juga?) Maka semuanya tidak murni dan rusak tentu saja.foo.bar()
bukan satu operasi "memanggil metodebar
objekfoo
" tetapi dua operasi, "bidang dereferensibar
objekfoo
dan kemudian memanggil objek yang dikembalikan dari bidang itu". Jadi, itu.
adalah operator pemilih bidang dan()
merupakan operator panggilan dan mereka independen.Memang, Scala memungkinkan ini, meskipun ada konvensi yang diikuti: jika metode ini memiliki efek samping, tanda kurung harus tetap digunakan.
Sebagai penulis kompiler, saya akan menemukan kehadiran kurung dijamin cukup nyaman; Saya akan selalu tahu bahwa itu adalah pemanggilan metode, dan saya tidak perlu membangun bifurkasi untuk kasus aneh.
Sebagai seorang programmer dan pembaca kode, keberadaan tanda kurung tidak diragukan lagi bahwa itu adalah pemanggilan metode, meskipun tidak ada parameter yang dilewati.
Melewati parameter bukan satu-satunya karakteristik pendefinisian pemanggilan metode. Mengapa saya memperlakukan metode tanpa parameter yang berbeda dari metode yang memiliki parameter?
sumber
def foo()
adalah metode dengan satu daftar parameter kosong, dandef foo
merupakan metode tanpa daftar parameter dan keduanya adalah hal yang berbeda ! Secara umum, metode tanpa daftar parameter perlu dipanggil tanpa daftar argumen dan metode dengan daftar parameter kosong perlu dipanggil dengan daftar argumen kosong. Memanggil metode tanpa daftar parameter dengan argumen kosong sebenarnya ...apply
metode objek yang dikembalikan oleh pemanggilan metode dengan daftar argumen kosong, sedangkan memanggil metode dengan daftar parameter kosong tanpa daftar argumen dapat bergantung pada konteks yang diartikan sebagai memanggil metode dengan daftar argumen kosong, η-ekspansi ke metode yang diterapkan sebagian (yaitu "referensi metode"), atau mungkin tidak dapat dikompilasi sama sekali. Juga, Scala mengikuti Prinsip Akses Seragam, dengan tidak (secara sintaksis) membedakan antara memanggil metode tanpa daftar argumen dan referensi bidang.Ini sebenarnya adalah kebetulan pilihan sintaks yang cukup halus. Saya akan berbicara dengan bahasa fungsional, yang didasarkan pada kalkulus lambda yang diketik.
Dalam bahasa tersebut, setiap fungsi memiliki tepat satu argumen . Apa yang sering kita anggap sebagai "argumen berganda" sebenarnya adalah parameter tunggal dari jenis produk. Jadi misalnya, fungsi yang membandingkan dua bilangan bulat:
membutuhkan sepasang bilangan bulat. Tanda kurung tidak menunjukkan parameter fungsi; mereka digunakan untuk mencocokkan pola argumen. Untuk meyakinkan Anda bahwa ini benar-benar satu argumen, kita dapat menggunakan fungsi proyektif alih-alih pencocokan pola untuk mendekonstruksi pasangan:
Dengan demikian, tanda kurung benar-benar kenyamanan yang memungkinkan kita untuk mencocokkan pola dan menyimpan beberapa pengetikan. Mereka tidak diharuskan.
Bagaimana dengan fungsi yang seolah-olah tidak memiliki argumen? Fungsi semacam itu sebenarnya memiliki domain
Unit
. Anggota tunggalUnit
biasanya ditulis sebagai()
, jadi itu sebabnya tanda kurung muncul.Untuk memanggil fungsi ini, kita harus menerapkannya ke nilai tipe
Unit
, yang harus()
, jadi kita akhirnya menulissay_hi ()
.Jadi, sebenarnya tidak ada daftar argumen!
sumber
()
menyerupai sendok dikurangi pegangannya.leq
fungsi yang menggunakan<
daripada<=
cukup membingungkan!()
untuk mewakili unit. Saya tidak akan terkejut jika setidaknya sebagian alasannya menyerupai "fungsi parameterless". Namun, ada penjelasan lain yang mungkin untuk sintaksis. Dalam aljabar jenis,Unit
sebenarnya identitas untuk jenis produk. Produk biner ditulis sebagai(a,b)
, kita dapat menulis produk yang belum(a)
jadi, jadi ekstensi logis adalah menulis produk nullary secara sederhana()
.Dalam Javascript misalnya menggunakan nama metode tanpa () mengembalikan fungsi itu sendiri tanpa menjalankannya. Dengan cara ini misalnya Anda dapat melewatkan fungsi sebagai argumen ke metode lain.
Di Jawa pilihan dibuat bahwa pengidentifikasi diikuti oleh () atau (...) berarti pemanggilan metode sementara pengidentifikasi tanpa () merujuk ke variabel anggota. Ini dapat meningkatkan keterbacaan, karena Anda tidak ragu apakah Anda berurusan dengan metode atau variabel anggota. Bahkan, nama yang sama dapat digunakan untuk metode dan variabel anggota, keduanya akan dapat diakses dengan sintaks masing-masing.
sumber
Perl Best Practices
tidakObject::toString
mengidentifikasi suatu metode.Sintaks mengikuti semantik, jadi mari kita mulai dari semantik:
Sebenarnya ada beberapa cara:
Memiliki sintaksis yang serupa untuk kegunaan yang berbeda adalah cara terbaik untuk membuat bahasa yang ambigu atau setidaknya yang membingungkan (dan kami memiliki cukup banyak dari itu).
Dalam bahasa C, dan bahasa mirip C:
func()
&func
(C membuat pointer fungsi)someVariable::someMethod
misalnya (terbatas pada penerima metode, tetapi masih berguna)Perhatikan bagaimana setiap penggunaan memiliki sintaks yang berbeda, memungkinkan Anda membedakannya dengan mudah.
sumber
::
Operator di Jawa adalah operator aplikasi parsial, bukan operator currying. Aplikasi parsial adalah operasi yang mengambil fungsi dan satu atau lebih nilai dan mengembalikan fungsi yang menerima nilai lebih sedikit. Currying hanya beroperasi pada fungsi, bukan nilai.Dalam bahasa dengan efek samping, IMO sangat membantu untuk membedakan antara pembacaan variabel (dalam efek samping teori)
dan memanggil fungsi, yang mungkin menimbulkan efek samping
OTOH, jika tidak ada efek samping (selain yang dikodekan dalam sistem tipe) maka tidak ada titik untuk bahkan membedakan antara membaca variabel dan memanggil fungsi tanpa argumen.
sumber
noun.verb
sintaksis bisa sejelas dalam artinoun.verb()
dan sedikit kurang berantakan. Demikian pula, sebuah fungsi yangmember_
mengembalikan referensi kiri mungkin menawarkan sintaksis ramah daripada fungsi setter yang khas:obj.member_ = new_value
vs.obj.set_member(new_value)
(_
postfix adalah sebuah petunjuk yang mengatakan "terkena" mengingatkan_
awalan untuk fungsi "tersembunyi").noun.verb
berarti pemanggilan fungsi, bagaimana Anda membedakannya dari akses anggota belaka?Tidak ada jawaban lain yang mencoba menjawab pertanyaan: berapa banyak redundansi dalam desain suatu bahasa? Karena bahkan jika Anda dapat mendesain bahasa sehingga
x = sqrt y
menetapkan x ke akar kuadrat dari y, itu tidak berarti Anda harus melakukannya.Dalam bahasa tanpa redundansi, setiap urutan karakter berarti sesuatu, yang berarti bahwa jika Anda membuat kesalahan tunggal, Anda tidak akan mendapatkan pesan kesalahan, program Anda akan melakukan hal yang salah, yang mungkin sesuatu yang sangat berbeda dari apa yang Anda dimaksudkan dan sangat sulit untuk di-debug. (Seperti yang diketahui oleh siapa pun yang bekerja dengan ekspresi reguler.) Sedikit redundansi adalah hal yang baik karena memungkinkan banyak kesalahan Anda terdeteksi, dan semakin banyak redundansi, semakin besar kemungkinan diagnosis akan akurat. Sekarang tentu saja, Anda bisa mengambil terlalu jauh (tidak ada dari kita yang ingin menulis dalam COBOL hari ini), tetapi ada keseimbangan yang benar.
Redundansi juga membantu keterbacaan, karena ada lebih banyak petunjuk. Bahasa Inggris tanpa redundansi akan sangat sulit dibaca, dan hal yang sama berlaku untuk bahasa pemrograman.
sumber
:=
dan perbandingan==
, dengan=
hanya dapat digunakan untuk inisialisasi waktu kompilasi, maka kemungkinan kesalahan pengubahan perbandingan menjadi penugasan akan sangat berkurang.()
untuk pemanggilan fungsi argumen-nol tetapi juga membutuhkan token untuk "mengambil alamat" dari suatu fungsi. Tindakan sebelumnya jauh lebih umum, jadi menyimpan token dalam kasus yang kurang umum tidak terlalu berguna.Dalam kebanyakan kasus, ini adalah pilihan sintaksis tata bahasa. Ini berguna untuk tata bahasa agar berbagai konstruksi individu menjadi (relatif) tidak ambigu ketika disatukan. (Jika ada ambiguitas, seperti dalam beberapa deklarasi C ++, harus ada aturan khusus untuk resolusi.) Kompiler tidak memiliki lintang untuk menebak; diperlukan untuk mengikuti spesifikasi bahasa.
Visual Basic, dalam berbagai bentuk, membedakan antara prosedur yang tidak mengembalikan nilai, dan fungsi.
Prosedur harus disebut sebagai pernyataan, dan tidak memerlukan paren, cukup argumen yang dipisahkan koma, jika ada. Fungsi harus dipanggil sebagai bagian dari ekspresi, dan memerlukan parens.
Ini adalah perbedaan yang relatif tidak perlu yang membuat refactoring manual antara kedua bentuk lebih menyakitkan daripada yang seharusnya.
(Di sisi lain Visual Basic menggunakan parens yang sama
()
untuk referensi array seperti untuk panggilan fungsi, sehingga referensi array terlihat seperti panggilan fungsi. Dan ini memudahkan refactoring manual dari array menjadi panggilan fungsi! Jadi, kita bisa merenungkan bahasa lain menggunakan[]
's untuk referensi array, tapi saya ngelantur ...)Dalam bahasa C dan C ++ isi variabel secara otomatis diakses dengan menggunakan namanya, dan jika Anda ingin merujuk ke variabel itu sendiri alih-alih isinya, Anda menerapkan
&
operator unary .Mekanisme semacam ini juga bisa diterapkan pada nama-nama fungsi. Nama fungsi mentah dapat menyiratkan panggilan fungsi, sedangkan
&
operator unary akan digunakan untuk merujuk ke fungsi (itu sendiri) sebagai data. Secara pribadi, saya suka ide mengakses fungsi no-argumen bebas efek samping dengan sintaks yang sama dengan variabel.Ini sangat masuk akal (seperti pilihan sintaksis lainnya untuk ini).
sumber
call <procedure name> (<arguments>)
? Cukup yakin itu adalah cara yang sah untuk menggunakan prosedur saat saya melakukan pemrograman QuickBASIC di masa yang lain ...Call
metode yang hampir tidak pernah saya perlukan dalam kode produksi "normal".Konsistensi dan keterbacaan.
Jika saya belajar bahwa Anda memanggil fungsi
X
seperti ini:X(arg1, arg2, ...)
, maka saya mengharapkan untuk bekerja dengan cara yang sama tanpa argumen:X()
.Sekarang pada saat yang sama saya belajar bahwa Anda dapat mendefinisikan variabel sebagai beberapa simbol, dan menggunakannya seperti ini:
Sekarang apa yang akan saya pikirkan ketika saya menemukan ini?
Dugaanmu sebaik milikku. Dalam keadaan normal,
X
ini adalah variabel. Tetapi jika kami mengambil jalan Anda, itu juga bisa menjadi fungsi! Apakah saya harus mengingat apakah simbol mana yang memetakan ke grup mana (variabel / fungsi)?Kita dapat memaksakan batasan artifisial, misalnya "Fungsi dimulai dengan huruf kapital. Variabel mulai dengan huruf kecil", tetapi itu tidak perlu dan membuat segalanya lebih rumit, meskipun kadang-kadang mungkin membantu beberapa tujuan desain.
Catatan-samping 1: Jawaban lain sama sekali mengabaikan satu hal lagi: bahasa memungkinkan Anda menggunakan nama yang sama untuk fungsi dan variabel, dan membedakan berdasarkan konteks. Lihat
Common Lisp
sebagai contoh. Fungsix
dan variabelx
hidup berdampingan dengan sangat baik.Side-catatan 2: Jawaban yang diterima menunjukkan sintaks:
object.functionname
. Pertama, itu tidak universal untuk bahasa dengan fungsi kelas satu. Kedua, sebagai seorang programmer saya akan memperlakukan ini sebagai informasi tambahan:functionname
milik seorangobject
. Apakahobject
objek, kelas atau namespace tidak terlalu menjadi masalah, tapi itu memberitahu saya bahwa itu milik suatu tempat . Ini berarti Anda harus menambahkan sintaks buatanobject.
untuk setiap fungsi global atau membuat beberapaobject
untuk menahan semua fungsi global.Dan bagaimanapun, Anda kehilangan kemampuan untuk memiliki ruang nama terpisah untuk fungsi dan variabel.
sumber
Untuk menambah jawaban lain, ambil contoh C ini:
Jika operator pemanggilan adalah opsional, tidak akan ada cara untuk membedakan antara penugasan fungsi dan pemanggilan fungsi.
Ini bahkan lebih bermasalah dalam bahasa yang tidak memiliki sistem tipe, misalnya JavaScript, di mana inferensi tipe tidak dapat digunakan untuk mencari tahu apa yang bisa dan tidak berfungsi.
sumber
function cross(a, b) { return a + b; }