Kullanıcı tanımlı operatörler neden daha yaygın değil?


94

İşlevsel dillerden özlediğim bir özellik, işleçlerin yalnızca işlevler olduğu fikridir, bu nedenle özel bir işleç eklemek, genellikle bir işlev eklemek kadar basittir. Prosedürel dillerin birçoğu operatörün aşırı yüklenmesine izin verir, bu nedenle bazı durumlarda operatörler hala işlev görür (bu, operatörün bir şablon parametresinde bir dize olarak geçirildiği D için çok doğrudur ).

Operatör aşırı yüklenmesine izin verilen yerlerde, ek, özel operatörler eklemek genellikle önemsiz görünmektedir. Özel operatörlerin öncelikli kurallar nedeniyle ek gösterimi ile iyi çalışmadıklarını iddia eden bu blog gönderisini buldum , ancak yazar bu soruna birkaç çözüm getiriyor.

Etrafıma baktım ve dilde özel operatörleri destekleyen hiçbir prosedürel dil bulamadım. Hack'ler (C ++ 'daki makrolar gibi) var, ancak bu dil desteği ile neredeyse aynı değil.

Bu özelliğin uygulanması oldukça önemsiz olduğu için neden daha yaygın değil?

Bazı çirkin kodlara yol açabileceğini anlıyorum, ancak geçmişte dil tasarımcılarının kolayca kötüye kullanılabilecek yararlı özellikler eklemesini engellemedi (makrolar, üçlü operatör, güvensiz işaretçiler).

Gerçek kullanım durumları:

  • Eksik operatörleri uygulayın (örneğin Lua'da bitsel operatörler yok)
  • Mimik D'ler ~(dizi birleştirme)
  • DSL'ler
  • |Unix boru tarzı sözdizimi şekeri olarak kullanın (koroinler / jeneratörler kullanarak)

Ben de dillere ilgileniyorum yapmak özel operatörleri izin ama daha çok ilgileniyorum neden o dışlanmıştır. Kullanıcı tanımlı operatörler eklemek için bir betik dili oluşturmayı düşündüm, ancak bir yerde görmediğimi fark ettiğimde kendimi durdurdum, bu nedenle dil tasarımcılarının benden daha akıllıca olmasına izin vermemesinin iyi bir nedeni olabilir.


4
Bu soru hakkında devam eden bir Reddit tartışması var .
Dinamik

2
@ dimatura: R hakkında fazla bir şey bilmiyorum, ancak özel operatörler hala çok esnek görünmüyorlar (örneğin uygunluğu, kapsamı, aşırı yüklemeyi vb. tanımlamıyorlar, bu da neden fazla kullanılmadıklarını açıklıyor). Bu, başka dillerde, kesinlikle özel infix operatörlerini çok kullanan Haskell'de farklı . Oldukça özel ek desteğine sahip bir prosedürel dilin bir örneği Nimrod ve tabii ki Perl de izin veriyor .
Aralık'ta

4
Başka bir seçenek daha var, Lisp operatörler ve fonksiyonlar arasında hiçbir fark yok.
BeardedO

4
Henüz Prolog söz eden yok, operatörler fonksiyonları için (evet hatta matematik olanlar) sadece sözdizimsel şeker ve özel operatörleri tanımlamasına olanak veren başka bir dil ile özel öncelik.
David Cowden

2
@BeardedO, Lisp'ta hiç infix operatörü yok. Onları tanıttığınızda, öncelikli olan tüm meseleleri ele almanız gerekir.
SK-mantığı

Yanıtlar:


134

Dil tasarımında, tabanca karşıt iki düşünce okulu vardır. Birincisi, programcıların daha az kısıtlamayla daha iyi kod yazmaları, diğeri daha çok kısıtlamayla daha iyi kod yazmalarıdır. Bence gerçek şu ki, iyi deneyimli programcılar daha az kısıtlamayla gelişiyor, ancak bu kısıtlamalar yeni başlayanların kod kalitesinde fayda sağlıyor.

Kullanıcı tanımlı operatörler deneyimli ellerde çok şık kodlar ve yeni başlayanlar için son derece berbat kodlar yapabilir. Bu nedenle, dilinizin bunları içerip içermediği, dil tasarımcınızın düşünce okuluna bağlıdır.


23
İki düşünce okulunda, dil tasarımcılarının neden birini seçip diğerini seçtikleri sorusuna en doğrudan (ve muhtemelen en doğru) cevabı verdikleri için plus.google.com/110981030061712822816/posts/KaSKeg4vQtz ayrıca +1 . Ayrıca, tezinize dayanarak, daha az dilin izin verdiğini tahmin edebileceğinizi söyleyebilirim çünkü geliştiricilerin daha küçük bir kısmı diğerlerinden daha yeteneklidir (her şeyin yüzde üst kısmı her zaman tanım gereği azınlık olacaktır)
Jimmy Hoffa

12
Bence bu cevap belirsiz ve sadece marjinal olarak soru ile ilgili. (Ayrıca, benim karakterizasyonunuzun deneyimlerime aykırı olduğu için yanlış olduğunu düşünüyorum ama bu önemli değil.)
Konrad Rudolph

1
@KonradRudolph'un dediği gibi, bu soruya gerçekten cevap vermiyor. Prolog gibi bir dil atın; operatörlerle birlikte ek veya yerleştirme sırasındaki önceliğini tanımlamak da dahil olmak üzere istediğiniz her şeyi yapmanıza izin verin. Özel operatörler yazabildiğiniz gerçeğinin, hedef kitlenin beceri seviyesi ile ilgisi olduğunu sanmıyorum, çünkü Prolog'un amacı mümkün olduğunca mantıklı bir şekilde okumaktır. Özel operatörlerin eklenmesi, mantıklı bir şekilde okunan programlar yazmanıza izin verir (sonuçta bir prolog programı yalnızca bir sürü mantıksal ifadedir).
David Cowden

Bu Stevia rantını nasıl özledim? Oh adamım, yer imi
George Mauer

Eğer dilbilgileri , derleyicinin ne zaman çeşitli çıkarımlar yapması gerektiğini (örneğin bir Formatyönteme otomatik boks argümanları ) ve ne zaman reddetmesi gerektiğini söyleyen araçlar verirse, programcıların en iyi kodu yazma ihtimalinin yüksek olduğunu düşünürsem, hangi felsefeye düşebilirim? örneğin, otomatik boks argümanları ReferenceEquals). Ne kadar yetenek dili, programcılara belirli çıkarımların ne zaman uygunsuz olacağını söylerken, daha güvenli bir şekilde uygun durumlarda uygun çıkarımlar sunabilir.
supercat

