Değişken tipi 'işleç' olan çok az dil neden var?


47

Bu şekilde demek istiyorum:

<?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);
?>

Ancak bu şekilde:

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

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

Herhangi bir dilde bu var gibi görünmüyor - neden olmasın iyi bir nedeni var mı?


28
Genel olarak yapmak istedikleriniz, bu tür özellikleri uygulamanın ortak yolu olan bazı türdeki lambdalar, kapaklar veya adsız işlevlerle bir tür meta programlamayı destekleyen tüm diller tarafından ele alınacaktır. Yöntemlerin birinci sınıf vatandaş olduğu dillerle, bunları değişkenlerle aynı ya da daha az aynı şekilde kullanabilirsiniz. Burada kullandığınız basit sözdiziminde olmasa da, çoğu dilde değişkende depolanan yöntemi çağırmak istediğinizi açıkça belirtmeniz gerekir.
thorsten müller

7
@MartinMaat İşlevsel diller bunu çok yapar.
Thorbjørn Ravn Andersen

7
haskell'de operatörler, diğer fonksiyonlar gibi fonksiyonlardır. türü (+)olan Num a => a -> a -> aIIRC. ayrıca işlevleri tanımlayarak eklenmiş olarak yazılabilirler ( a + byerine (+) a b)
sara

5
@enderland: Düzenlemeniz sorunun amacını tamamen değiştirdi. Herhangi bir dilin olup olmadığını sormaktan, neden bu kadar azının mevcut olduğunu sormaktan geçti. Düzenlemenizin bir sürü kafası karışmış okuyucuya yol açacağını düşünüyorum.
Bryan Oakley

5
@enderland: bu doğru olsa da, tamamen konuyu değiştirmek sadece kafa karıştırmak için hizmet vermektedir. Konu dışıysa, topluluk kapatmak için oy kullanacaktır. Cevapların hiçbiri (bunu yazdığımda) soruya yazılı olarak bir anlam ifade etmiyor.
Bryan Oakley

Yanıtlar:


104

Operatörler sadece komik isimler altında fonksiyonlar, bazı özel sözdizimleri var.

C ++ ve Python gibi çeşitli dillerde, sınıfınızın özel yöntemlerini geçersiz kılarak operatörleri yeniden tanımlayabilirsiniz. Ardından standart operatörler (örn. +) Sağladığınız mantığa göre çalışırlar (örn. Dizeleri birleştirerek veya matris veya her neyse).

Bu tür operatör tanımlayan işlevler yalnızca yöntem olduğundan, onları işlev olarak yaptığınız gibi geçirebilirsiniz:

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

Diğer diller, yeni operatörleri doğrudan işlevler olarak tanımlamanıza ve bunları ek olarak kullanmanıza olanak tanır.

-- 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

Ne yazık ki, PHP'nin böyle bir şeyi destekleyip desteklemediğinden emin değilim ve desteklemesi iyi bir şey olacaktır. Düz bir işlev kullanın, daha okunabilir $foo $operator $bar.


2
@tac: Evet, bu çok hoş ve hatta diğer dillere de aktarılabilir. :) Haskell'e göre, en çok özlediğim şey bu bölüm $parantezleri gizleyen operatördür (özellikle çoklu yuvalanmış) - ancak bu yalnızca -variadik fonksiyonlar, örneğin Python ve Java hariç. OTOH unary fonksiyon bileşimi güzelce yapılabilir .
9000,

6
Daha önce hiç operatörün aşırı yüklenme hayranı olmadım, çünkü evet, bir operatör sadece özel bir sözdizimine sahip bir fonksiyondur, ancak genellikle fonksiyonlara uymayan operatörler için geçerli bir zımni sözleşme vardır. Örneğin, "+", belirli beklentilere sahiptir - operatör önceliği, değişebilirlik, vb. - ve bu beklentilere karşı çıkmak insanları şaşırtmak ve böcek üretmek için kesin bir yoldur. Bir nedeni, javascript'i sevmeme rağmen, toplama ve birleştirme için + arasında ayrım yapmalarını tercih ederdim. Perl oradaydı.
fool4jesus

4
Python'un operatoryazmanıza izin verecek standart bir modüle action = operator.addsahip olduğunu ve tanımlayan herhangi bir tür için çalışmasını sağlayın +(yalnızca değil int).
dan04

