Mengapa ada beberapa bahasa dengan 'operator' tipe variabel?

47

Maksud saya seperti ini:

<?php
    $number1 = 5;   // (Type 'Int')
    $operator1 = +; // (Type non-existent 'Operator')
    $number2 = 5;   // (Type 'Int')
    $operator2 = *; // (Type non-existent 'Operator')
    $number3 = 8;   // (Type 'Int')

    $test = $number1 $operator1 $number2 $operator2 $number3; //5 + 5 * 8.

    var_dump($test);
?>

Tetapi juga dengan cara ini:

<?php
    $number1 = 5;
    $number3 = 9;
    $operator1 = <;

    if ($number1 $operator1 $number3) { //5 < 9 (true)
        echo 'true';
    }
?>

Sepertinya tidak ada bahasa yang memiliki ini - apakah ada alasan bagus mengapa mereka tidak memilikinya?

kgongonowdoe
sumber
28
Secara umum apa yang ingin Anda lakukan akan dibahas oleh semua bahasa yang mendukung beberapa bentuk pemrograman meta dengan beberapa jenis lambda, penutupan atau fungsi anonim yang akan menjadi cara umum untuk mengimplementasikan fitur-fitur tersebut. Dengan bahasa di mana metode adalah warga negara kelas satu Anda akan dapat menggunakannya lebih atau kurang identik dengan variabel. Meskipun tidak dalam sintaksis sederhana yang Anda gunakan di sini karena di sebagian besar bahasa seperti itu harus diperjelas bahwa Anda sebenarnya ingin memanggil metode yang disimpan dalam variabel.
thorsten müller
7
@MartinMaat Bahasa fungsional banyak melakukan hal ini.
Thorbjørn Ravn Andersen
7
dalam haskell, operator adalah fungsi seperti fungsi lainnya. jenisnya (+)adalah Num a => a -> a -> aIIRC. Anda juga dapat mendefinisikan fungsi agar dapat ditulis infixed ( a + bbukan (+) a b)
sara
5
@enderland: Suntingan Anda benar-benar mengubah tujuan pertanyaan. Mulai dari menanyakan apakah ada bahasa, menjadi bertanya mengapa begitu sedikit ada. Saya pikir hasil edit Anda akan menghasilkan banyak pembaca yang bingung.
Bryan Oakley
5
@enderland: sementara itu benar, mengubah subjek sepenuhnya hanya berfungsi untuk membingungkan. Jika di luar topik, komunitas akan memilih untuk menutupnya. Tidak ada jawaban (pada saat saya menulis ini) yang masuk akal untuk pertanyaan seperti yang tertulis.
Bryan Oakley

Jawaban:

104

Operator hanyalah fungsi dengan nama lucu, dengan beberapa sintaks khusus.

Dalam banyak bahasa, beragam seperti C ++ dan Python, Anda dapat mendefinisikan ulang operator dengan mengganti metode khusus kelas Anda. Kemudian operator standar (mis. +) Bekerja sesuai dengan logika yang Anda berikan (mis. String gabungan atau menambahkan matriks atau apa pun).

Karena fungsi-fungsi yang mendefinisikan operator seperti itu hanyalah metode, Anda dapat meneruskannya seperti fungsi:

# python
action = int.__add__
result = action(3, 5)
assert result == 8

Bahasa lain memungkinkan Anda untuk secara langsung mendefinisikan operator baru sebagai fungsi, dan menggunakannya dalam bentuk infix.

-- haskell
plus a b = a + b  -- a normal function
3 `plus` 5 == 8 -- True

(+++) a b = a + b  -- a funny name made of non-letters
3 +++ 5 == 8 -- True

let action = (+)
1 `action` 3 == 4 -- True

Sayangnya, saya tidak yakin apakah PHP mendukung hal seperti itu, dan jika mendukungnya akan menjadi hal yang baik. Gunakan fungsi sederhana, lebih mudah dibaca daripada $foo $operator $bar.