83

Dizileri ~ veya "myArray.Concat (secondArray)" ile birleştirerek arasında bir seçim yapıldığında, muhtemelen ikincisini tercih ederim. Neden? Çünkü ~ tamamen anlamsız bir karakterdir, sadece yazılan belirli projede verilen - dizi birleştirmenin anlamı olan - anlamı.

Temel olarak, dediğiniz gibi, operatörler yöntemlerden farklı değildir. Ancak, kod akışının anlaşılmasına katkıda bulunan okunaklı, anlaşılır isimler verilebilirken, operatörler opak ve durumsaldır.

Bu yüzden PHP'nin .operatörünü (string birleştirme) veya Haskell veya OCaml'deki operatörlerin çoğundan da hoşlanmıyorum , ancak bu durumda, evrensel olarak kabul görmüş bazı standartlar işlevsel diller için ortaya çıkmaktadır.


27
Aynısı tüm operatörler için de söylenebilir. Onları iyi yapan ve aynı zamanda kullanıcı tanımlı operatörleri iyi yapan da (makul bir şekilde tanımlanmışlarsa) şudur: Bunlar, sadece bir karakter olmaları için kullanılan karaktere rağmen, yaygın kullanım ve muhtemelen anımsatıcı özellikler derhal kaynak kodunda anlam kazanır. aşina olanlar için açık. Demekki cevapınız IMHO'ya inandırıcı değil.

33
Sonunda bahsettiğim gibi, bazı operatörler evrensel tanınırlığa sahiptir (standart aritmentik semboller, bitsel kaymalar, AND / OR vb. Gibi) ve bu yüzden de açıklıkları opaklıklarından ağır basar. Ancak keyfi operatörleri tanımlayabilmek her iki dünyanın da en kötüsünü koruyor gibi görünüyor.
Avner Shahar-Kashtan

11
Öyleyse, örneğin dil düzeyinde tek harfli isimleri yasaklamaktan da hoşlanıyorsunuz? Daha genel olarak, dilde hiçbir şeyden daha fazla zarar verebilecek bir şeye izin vermemek? Bu, dil tasarımına yönelik geçerli bir yaklaşımdır, ancak yalnızca biri değildir, bu yüzden bunu öngöremeyeceğinizi sanmıyorum.

9
Özel operatörlerin bir özellik "eklediğini" kabul etmiyorum, bir kısıtlamayı kaldırıyor. İşleçler genellikle yalnızca işlevlerdir, bu nedenle işleçler için statik bir sembol tablosu yerine, bunun yerine dinamik, bağlama bağımlı bir kişi kullanılabilir. Çünkü, bu operatör aşırı yükler nasıl işlendiğini hayal +ve <<kesinlikle üzerinde tanımlanmamış Object(I "operatör için + eşleşme ..." C çıplak sınıfına yapıyor ++ olsun).
Aralık'ta

10
Bunu çözmenin uzun zaman alması, kodlamanın genellikle bir bilgisayarın sizin için bir şeyler yapmasıyla ilgili olmadığı, insanların hem bilgisayarların hem de bilgisayarlardan biraz daha önemli olduğu bir belge üretmesidir. Bu cevap tam olarak doğrudur, Eğer bir sonraki kişi (ya da 2 yıl içinde) ne anlama geldiğini bulmak için 10 saniye harcamak zorunda kalırsa, bunun yerine bir yöntem çağrısında yazarak 10 saniye geçirmiş olabilirsiniz.
Bill K,

71

Bu özelliğin uygulanması oldukça önemsiz olduğu için neden daha yaygın değil?

Senin öncülün yanlış. O var değil “uygulamak için oldukça önemsiz”. Aslında, bir çanta dolusu sorun getiriyor.

Yazıdaki önerilen "çözümlere" göz atalım:

  • Öncelik yok . Yazarın kendisi “Öncelik kurallarını kullanmamak bir seçenek değildir” diyor.
  • Anlamsal farkındalık ayrıştırma . Makalede yazdığı gibi, bu derleyicinin çok fazla anlamsal bilgiye sahip olmasını gerektirir. Makale aslında bunun için bir çözüm sunmuyor ve size söyleyeyim, bu basit değil. Derleyiciler, güç ve karmaşıklık arasında bir denge oluşturacak şekilde tasarlanmıştır. Özellikle, yazar ilgili bilgiyi toplamak için bir ayrıştırma adımından bahseder, ancak ayrıştırma öncesi verimsizdir ve derleyiciler ayrıştırma işlemlerini en aza indirmek için çok çaba sarf eder.
  • Özel infix operatörü yok . Bu bir çözüm değil.
  • Hibrit çözüm . Bu çözüm, semantik bilinçli ayrıştırmanın dezavantajlarının çoğunu (tümünü değil) taşır. Özellikle, derleyici bilinmeyen belirteçleri özel operatörleri temsil eden potansiyel olarak ele almak zorunda kaldığından, genellikle anlamlı hata mesajları üretemez. Ayrıca, söz konusu operatörün tanımının ayrıştırma işlemine devam etmesini (tip bilgisi toplamak için vb.) Bir kez daha ilave bir ayrıştırma geçişi gerektirmesini gerektirebilir.