3
Haskell'de +Num typeclass üzerinde çalışır, böylece +oluşturduğunuz herhangi bir yeni veri türü için uygulayabilirsiniz , ancak bir functor için fmap gibi başka bir şey yaptığınız gibi. Haskell için operatörün aşırı yüklenmesine izin vermek çok doğal bir seçenektir, izin vermemek için çok çalışmak zorunda kalırlar!
Martin Capodici 23.03

17
İnsanların neden aşırı yüklenmeye zorlandıklarından emin değiliz. Asıl soru aşırı yükleme ile ilgili değil. Bana operatörler hakkında birinci sınıf değerler olarak görünüyor. Yazmak $operator1 = +ve sonra bir ifade kullanmak için operatör aşırı yüklemesini kullanmanıza gerek yoktur !
Andres F.

16

Bir çeşit meta programlamaya izin veren birçok dil var . Özellikle, Lisp dil ailesi hakkında konuşacak bir cevap görünmediğine şaşırdım .

Wikipedia'dan:

Metaprogramming, programları kendi verileri olarak ele alma becerisine sahip bilgisayar programları yazmaktır.

Metinde daha sonra:

Lisp muhtemelen hem tarihsel önceliği hem de metaprogramlamanın basitliği ve gücünden dolayı metaprogramlama olanaklarına sahip olan en iyi dildir.

Lisp dilleri

Lisp’e hızlı bir şekilde giriş yapılır.

Kodu görmenin bir yolu bir talimat paketidir: bunu yapın, sonra yapın, sonra başka bir şey yapın ... Bu bir liste! Programın yapması gerekenlerin bir listesi. Ve tabii ki döngüler ve benzerlerini temsil etmek için listelerde listeler olabilir.

Biz a, b, c, d böyle unsurları içeren bir liste temsil ederse: (abcd) bir şeyler almak bir Lisp işlev çağrısında gibi görünüyor afonksiyonudur ve b, c, dbağımsız değişkendir. Gerçekte tipik "Merhaba Dünya!" program şöyle yazılabilir:(println "Hello World!")

Tabii ki b, cya dyanı şey değerlendirmek listeleri olabilir. Aşağıdaki: (println "I can add :" (+ 1 3) )sonra "" ekleyebilirim: 4 "yazdıracaktı.

Dolayısıyla, program iç içe geçmiş listelerin bir dizisidir ve ilk öğe bir işlevdir. İyi haber şu ki listeleri değiştirebiliriz! Böylece programlama dillerini değiştirebiliriz.

Lisp avantajı

Lisps, programlama dilleri yapmak için bir araç takımı kadar programlama dili değildir. Programlanabilir bir programlama dili.

Bu, Lisps'ta yeni operatörler yapmak için çok daha kolay değil, bazı operatörleri başka dillerde yazmak da neredeyse imkansız, çünkü fonksiyona geçildiğinde argümanlar değerlendiriliyor.

Örneğin, C benzeri bir dilde diyelim ki ifkendinize bir operatör yazmak isteyin ,

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

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

Bu durumda, her iki argüman, argümanların değerlendirme sırasına bağlı bir sırada değerlendirilecek ve yazdırılacaktır.

Lisps'ta bir operatör yazmak (buna makro diyoruz) ve bir fonksiyon yazmak aynı şeyle ilgili ve aynı şekilde kullanılıyor. Makro için bu parametreleri olmak önemli fark edilir değil makro argümanlar olarak iletilen önce değerlendirilir. ifYukarıdaki gibi bazı operatörleri yazabilmek için bu şarttır .

Gerçek dünya dilleri

Burada tam olarak ne kadar kapsam dışında olduğunu gösteriyorum, ancak daha fazla bilgi edinmek için bir Lisp'te programlama yapmayı denemenizi tavsiye ediyorum. Mesela şuna bir bakabilirsiniz:

  • Şema , küçük çekirdekli yaşlı, oldukça "saf" bir Lisp
  • Common Lisp, iyi entegre bir nesne sistemine sahip daha büyük bir Lisp ve birçok uygulama (ANSI standardizedir)
  • Yazılı bir Lisp raketi
  • En sevdiğim Clojure , yukarıdaki örnekler Clojure koduydu. JVM'de çalışan modern bir Lisp. Orada üzerinde Clojure makro bazı örnekler SO yanı (ancak bu başlamak için doğru yer değil. Ben bakardım 4clojure , braveclojure veya Clojure koans ilk başta)).