9000
sumber
2
@tac: Ya, ini bagus dan bahkan bisa semacam porting ke bahasa lain :) Mengenai Haskell, yang paling saya rindukan adalah departemen ini adalah $operator yang meniadakan tanda kurung (terutama beberapa bersarang) - tetapi ini hanya dapat bekerja dengan non Fungsi -variadic, sehingga tidak termasuk misalnya Python dan Java. Komposisi fungsi OTOH unary dapat dilakukan dengan baik .
9000
6
Saya tidak pernah menjadi penggemar overloading operator karena ya, operator hanya fungsi dengan sintaks khusus, tetapi ada kontrak tersirat yang biasanya berlaku untuk operator yang tidak sesuai dengan fungsi. "+" misalnya, memiliki harapan tertentu - prioritas operator, komutatif, dll - dan bertentangan dengan harapan itu adalah rute yang pasti untuk membingungkan orang dan menghasilkan bug. Salah satu alasannya, meskipun saya suka javascript, saya lebih suka mereka membedakan antara + untuk penambahan dan penggabungan. Perl memilikinya di sana.
fool4jesus
4
Perhatikan bahwa Python memiliki operatormodul standar yang memungkinkan Anda menulis action = operator.add, dan membuatnya bekerja untuk semua jenis yang mendefinisikan +(bukan hanya int).
dan04
3
Dalam Haskell +bekerja pada Num typeclass sehingga Anda dapat menerapkan +untuk semua tipe data baru yang Anda buat, tetapi dengan cara yang sama seperti Anda melakukan hal lain misalnya fmap untuk functor. Merupakan hal yang wajar bagi Haskell untuk memungkinkan operator kelebihan beban sehingga mereka harus bekerja keras untuk tidak membiarkannya!
Martin Capodici
17
Tidak yakin mengapa orang menjadi terpaku pada kelebihan beban. Pertanyaan aslinya bukan tentang kelebihan muatan. Sepertinya bagi saya tentang operator sebagai nilai kelas satu. Untuk menulis $operator1 = +dan menggunakannya sebagai ekspresi, Anda tidak perlu menggunakan operator yang berlebihan sama sekali !
Andres F.
16

Ada banyak bahasa yang memungkinkan semacam pemrograman . Secara khusus, saya terkejut melihat tidak ada jawaban yang berbicara tentang keluarga bahasa Lisp .

Dari wikipedia:

Metaprogramming adalah penulisan program komputer dengan kemampuan untuk memperlakukan program sebagai datanya.

Kemudian dalam teks:

Lisp mungkin merupakan bahasa klasik dengan fasilitas pemrograman metaprogram, baik karena didahulukan secara historis maupun karena kesederhanaan dan kekuatan metaprogrammingnya.

Bahasa yang gampang

Intro cepat dibuat untuk Lisp berikut.

Salah satu cara untuk melihat kode adalah sebagai rangkaian instruksi: lakukan ini, lalu lakukan itu, lalu lakukan hal lain ini ... Ini adalah daftar! Daftar hal-hal yang harus dilakukan program. Dan tentu saja Anda dapat memiliki daftar di dalam daftar untuk mewakili loop dan seterusnya ..

Jika kita mewakili daftar yang berisi unsur-unsur a, b, c, d seperti ini: (abcd) kita mendapatkan sesuatu yang tampak seperti Lisp fungsi panggilan, di mana aadalah fungsi, dan b, c, dadalah argumen. Jika fakta khas "Hello World!" Program dapat ditulis seperti ini:(println "Hello World!")

Tentu saja b, catau ddapat berupa daftar yang mengevaluasi sesuatu juga. Berikut ini: (println "I can add :" (+ 1 3) )kemudian akan mencetak "" Saya dapat menambahkan: 4 ".

Jadi, sebuah program adalah serangkaian daftar bersarang, dan elemen pertama adalah fungsi. Berita baiknya adalah kita dapat memanipulasi daftar! Jadi kita bisa memanipulasi bahasa pemrograman.