Sonuç olarak, bu hem ayrıştırıcı karmaşıklığı hem de performans açısından uygulamak için pahalı bir özelliktir ve birçok fayda sağlayacağı açık değildir. Elbette, yeni operatörleri tanımlamanın bazı faydaları vardır, ancak çekişmeli olsalar bile (sadece yeni operatörlere sahip olmanın iyi bir şey olmadığını söyleyen diğer cevaplara bakın).


2
Akıl sağlığı için teşekkürler. Tecrübelerime göre, bazı şeyleri önemsiz olarak reddettiklerini ya uzman olarak ya da memnuniyetsizce cahil olduklarını ve daha sonra ikinci kategoride bulunduğunu gösteriyor: x
Matthieu M.

14
bu sorunların her biri, 20 yıldır var olan dillerde çözüldü ...
Philip JF

11
Ve yine de, uygulanması son derece önemsizdir. Tüm o ejderha kitabı ayrıştırma algoritmalarını unut, 21. yüzyıl ve devam etme zamanı. Dil sözdizimini genişletmek bile kolaydır ve verilen bir önceliğe sahip işleçler eklemek önemsizdir. Bir bak, diyelim, Haskell ayrıştırıcı. “Ana” şişirilmiş diller için ayrıştırıcılardan çok, çok daha basittir.
SK-mantığı

3
@ SK-mantık Battaniye iddialarınız beni ikna etmiyor. Evet, ayrıştırma devam etti. Hayır, türüne bağlı olarak isteğe bağlı operatör önceliklerinin uygulanması “önemsiz” değildir. Sınırlı bağlamda iyi hata mesajları üretmek “önemsiz” değildir. Dönüştürmek için birden fazla geçiş gerektiren dillerle verimli ayrıştırıcılar üretmek mümkün değildir.
Konrad Rudolph

6
Haskell'de ayrıştırma "kolaydır" (çoğu dile kıyasla). Öncelik bağlam bağımsızdır. Size zor hata mesajları veren kısım, kullanıcı tanımlı operatörler için değil, sınıf sınıfı ve ileri tip sistem özellikleriyle ilgilidir. Kullanıcı tanımı değil aşırı yüklenme, zor olan budur.
Philip JF

25

Şimdilik "operatörler okunabilirliğe zarar vermek için suistimal ediliyor" argümanını görmezden gelelim ve dil tasarım uygulamalarına odaklanalım.

Infix operatörleri basit öncelik kurallarından daha fazla meseleye sahiptir (açık olmasına rağmen, referans verdiğiniz bağlantı bu tasarım kararının etkisini önemsiz kılar). Tanımlamak ne olur: Bir çatışma çözümü olduğunu a.operator+(b)ve b.operator+(a)? Birini diğerinden tercih etmek, bu operatörün beklenen değişme özelliğini kırmaya neden olur. Bir hata atmak, aksi halde çalışacak modüllerin bir kez kırılmasına neden olabilir. Türetilmiş türleri karışıma atmaya başladığınızda ne olur?

Meselenin gerçekleri, operatörlerin sadece işlevler olmadığıdır. Fonksiyonlar ya tek başlarına ya da kendi sınıflarına aittir ve bu, hangi parametrenin (varsa) polimorfik gönderime sahip olduğu konusunda net bir tercih sağlar.

Ve bu, operatörlerden kaynaklanan çeşitli paketleme ve çözünürlük sorunlarını göz ardı eder. Dil tasarımcılarının (genel olarak) ek operatör operatör tanımını sınırlandırmasının nedeni, dil için tartışmalı bir fayda sağlarken dil için birtakım sorunlar yaratmasıdır.

Ve açıkçası, çünkü uygulamak için önemsiz değiller .


1
Java'nın bunları içermemesinin sebebinin bu olduğuna inanıyorum, ancak kendi dillerinde öncelikleri için açık kuralları var. Bunun matris çarpımına benzer olduğunu düşünüyorum. Değişmeli değil, bu yüzden matris kullanırken farkında olmalısınız. Sanırım sınıflarla uğraşırken de aynı şey geçerli, ama evet, değişmeyenlerin +kötülük olduğuna katılıyorum . Ama bu gerçekten kullanıcı tanımlı operatörlere karşı bir argüman mı? Genel olarak operatörün aşırı yüklenmesine karşı bir argüman gibi görünüyor.
Beatgammit

1
@tjameson - Evet, dillerin matematik için öncelik için açık kuralları vardır . Operatörler gibi şeyler için aşırı yükleniyorsa matematik işlemlerinde öncelik kuralları gerçekten geçerli olmayacaktır boost::spirit. Kullanıcı tanımlı işleçlere izin verir vermez daha da kötüye gider, çünkü matematik için önceliği bile iyi tanımlamanın bir yolu yoktur. Bu konuda kendim hakkında çok az şey yazdım , özellikle keyfi olarak tanımlanmış işleçlerle ilgili sorunları ele almak isteyen bir dil bağlamında.
Telastyn