Oh ve bu arada, Lisp LISt İşleme anlamına geliyor.

Örneklerinizle ilgili olarak

Aşağıdaki Clojure kullanarak örnekler vereceğim:

Bir yazabilirsiniz Eğer addClojure işlevi (defn add [a b] ...your-implementation-here... ), bunu adlandırabilirsiniz +öylesine gibi (defn + [a b] ...your-implementation-here... ). Aslında, gerçek uygulamada yapılan şey budur (işlevin gövdesi biraz daha fazladır, ancak tanım aslında yukarıda yazdığımla aynıdır).

Peki ya ek gösterimi? Clojure bir prefix(veya Lehçe) gösterimi kullanır, bu yüzden infix-to-prefixöneki kodu Clojure koduna dönüştürecek bir makro yapabiliriz . Bu aslında şaşırtıcı derecede kolay (aslında clojure koanlarındaki makro egzersizlerden biri)! Ayrıca vahşi doğada da görülebilir, örneğin bkz. Incanter $=makrosu .

İşte anlatılan koanların en basit hali:

(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

Noktayı daha da ileri götürmek için bazı Lisp alıntıları :

“Lisp'i farklı kılan şeyin bir kısmı, gelişmek üzere tasarlanmış olmasıdır. Lisp'i yeni Lisp operatörleri tanımlamak için kullanabilirsiniz. Yeni soyutlamalar popüler hale geldiğinde (örneğin, nesne yönelimli programlama), bunları Lisp'te uygulamak her zaman kolay olur. DNA gibi, böyle bir dilin de tarzı yok. ”

- Paul Graham, ANSI Ortak Lisp

“Lisp'te programlama, evrenin ilkel güçleriyle oynamak gibidir. Parmak uçlarınız arasında yıldırım gibi geliyor. Başka hiçbir dil bile yakın hissetmiyor. ”

- Glenn Ehrlich, Lisp'e Giden Yol


1
OP'nin sorduğu şeyi desteklemek için metaprogramlamanın ilginç olsa da gerekli olmadığını unutmayın. Birinci sınıf fonksiyonları destekleyen herhangi bir dil yeterlidir.
Andres F.

1
OP'in sorusuna örnek: (let ((opp # '+))) (yazdır (opp' (1 2))))
Kasper van den Berg

1
Common Lisp'ten söz yok mu?
coredump,

3
Dillerle ilgili bir panelde, Ken'in APL'de öncelikten bahsettiğini ve "Hiç parantez kullanmam!" Ve seyircilerden biri bağırdı, "çünkü Dick hepsini kullandı!"
JDługosz

2
C ++ tarzı bir dilde yeniden ifuygulayabilirsiniz, ancak thenve elseargümanlarını lambdalarla sarmanız gerekir . PHP ve JavaScript function(), C ++ 'ın lambdası var ve C' ye lambdalı bir Apple uzantısı var .
Damian Yerrick

9

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

Çoğu dil uygulamasında, bir çözümleyici kodunuzu analiz ettiği ve ondan bir ağaç oluşturduğu bir adım vardır. Bu nedenle, örneğin ifade 5 + 5 * 8olarak ayrıştırılır

  +
 / \
5   *
   / \
  8   8

derleyicinin öncelikler hakkındaki bilgisi sayesinde. Operatör yerine değişkenleri beslerseniz, kodu çalıştırmadan önce işlemlerin uygun sıralamasını bilemezdi. Ciddi bir problem olacak uygulamaların çoğu için, çoğu dil buna izin vermiyor.

Elbette, çözümleyicinin, çalışma zamanında sıralanıp değerlendirilmek üzere bir ifadeler ve işleçler dizisi olarak yalnızca ayrıştırdığı bir dil tasarlayabilirsiniz. Muhtemelen bunun için çok fazla bir uygulama yoktur.

Birçok kodlama dili expr, çalışma zamanında rastgele ifadelerin (veya en azından keyfi rasgele ifadelerin) değerlendirilmesine izin verir . Orada numaralarınızı ve operatörlerinizi tek bir ifadede birleştirebilir ve dilin bunu değerlendirmesine izin verebilirsiniz. PHP'de (ve diğerleri) bu fonksiyon denir eval.

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

Derleme zamanında kod oluşturmaya izin veren diller de vardır. D mixin ifadesi ben gibi bir şey yazabilirsiniz inanıyoruz aklıma, geliyor

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

Burada operator1ve operator2derleme zamanında bilinen dize sabitleri olması gerekir, örneğin şablon parametreleri. number1, number2Ve number3normal bir çalışma zamanı değişkenler olarak bırakılmıştır.

Diğer cevaplar, bir operatörün ve işlevin diline bağlı olarak nasıl aynı veya az aynı şey olduğunu çeşitli şekillerde tartışmıştı. Ancak, genellikle, yerleşik bir gömme işleç simgesi gibi +ve adlandırılmış bir çağrılabilir gibi bir sözdizimsel fark vardır operator1. Detayları diğer cevaplara bırakacağım.


+1 " eval()Dil yapılı dilde PHP'de mümkün " ... ile başlamalısınız . Teknik olarak, sorulan soruya tam olarak istenen sonucu verir.
Kol Ayağı

@Armfoot: Sorunun odağının nerede olduğunu söylemek zor. Başlık, "tür değişkeni değişkeni" yönünü vurgular ve operatörler için yalnızca dizeler evalolduğundan, bu yönü yanıtlamaz eval. Bu nedenle, operatör tarafından yazılan değişkenlerin neden alternatifleri tartışmaya başlamadan önce sorunlara neden olacağını açıklamaya başladım.
MvG

Puanlarınızı anlıyorum, ancak her şeyi bir dizgiye yerleştirerek, temelde değişken türlerinin artık alakalı olmadığını ima ediyorsunuz (PHP örnekleme için kullanıldığından beri, bu sorunun odağı gibi görünüyor) ve sonunda, siz aynı sonuçları elde ederken bu değişkenlerden bazıları "type operatörü" gibi aynı şekilde yerleştirebilirler ... Bu yüzden önerinizin soruya en doğru cevabı verdiğine inanıyorum.
Armfoot

2

Algol 68 tam olarak bu özelliğe sahipti. Algol 68'deki örneğiniz şöyle görünür:

int sayı1 = 5;                              ¢ (Tip 'Int') ¢
op İşlem1 = int ( int bir , b ) bir + b ; ¢ (olmayan 'Operatör' Tip) ¢
prio İşlem1 = 4;
int sayı2 = 5;                              ¢ (Tip 'Int') ¢
op İşlem2 = int ( int bir , b ) bir * b ;  ¢(Olmayan 'Operatör' Tip) ¢
prio İşlem2 = 5;
int sayı3 = 8;                              ¢ (Tür 'Int') ¢

int testi = sayı1 operatör1 sayı2 operatör2 sayı3 ; ¢ 5 + 5 * 8. ¢

var_dump ( test );

İkinci örneğiniz şöyle görünür:

int sayı4 = 9;
op operatörü3 = bool ( int a , b ) a < b ;
prio operatörü3 = 3;
eğer sayı1 $ İşlem3 number4 sonra ¢ 5 <9 (doğru) ¢
baskı ( gerçek )
fi

Operatör sembollerinin tanımlanmış ve istenen işlemi içeren yöntem gövdeleri atandığını not edersiniz. İşleçler ve işlenenlerin hepsi yazılır ve işleçlere öncelikler atanabilir, böylece değerlendirme doğru sırada gerçekleşir. Operatör sembolü ile değişken sembolü arasında yazı tipinde küçük bir fark olduğunu da fark edebilirsiniz.

Aslında, dil yazı tipi kullanılarak yazılmış olsa da, günün makineleri yazı tiplerini (kağıt bant ve delikli kartlar) kaldıramadı ve vuruş kullanıldı. Program muhtemelen şu şekilde girilecektir:

'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'

Aynı zamanda, yıllar önce sömürdüğüm operatörler için kendi sembollerinizi tanımlayabildiğiniz zaman, dille ilginç oyunlar oynayabilirsiniz ... [2].


Referanslar:

[1] Algol 68’e resmi olmayan giriş CHLindsey ve SG van der Meulen, Kuzey Hollanda, 1971 .

[2] Algol 68 İfadeler, Algol 68 yardım derleyici yazma için bir araç Algol 68 Uygulamaları uzerine, M.Ö. Tompsett, Uluslararası Konferans, East Anglia, Norwich, Birleşik Krallık, 1976 en Üniversitesi .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.