Keuntungan Lisp

Lisps tidak begitu banyak bahasa pemrograman seperti halnya toolkit untuk membuat bahasa pemrograman. Bahasa pemrograman yang dapat diprogram.

Ini tidak hanya jauh lebih mudah di Lisps untuk membuat operator baru, juga hampir mustahil untuk menulis beberapa operator dalam bahasa lain karena argumen dievaluasi ketika diteruskan ke fungsi.

Misalnya dalam bahasa mirip C, misalkan Anda ingin menulis ifoperator sendiri, seperti:

my-if(condition, if-true, if-false)

my-if(false, print("I should not be printed"), print("I should be printed"))

Dalam hal ini kedua argumen akan dievaluasi dan dicetak, dalam urutan yang tergantung pada urutan evaluasi argumen.

Di Lisps, menulis operator (kami menyebutnya makro) dan menulis fungsi adalah tentang hal yang sama dan digunakan dengan cara yang sama. Perbedaan utama adalah bahwa parameter untuk makro tidak dievaluasi sebelum diteruskan sebagai argumen ke makro. Ini penting untuk dapat menulis beberapa operator, seperti di ifatas.

Bahasa dunia nyata

Menunjukkan betapa tepatnya sedikit di luar ruang lingkup di sini, tetapi saya mendorong Anda untuk mencoba pemrograman dalam satu Lisp untuk mempelajari lebih lanjut. Misalnya, Anda dapat melihat:

  • Skema , Lisp tua, cukup "murni" dengan inti kecil
  • Lisp umum, Lisp lebih besar dengan sistem objek terintegrasi dengan baik, dan banyak implementasi (standar-ANSI)
  • Racket Lisp yang diketik
  • Clojure favorit saya, contoh di atas adalah kode Clojure. Lisp modern yang berjalan di JVM. Ada beberapa contoh makro Clojure pada SO juga (tapi ini bukan tempat yang tepat untuk memulai. Saya akan melihat 4clojure , braveclojure atau clojure koans pada awalnya)).

Oh, ngomong-ngomong, Lisp berarti LISt Processing.

Mengenai contoh Anda

Saya akan memberikan contoh menggunakan Clojure di bawah ini:

Jika Anda dapat menulis suatu addfungsi di Clojure (defn add [a b] ...your-implementation-here... ), Anda dapat menamainya +seperti itu (defn + [a b] ...your-implementation-here... ). Ini sebenarnya apa yang dilakukan dalam implementasi nyata (tubuh fungsi sedikit lebih terlibat tetapi definisi dasarnya sama dengan yang saya tulis di atas).

Bagaimana dengan notasi infiks? Nah Clojure menggunakan prefixnotasi (atau Polandia), jadi kita bisa membuat infix-to-prefixmakro yang akan mengubah kode awalan menjadi kode Clojure. Yang sebenarnya sangat mudah (sebenarnya ini adalah salah satu latihan makro di koalisi clojure)! Itu juga bisa dilihat di alam liar, misalnya lihat Incanter $=macro .

Ini adalah versi paling sederhana dari koans yang dijelaskan:

(defmacro infix [form]
  (list (second form) (first form) (nth form 2)))

;; takes a form (ie. some code) as parameter
;; and returns a list (ie. some other code)
;; where the first element is the second element from the original form
;; and the second element is the first element from the original form
;; and the third element is the third element from the original form (indexes start at 0)
;; example :
;; (infix (9 + 1))
;; will become (+ 9 1) which is valid Clojure code and will be executed to give 10 as a result

Untuk mengarahkan poin lebih jauh, beberapa Lisp mengutip :

“Bagian dari apa yang membuat Lisp istimewa adalah ia dirancang untuk berkembang. Anda dapat menggunakan Lisp untuk menentukan operator Lisp baru. Sebagai abstraksi baru menjadi populer (pemrograman berorientasi objek, misalnya), selalu ternyata mudah untuk mengimplementasikannya dalam Lisp. Seperti DNA, bahasa seperti itu tidak ketinggalan gaya. ”