22
Saçmalama diyorum efendim. OO gettosunun dışında bir yaşam var ve fonksiyonlar mutlaka herhangi bir nesneye ait değil , bu nedenle doğru fonksiyonu bulmak, doğru operatörü bulmakla tamamen aynı.
Matthieu M.

1
@matthieuM. - Kesinlikle. Sahip olma ve gönderme kuralları, üye işlevlerini desteklemeyen dillerde daha düzgündür. Gerçi noktada asıl soru 'neden OO olmayan diller daha yaygın değil?' Bu tamamen başka bir ballgame.
Telastyn

11
Haskell'in özel operatörleri nasıl yaptığını gördünüz mü? Onlar işe tam olarak onlar da ilişkili bir önceliğe sahip hariç normal fonksiyonları gibi. (Aslında, normal işlevler de olabilir, bu yüzden orada bile gerçekten farklı değillerdir.) Temel olarak, operatörler varsayılan olarak ekler ve adlar önekdir, ancak tek fark budur.
Tikhon Jelvis

19

Operatör aşırı yüklenmelerinin ne sıklıkta bir şekilde uygulandığına şaşırırsınız . Ancak birçok toplulukta yaygın olarak kullanılmazlar.

Bir diziyi birleştirmek için neden ~ kullanılır? Neden << gibi Ruby kullanmıyorsunuz ? Çünkü birlikte çalıştığınız programcılar muhtemelen Ruby programcıları değillerdir. Veya D programcıları. Peki kodunuzla karşılaştıklarında ne yaparlar? Sembolün ne anlama geldiğine bakmalılar.

Fonksiyonel diller için de bir tadı olan çok iyi bir C # geliştiricisi ile çalışırdım. Maviden, uzatma yöntemleriyle C # 'ya monadları tanıtmaya ve standart monad terminolojisini kullanmaya başladı. Kimse kodunun bir kısmının tereddütlü olduğunu ve ne anlama geldiğini öğrendikten sonra daha okunaklı olduğu konusunda itiraz edemezdi, ancak bu , kodun anlaşılmasından önce herkesin monad terminolojisini öğrenmek zorunda olduğu anlamına geliyordu .

Sence yeterince adil mi? Sadece küçük bir takımdı. Şahsen ben aynı fikirde değilim. Her yeni geliştirici bu terminoloji ile karıştırılmaya mahkum edildi. Yeni bir alan öğrenmek için yeterli problemimiz yok mu?

Öte yandan, mutlulukla kullanacağı operatörü Ne olduğunu bilmek diğer C # geliştiriciler bekliyoruz çünkü C #, ama varsayılan olarak desteklemeyen bir dile aşırı olmaz.??


Double.NaN örneğinizi anlamıyorum, bu davranış kayan nokta özelliğinin bir parçasıdır ve kullandığım her dilde desteklenir. Özel operatörlerin, özelliği bilmeyen geliştiricilerin kafasını karıştıracağı için desteklenmediğini mi söylüyorsunuz? Üçlü operatör veya ??örneğinize karşı aynı argüman gibi geliyor .
beatgammit

@tjameson: Haklısın, aslında, bu yapmaya çalıştığım noktaya biraz teğet oldu ve özellikle iyi yazılmış değildi. Ben düşündüm ki ?? Bunu yazarken ve bu örneği tercih ederken. Double.NaN paragrafının kaldırılması.
pdr,

6
"Her yeni geliştiricinin bu terminoloji ile karıştırılmaya mahkum olduğu" noktanızın "noktasının biraz eksik olduğunu düşünüyorum, yeni geliştiricilerin kod tabanınıza öğrenme eğrisi hakkında endişelenmek, benim için yeni geliştiricilerin öğrenme eğrisi hakkında endişelenmekle aynı şey. .NET'in yeni sürümü. Daha önemlisi, devs bunu öğrendiğinde (yeni .NET veya kod tabanınız), bir anlam ifade ediyor ve değer gösteriyor mu? Eğer öyleyse, öğrenme eğrisi buna değer, çünkü her geliştiricinin sadece bir kez öğrenmesi gerekiyor, ancak kodun sayısız kez yönetilmesi gerekiyor, bu nedenle kodu geliştirirse bu küçük bir maliyettir.
Jimmy Hoffa,

7
@JimmyHoffa Bu sürekli bir maliyet. Her yeni geliştirici için ön ödeme yaptım ve mevcut geliştiricileri de etkiledi çünkü desteklemesi gerekiyor. Bir dokümantasyon maliyeti de var ve bir risk unsuru var - bugün yeterince güvenli hissediyor, ancak 30 yıldan beri, herkes harekete geçecek, dil ve uygulama artık "eski" ve bazı fakir emiciler, ".concat ()" yazamayacak kadar tembel olan programcıların parlaklığında saçlarını yırtmaya başlayacaklar. Beklenen değer maliyetleri dengelemek için yeterli mi?
Sylverdrag

3
Ayrıca, "Her yeni geliştirici bu terminoloji ile karıştırılmaya mahkum edildi." Yeni bir alan öğrenmek için yeterli sorun yaşamadık mı? " 80'lerde OOP'a, ondan önce yapılandırılmış programlamaya veya 10 yıl önce IoC'ye uygulanabilirdi. Bu yanıltıcı argümanı kullanmayı bırakmanın zamanı geldi.
Mauricio Scheffer

11

Birkaç neden düşünebilirim:

  • Onlar uygulamak için önemsiz değildir Eğer kullanıcı tanımlı öncelik, fixity ve Arity kuralları izin, özellikle keyfi özel operatörleri derleyici çok daha karmaşık hale getirebilir izin -. Sadelik bir erdem ise, operatörün aşırı yüklenmesi sizi iyi dil tasarımından uzaklaştırıyor.
  • Onlar istismar olsun - çoğunlukla operatörleri yeniden tanımlamak ve özel sınıfların her türlü yeniden tanımlayarak başlamak için "cool" olduğunu düşünüyorum kodlayıcılar tarafından. Çok geçmeden, kodunuz, başka hiç kimsenin okuyamayacağı ve anlayamayacağı özel semboller yükü ile doludur, çünkü operatörler geleneksel olarak anlaşılan kuralları takip etmez. DSL'iniz bir matematik alt kümesi olmadıkça "DSL" argümanını satın almıyorum :-)
  • Bunlar okunabilirliği ve idame zarar - operatörler düzenli geçersiz ise, bu tesisin kullanıldığında noktaya sabit hale gelebilir ve kodlayıcılar sürekli bir operatör ne yaptığını kendilerine sormak zorunda kalıyor. Anlamlı fonksiyon isimleri vermek daha iyidir. Birkaç ekstra karakter yazmak ucuzdur, uzun süreli bakım problemleri pahalıdır.
  • Bunlar olabilir örtük performans beklentilerini kırmak . Örneğin, normalde bir dizideki bir elementin aranmasını beklerdim O(1). Ancak operatörün aşırı yüklenmesi ile indeksleme operatörünün uygulanmasına bağlı olarak someobject[i]kolayca bir O(n)işlem olabilir.

Gerçekte, operatörün aşırı yüklenmesinin sadece normal fonksiyonlara kıyasla haklı kullanımlara sahip olduğu çok az durum vardır. Meşru bir örnek, matematiksel operatörlerin karmaşık sayılar için tanımlandığı iyi anlaşılmış yöntemleri anlayan, matematikçiler tarafından kullanılmak üzere karmaşık bir sayı sınıfı tasarlıyor olabilir. Ancak bu gerçekten çok yaygın bir durum değil.

Dikkate alınması gereken bazı ilginç durumlar:

  • Lisps : Genel olarak operatörler ve fonksiyonlar arasında hiçbir ayrım yapmayın - +sadece normal bir fonksiyondur. +Operatörleri de dahil olmak üzere, fonksiyonları istediğiniz gibi tanımlayabilirsiniz (genellikle yerleşiklerle çakışmamak için bunları ayrı ad alanlarında tanımlamanın bir yolu vardır ). Ancak anlamlı işlev adlarını kullanma konusunda kültürel bir eğilim vardır, bu nedenle bu çok fazla suiistimal edilmez. Ayrıca, Lisp ön ekinde notasyon özel olarak kullanılma eğilimindedir, bu nedenle operatörün aşırı yük sağladığı "sözdizimsel şeker" de daha az değer vardır.
  • Java - operatör aşırı yüklenmesine izin vermiyor. Bu bazen can sıkıcıdır (Karmaşık sayı durumu için olanlar için) ancak ortalama olarak basit, genel amaçlı bir OOP dili olarak tasarlanan Java için doğru tasarım kararıdır. Java kodu aslında düşük / orta vasıflı geliştiricilerin bu basitliğin bir sonucu olarak sürdürmesi için oldukça kolaydır.
  • C ++ çok gelişmiş bir operatör aşırı yüklemesine sahiptir. Bazen bu kötüye kullanır ( cout << "Hello World!"herkes)? tam olarak performanstan ödün vermeden istediğiniz gibi. Kendinizi ayağınızdan vurursanız, kendi sorumluluğunuz olduğu anlaşılır.

8

Bu özelliğin uygulanması oldukça önemsiz olduğu için neden daha yaygın değil?

Uygulaması önemsiz değildir ( önemsiz bir şekilde uygulanmadıkça). İdeal olarak uygulanmış olsa bile, sizi çok fazla zorlamaz: Açıklıktan elde edilen okunabilirlik, bilinmeyenlik ve opaklıktan kaynaklanan okunabilirlik kayıpları ile dengelenir. Kısacası, nadirdir, çünkü genellikle geliştiricilerin veya kullanıcıların zamanına değmez.

Bu, bunu yapan üç dili düşünebildiğimi ve farklı şekillerde yaptıklarını söyledi:

  • Raket, bir şema, hepsi S-expression-y olmadığında, uzatmak istediğiniz herhangi bir sözdizimi için ayrıştırıcı miktarını yazmanıza izin verir ve beklemenizi ister (ve bu izlenebilirliği sağlamak için kullanışlı kancalar sağlar).
  • Tamamen işlevli bir programlama dili olan Haskell, yalnızca noktalama işaretlerinden oluşan herhangi bir operatörün tanımlanmasına izin verir ve bir sabitlik seviyesi (mevcut 10) ve bir ilişkilendirme sağlamanıza izin verir. Üçlü vb. Operatörler, ikili operatörlerden ve daha yüksek dereceli fonksiyonlardan yaratılabilir.
  • Bağımlı olarak yazılmış bir programlama dili olan Agda, aynı programdaki operatörler olarak tanımlanmışsa, eğer öyleyse ve eğer öyleyse operatör olarak son derece esnektir ( ancak burada belirtildiği gibi ). sonuç olarak.

4
"Önemsiz olmadığını" söylediniz ve hemen bazı aşırı önemsiz uygulamaları içeren üç dilde listeledik. Yani, sonuçta oldukça önemsiz, değil mi?
SK-mantığı