- Paul Graham, ANSI Common Lisp

“Memprogram di Lisp seperti bermain dengan kekuatan primordial alam semesta. Rasanya seperti kilat di antara ujung jari Anda. Tidak ada bahasa lain yang terasa dekat. ”

- Glenn Ehrlich, Jalan Menuju Lisp

nha
sumber
1
Perhatikan bahwa metaprogramming, meskipun menarik, tidak diperlukan untuk mendukung apa yang ditanyakan OP. Bahasa apa pun dengan dukungan untuk fungsi kelas satu sudah cukup.
Andres F.
1
Contoh untuk pertanyaan OP: (let ((opp # '+)) (cetak (berlaku opp' (1 2))))
Kasper van den Berg
1
Tidak disebutkan Common Lisp?
coredump
3
Saya ingat pada diskusi panel tentang bahasa, Ken berbicara tentang prioritas di APL dan menyimpulkan dengan, "Saya hampir tidak pernah menggunakan tanda kurung sama sekali!" Dan seseorang dari hadirin berteriak, "itu karena Dick menggunakan semuanya!"
JDługosz
2
Dalam bahasa gaya C ++, Anda bisa menerapkan kembali if, tetapi Anda harus membungkus thendan elseargumen dengan lambdas. PHP dan JavaScript miliki function(), C ++ memiliki lambdas, dan ada ekstensi Apple ke C dengan lambdas.
Damian Yerrick
9

$test = $number1 $operator1 $number2 $operator2 $number3;

Sebagian besar implementasi bahasa memiliki langkah di mana pengurai menganalisis kode Anda dan membangun pohon darinya. Jadi misalnya ekspresi 5 + 5 * 8akan diuraikan sebagai

  +
 / \
5   *
   / \
  8   8

terima kasih kepada pengetahuan kompiler tentang prioritas. Jika Anda memberinya variabel sebagai pengganti operator, ia tidak akan mengetahui urutan operasi yang tepat sebelum menjalankan kode. Untuk sebagian besar implementasi yang akan menjadi masalah serius, jadi sebagian besar bahasa tidak mengizinkannya.

Anda tentu saja bisa memahami bahasa di mana parser hanya mem-parsing di atas sebagai urutan ekspresi dan operator, untuk diurutkan dan dievaluasi saat runtime. Mungkin tidak ada banyak aplikasi untuk ini.

Banyak bahasa scripting memungkinkan evaluasi ekspresi sewenang-wenang (atau setidaknya ekspresi aritmatika sewenang-wenang seperti dalam kasus expr) pada saat runtime. Di sana Anda hanya bisa menggabungkan angka dan operator Anda menjadi satu ekspresi dan biarkan bahasa mengevaluasi itu. Dalam PHP (dan banyak lainnya) fungsi itu disebut eval.

$test = eval("$number1 $operator1 $number2 $operator2 $number3");

Ada juga bahasa yang memungkinkan pembuatan kode pada waktu kompilasi. The ekspresi mixin di D datang ke pikiran saya, di mana saya percaya Anda bisa menulis sesuatu seperti

test = mixin("number1 " + operator1 + " number2 " + operator2 + "number3");

Di sini operator1dan operator2harus berupa konstanta string yang diketahui pada waktu kompilasi, misalnya parameter templat. number1, number2dan number3dibiarkan sebagai variabel runtime normal.

Jawaban lain sudah membahas berbagai cara bagaimana operator dan suatu fungsi kurang lebih sama, tergantung pada bahasanya. Tetapi biasanya ada perbedaan sintaksis antara simbol operator infix bawaan +dan nama yang bisa dipanggil operator1. Saya akan menyerahkan detailnya ke jawaban-jawaban lainnya.

MvG
sumber
+1 Anda harus mulai dengan "itu mungkin dalam PHP dengan eval()konstruksi bahasa " ... Secara teknis ini memberikan hasil yang diinginkan yang ditanyakan dalam pertanyaan.
Armfoot
@Armfoot: Sulit untuk mengatakan di mana fokus pertanyaan itu berada. Judul menekankan aspek "variabel operator tipe", dan evaltidak menjawab aspek itu, karena untuk evaloperator hanya string. Oleh karena itu saya mulai dengan penjelasan mengapa variabel yang diketik operator akan menyebabkan masalah, sebelum saya mulai membahas alternatif.
MvG
Saya memahami poin Anda, tetapi pertimbangkan bahwa dengan memasukkan semuanya ke dalam string, pada dasarnya Anda menyiratkan bahwa tipe variabel tidak lagi relevan (karena PHP digunakan untuk memberikan contoh, ini tampaknya menjadi fokus pertanyaan), dan pada akhirnya, Anda dapat menempatkan mereka dengan cara yang sama seolah-olah beberapa dari variabel tersebut adalah "operator tipe" sambil mendapatkan hasil yang sama ... Itulah sebabnya saya yakin saran Anda memberikan jawaban paling akurat untuk pertanyaan tersebut.
Armfoot
2

Algol 68 memiliki fitur itu. Contoh Anda dalam Algol 68 akan terlihat seperti ini:

int number1 = 5;                              ¢ (Ketik 'Int') ¢
op operator1 = int ( int a , b ) a + b ; ¢ (Ketik 'Operator' yang tidak ada) ¢ operator
prio1 = 4; int number2 = 5;                              ¢ (Ketik 'Int') ¢ op operator2 = int ( int a , b ) a * b ;  ¢

(Ketik 'Operator' yang tidak ada) ¢
prio operator2 = 5;
int number3 = 8;                              ¢ (Ketik 'Int') ¢

int uji = number1 operator1 number2 operator2 number3 ; ¢ 5 + 5 * 8. ¢

var_dump ( test );

Contoh kedua Anda akan terlihat seperti ini:

int number4 = 9;
op operator3 = bool ( int a , b ) a < b ; operator
prio3 = 3; jika number1 $ operator3 number4 kemudian ¢ 5 <9 (benar) ¢ cetak ( benar ) fi


Anda akan mencatat bahwa simbol-simbol operator didefinisikan dan ditetapkan metode tubuh yang berisi operasi yang diinginkan. Operator dan operan mereka semua diketik, dan operator dapat diberi prioritas sehingga evaluasi akan terjadi dalam urutan yang benar. Anda mungkin juga memperhatikan bahwa ada sedikit perbedaan dalam pengelompokan antara simbol operator dan simbol variabel.

Sebenarnya, meskipun bahasa tersebut ditulis menggunakan font, mesin-mesin saat itu tidak dapat menangani font (pita kertas dan kartu berlubang), dan stropping digunakan. Program mungkin akan dimasukkan sebagai:

'INT' NUMBER4 = 9;
'OP' 'OPERATOR3' = 'BOOL' ('INT' A,B) A < B;
'PRIO' 'OPERATOR3' = 3;
'IF' NUMBER1 'OPERATOR3' NUMBER4 'THEN' 'C' 5 < 9 'C'
PRINT('TRUE')
'FI'

Anda juga dapat memainkan game menarik dengan bahasa tersebut ketika Anda dapat menentukan simbol Anda sendiri untuk operator, yang pernah saya eksploitasi beberapa tahun yang lalu ... [2].


Referensi:

[1] Pengantar Informal ke Algol 68 oleh CHLindsey dan SG van der Meulen, Belanda Utara, 1971 .

[2] Algol 68 Frasa, Alat bantu penulisan kompiler di Algol 68 , BC Tompsett, Konferensi Internasional tentang Aplikasi Algol 68, Di Universitas East Anglia, Norwich, Inggris, 1976 ..

Brian Tompsett - 汤 莱恩
sumber