7

Özel operatörlerin cesaretini kırmasının ana nedenlerinden biri, herhangi bir operatörün her şeyi yapabileceği / yapabileceğidir.

Örneğin, cstreamçok fazla eleştirilen sola kaydırma aşırı yükü.

Bir dil operatörün aşırı yüklenmesine izin verdiğinde, karışıklığı önlemek için operatör davranışını temel davranışa benzer tutmak için genellikle bir teşvik vardır.

Ayrıca kullanıcı tanımlı operatörler, özellikle özel tercih kuralları olduğunda, ayrıştırmayı çok daha zorlaştırır.


6
Operatör aşırı yüklemesi ile ilgili parçaların tamamen yeni operatörler tanımlamak için nasıl uygulandığını anlamıyorum.

Operatör aşırı yüklenmesinin (doğrusal cebir kütüphaneleri) çok iyi kullanıldığını ve bahsettiğiniz gibi çok kötü olduğunu gördüm. Bunun özel operatörlere karşı iyi bir argüman olduğunu sanmıyorum. Ayrıştırma bir sorun olabilir, ancak bir operatör beklendiğinde oldukça açık. Ayrıştırmadan sonra, operatör anlamını aramak kod üretecine kalmıştır.
19

3
Ve bu yöntem aşırı yüklenmesinden ne kadar farklı?
Jörg W Mittag

@ JörgWMittag yöntemi aşırı yükleme ile (çoğu zaman) ekli anlamlı bir isim var. bir sembolle ne olacağını kesin olarak açıklamak daha zordur
cırcır ucube

1
@ ratchetfreak: Eh. +iki şey ekler, -çıkarır, *çoğaltır. Benim hissim, hiç kimsenin programcıyı işlev / yöntem yapmaya zorlamadığı addve doNothingnükleer silahlar ekleyebileceği yönünde . Ve a.plus(b.minus(c.times(d)).times(e)o zaman çok daha az okunabilir a + (b - c * d) * e(bonus eklenir - ilk acıta transkripsiyonda hata olur). İlk önce ne kadar anlamlı olduğunu anlamıyorum ...
Maciej Piechotka

4

Kullanıcı tanımlı operatörleri kullanmıyoruz, aynı nedenle kullanıcı tanımlı kelimeleri kullanmıyoruz. Kimse işlevine "yemin" demez. Düşüncelerinizi başkalarına iletmenin tek yolu paylaşılan dili kullanmaktır. Bu, hem sözlerin hem de işaretlerin (operatörler) kodunuzu yazdığınız toplum tarafından bilinmesi gerektiği anlamına gelir.

Bu nedenle, programlama dillerinde kullanımda gördüğünüz operatörler okulda öğretildiklerimiz (aritmetik) veya boolean operatörleri gibi programlama topluluğunda kurulmuş olanlardır.


1
Bir fonksiyonun ardındaki anlamı keşfetme yeteneğini de ekleyin. "Yemin" durumunda, kolayca bir arama kutusuna yapıştırabilir ve belgelerini bulabilirsiniz. Bir operatörü arama motoruna sokmak bambaşka bir top parkıdır. Mevcut arama motorlarımız sadece operatör bulma konusunda emilir ve bazı durumlarda anlamları bulmaya çalışırken çok fazla zaman kaybettim.
Mark H,

1
Ne kadar "okul" sayıyorsun? Örneğin, bence elemharika bir fikir ve kesinlikle herkesin bilmesi gereken bir operatör, ama diğerleri katılmıyor gibi görünüyor.
Tikhon Jelvis

1
Bu semboldeki sorun değil. Klavyedeki sorun bu. Örneğin, unicode beautifier etkinken emacs haskell-mode kullanın. Ve otomatik olarak ascii operatörlerini unicode sembollerine dönüştürür. Fakat bir ascii eşdeğeri olmasaydı, onu yazamazdım.
Vagif Verdi

2
Doğal diller yalnızca "kullanıcı tanımlı sözcüklerden" oluşur, başka bir şey yapmazlar. Bazı diller aslında özel kelime oluşturmayı teşvik eder.
SK-mantığı

4

Bu tür aşırı yüklemeyi destekleyen dillere gelince: Scala, C ++ 'ı daha temiz ve daha iyi bir şekilde yapar. Çoğu karakter fonksiyon isimlerinde kullanılabilir, böylece isterseniz! + * = ++ gibi operatörleri tanımlayabilirsiniz. Ekleme için yerleşik destek vardır (tek bir argüman alan tüm işlevler için). Bence bu tür işlevlerin ilişkilerini de tanımlayabilirsiniz. Bununla birlikte, önceliği tanımlayamazsınız (sadece çirkin numaralarla, buraya bakınız ).


4

Henüz söylenmemiş olan bir şey, her şeyin (operatörler dahil) bir mesaj gönderildiği Smalltalk'tır. "İşleçler" gibi +, |ve benzerleri aslında tek yöntemdir.

Tüm yöntemler geçersiz kılınabilir, bu nedenle her ikisi de tam a + bsayı ise tam sayı toplama ave bher ikisi de OrderedCollections ise vektör toplama anlamına gelir .

Öncelik kuralları yoktur, çünkü bunlar sadece yöntem çağrılarıdır. Bunun standart matematik gösterimi için önemli bir etkisi var: 3 + 4 * 5demek (3 + 4) * 5değil 3 + (4 * 5).

(Bu, Smalltalk yeni başlayanlar için çok büyük bir engeldir. Matematik kurallarını çiğnemek, özel bir durumu siler, böylece tüm kod değerlendirme, dili çok daha basit hale getirerek eşit derecede soldan sağa ilerler.)


3

Burada iki şeye karşı savaşıyorsun:

  1. Operatörler neden ilk olarak dillerde var?
  2. Operatörlerin fonksiyonlar / yöntemler üzerindeki erdemleri nedir?

Çoğu dilde, operatörler gerçekten basit fonksiyonlar olarak uygulanmaz. Bazı fonksiyon iskeleleri olabilirler, fakat derleyici / çalışma zamanı anlamsal anlamlarının ve nasıl verimli bir şekilde makine koduna çevrileceğinin farkındadır. Bu, yerleşik işlevlerle karşılaştırıldığında bile çok daha doğrudur (bu nedenle uygulamaların çoğu, uygulamalarına genel gider çağrısını da dahil etmez). Operatörlerin çoğu, CPU'larda bulunan ilkel talimatlar üzerindeki daha yüksek seviyeli soyutlamalardır (kısmen operatörlerin çoğunun aritmetik, boolean veya bitsel olması). Bunları "özel" işlevler olarak modelleyebilirsiniz (bunlara "ilkel" veya "yerleşik" veya "yerel" veya her ne olursa olsun). Alternatif, semantik olarak kullanıcı tanımlı operatörler gibi görünen, ancak derleyicideki özel yolları çağıran yerleşik operatörlere sahip olmaktır. Bu ikinci sorunun cevabını bozuyor ...

Yukarıda bahsettiğim makine çevirisi meselesi dışında, sözdizimsel düzeyde operatörler işlevlerden gerçekten farklı değildir. Ayırt edici özellikleri, özlü ve sembolik olma eğilimindedir; bu, faydalı olmaları gereken önemli bir ek özelliğe işaret eder: geliştiriciler için geniş bir anlam / anlam ifade etmeleri gerekir. Kısa semboller, halihazırda anlaşılan bir anlambilim kümesinin kısa eli olmadıkça fazla bir anlam ifade etmez. Bu, kullanıcı tanımlı operatörleri doğası gereği yararsız hale getirir, çünkü doğası gereği o kadar geniş anlamış değildir. Bir veya iki harf işlevi adı kadar anlamlı.

C ++ 'ın operatör aşırı yükleri bunu incelemek için verimli bir zemin sağlar. Çoğu operatör aşırı yükü "kötüye kullanma", genel olarak anlaşılan bazı anlamsal sözleşmelerden bazılarını kıran aşırı yükler biçiminde gelir (klasik bir örnek, + +! işlenen).

Operatör aşırı yüklenmesine ve kullanıcı tanımlı operatörlere izin veren Smalltalk'a bakarsanız, bir dilin bunu nasıl yapabileceğini ve ne kadar yararlı olacağını görebilirsiniz. Smalltalk'ta, operatörler yalnızca farklı sözdizimsel özelliklere sahip yöntemlerdir (yani, ekli ikili olarak kodlanırlar). Dil, özel hızlandırılmış operatörler ve yöntemler için "ilkel yöntemler" kullanır. Herhangi bir kullanıcı tanımlı işleç oluşturulduysa çok az sayı olduğunu farkedersiniz ve olduklarında, muhtemelen kullanmaları için tasarlanan yazar kadar kullanılmazlar. Bir operatör aşırı yüküne eşdeğer bile nadirdir, çünkü çoğu yeni bir işlevi bir yöntem yerine bir operatör olarak tanımlamak için net bir kayıptır;


1

Her zaman C ++ 'da operatör aşırı yüklerini tek bir geliştirici ekibi için uygun bir kısayol olarak buldum, ancak bu yöntem uzun vadede her türlü karışıklığa neden oluyor çünkü yöntem çağrıları kolay olmayan bir şekilde "gizleniyor" Doxygen gibi araçların ayrıştırılması ve insanların bunları doğru şekilde kullanabilmeleri için deyimleri anlamaları gerekir.

Bazen, beklediğinizden bile mantıklı gelmek çok daha zor. Bir zamanlar, büyük bir platformlar arası bir C ++ projesinde, FilePath(Java'nın Filenesnesine benzeyen) bir nesne oluşturarak yolların inşa edilme şeklini normalleştirmek için iyi bir fikir olacağına karar verdim. Bunun üzerine yol parçası (böylece bir şey File::getHomeDir()/"foo"/"bar"yapabilir ve tüm desteklenen platformlarımızda doğru olanı yapar). Bunu gören herkes aslında, "Ne cehennem? String bölümü?"

Benzer şekilde, grafik programlamada veya vektör / matris matematiğinin çok gerçekleştiği diğer alanlarda, Matrix * Matrix, Vector * Vector (nokta), Vector% Vector (çapraz), Matrix * Vector ( matris dönüşümü), Matrix ^ Vector (homojen koordinatı yok sayarak özel yüzey matrisi dönüşümü - yüzey normalleri için yararlıdır), vb., ancak vektör matematik kitaplığını yazan kişi için ayrıştırma zamanından biraz tasarruf sağlarken, konuyu başkaları için daha fazla karıştırmamak. Sadece buna değmez.


0

Operatör aşırı yüklemeleri, yöntem aşırı yüklenmelerinin kötü bir fikir olması nedeniyle aynı fikirdedir: Ekrandaki aynı sembol, etrafındakilere bağlı olarak farklı anlamlara gelir. Bu rahat okuma için daha zor hale getirir.

Okunabilirlik, bakımın kritik bir yönü olduğundan, her zaman aşırı yüklenmekten kaçınmalısınız (bazı çok özel durumlar hariç). Her sembolün (operatör veya alfanümerik tanımlayıcı olsun) kendi başına duran benzersiz bir anlamı olması çok daha iyidir.

Açıklamak gerekirse: bilmediğiniz kodları okurken, bilmediğiniz yeni bir alfanum tanımlayıcısıyla karşılaşırsanız, en azından bilmediğiniz bir avantaja sahip olursunuz.. Sonra gidip bakabilirsin. Bununla birlikte, anlamını bildiğiniz ortak bir tanımlayıcı veya operatör görüyorsanız, tamamen farklı bir anlama sahip olmak için aşırı yüklenildiğini fark etme ihtimaliniz çok daha düşüktür. Hangi operatörlerin aşırı yüklendiğini bilmek için (aşırı yüklemeyi yaygın şekilde kullanan bir kod tabanında), bunun yalnızca küçük bir bölümünü okumak isteseniz bile, tüm kodun çalışma bilgisine ihtiyacınız olacaktır. Bu, yeni geliştiricilerin bu kodun hızını yükseltmesini zorlaştıracak ve insanları küçük bir işe sokmak imkansız olacak. Bu, programcı iş güvenliği için iyi olabilir, ancak kod tabanının başarısından sorumluysanız, bu uygulamadan ne pahasına olursa olsun kaçınmalısınız.

Operatörler boyut olarak küçük olduğundan, aşırı operatörler daha yoğun koda izin verir, ancak kodu yoğunlaştırmak gerçek bir fayda değildir. Mantığın iki katı olan bir satırın okunması iki kat daha uzun sürer. Derleyici umursamıyor. Tek sorun, insan tarafından okunabilirlik. Kodun kompakt hale getirilmesi okunabilirliği artırmadığından, kompaktlığın gerçek bir faydası yoktur. Devam edin ve yer açın ve benzersiz işlemlere benzersiz bir tanımlayıcı verin; kodunuz uzun vadede daha başarılı olacaktır.


"Ekrandaki aynı sembol, etrafındakilere bağlı olarak farklı anlamlara gelirdi" - zaten birçok dilde birçok operatör için geçerli.
rkj

-1

Öncelik ve karmaşık ayrıştırmayla başa çıkmak için teknik zorluklar bir kenara bırakılırsa, bir programlama dilinin düşünülmesi gereken şeylerin birkaç yönü olduğunu düşünüyorum.

Operatörler tipik olarak ana dilde iyi tanımlanmış ve belgelenmiş kısa mantıksal yapılardır (karşılaştır, ata.). Ayrıca genellikle belgeler olmadan anlaşılması zordur ( örneğin a^bile karşılaştır xor(a,b)). Normal programlamada (>, <, =, + etc ..) gerçekten anlamlı olabilecek çok sayıda operatör vardır.

Benim fikrim, bir dilde iyi tanımlanmış bir dizi operatöre bağlı kalmanın daha iyi olduğudur - o zaman bu operatörlerin operatöre aşırı yüklenmesine izin verin (operatörlerin aynı şeyi yapması gerektiği, ancak özel bir veri türü ile ilgili yumuşak bir öneri verildiğinde).

Sizin kullanım durumları ~ve |aslında basit aşırı operatörü (C #, C ++, vs.) ile mümkün olacaktır. DSL geçerli bir kullanım alanıdır, ancak muhtemelen yalnızca geçerli alanlardan biridir (benim açımdan). Bununla birlikte, içinde yeni diller oluşturmak için daha iyi araçlar olduğunu düşünüyorum. Başka bir dilde gerçek bir DSL dili çalıştırmak, bu derleyici-derleyiciler araçlarından herhangi birini kullanarak zor değildir. Aynısı "LUA argümanını genişlet" için de geçerlidir. Bir dil, problemleri öncelikle belirli bir şekilde çözmek için, alt dillerin temelini oluşturmamak için tanımlanmıştır (istisnalar mevcuttur).


-1

Bunun bir başka faktörü de mevcut operatörlerle bir işlem tanımlamanın her zaman ileriye dönük olmamasıdır. Demek istediğim, evet, herhangi bir sayı için '*' operatörü mantıklı olabilir ve genellikle dilde veya mevcut modüllerde uygulanır. Ancak, tanımlamanız gereken tipik karmaşık sınıflar söz konusu olduğunda (ShipingAddress, WindowManager, ObjectDimensions, PlayerCharacter vb. Gibi şeyler) bu davranışın net olmadığı anlamına gelir. Bir Adrese sayı eklemek veya çıkarmak ne anlama gelir? İki Adresle çarpmak?

Tabii, bir ShippingAddress sınıfına bir dize eklemenin "adres 1'deki satır 1" yerine "(" setLine1 "işlevi yerine") ve bir sayı eklemenin "posta kodunu değiştir" ("setZipCode" yerine) gibi özel bir işlem anlamına geldiğini tanımlayabilirsiniz. , ancak daha sonra kod çok okunaklı ve kafa karıştırıcı değildir. Genelde operatörün temel türlerde / sınıflarda kullanıldığını düşünüyoruz, çünkü davranışları sezgisel, açık ve tutarlı (en azından dile aşina olduğunuzda). Tamsayı, String, ComplexNumbers vb. Türlerde düşünün.

Bu nedenle, operatörleri tanımlamak bazı özel durumlarda çok faydalı olsa bile, gerçek dünyadaki uygulamaları oldukça sınırlıdır, çünkü bunun net bir kazanç olacağı vakaların% 99'u zaten temel dil paketinde uygulanmaktadır.

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.