Neden örtük dönüşüm olamaz?


14

Anladığım kadarıyla, örtük dönüşümler hatalara neden olabilir.

Ama bu mantıklı değil - normal dönüşümler de hatalara neden olmamalı mı?

Neden olmasın

len(100)

dil ile çalışarak (veya derleyerek)

len(str(100))

özellikle de çalışmasının tek yolu bu. Dil hatanın ne olduğunu biliyor, neden düzeltmiyorsunuz?

Bu örnekte Python kullandım, ancak bu küçük şey için temelde evrensel olduğunu hissediyorum.


2
perl -e 'print length(100);'baskılar 3.

2
Ve böylece dilin doğası ve tür sistemi. Bu python tasarımının bir parçasıdır.

2
Bunu düzeltmez, çünkü itnesion'unuzu bilmiyor. belki tamamen farklı bir şey yapmak istedin. bir döngü açmak gibi ama daha önce hiç programlama gibi bir şey yapmadı. kendi kendine düzeltirse, kullanıcı ne yanlış olduğunu ne de uygulamanın beklediğini yapamayacağını bilmez.
Zaibis

5
@PieCrust Python'un nasıl dönüşmesi gerekiyor? Öyle değil tüm olası dönüşüm aynı sonucu iade edeceğini doğrudur.
Bakuriu

7
@PieCrust: dizeler ve diziler yinelenebilir. neden strbir int'i yinelemeye dönüştürmenin örtük yolu olabilir? nasıl range? veya bin( hex, oct) veya chr(veya unichr)? strBu durumda sizin için en açık gibi görünse bile, tüm bunlar tekrarlanabilir .
njzk2

Yanıtlar:


37

Değeri için len(str(100)), len(chr(100))ve len(hex(100))hepsi farklı. strolduğu değil bir dizeye bir tamsayı gelen Python birden fazla farklı dönüşüm var çünkü o iş yapmak için tek yol. Bunlardan biri en yaygın olanıdır, ancak demek istediğini söylemeye gerek yok. Örtük bir dönüşüm, kelimenin tam anlamıyla "söylemeden geçer" anlamına gelir.

Örtük dönüşümlerle ilgili pratik sorunlardan biri, çeşitli olasılıkların olduğu yerlerde hangi dönüşümün uygulanacağı her zaman açık değildir ve bu, okuyucuların doğru örtülü dönüşümü anlayamadıkları için kodu yorumlamada hata yapmalarına neden olur. Herkes her zaman kastettiklerinin "açık yorum" olduğunu söyler. Bu onlar için açık çünkü onlar demek istedikleri şey. Başka biri için açık olmayabilir.

Bu yüzden (çoğu zaman) Python örtük olarak açık olmayı tercih eder, risk almamayı tercih eder. Python'un zorlama türü yaptığı ana durum aritmetiktir. Bu izin veren 1 + 1.0alternatif yaşamak için çok can sıkıcı olurdu çünkü, ancak izin vermez 1 + "1"size Şunu belirtmek için olmalıdır düşünüyor çünkü int("1"), float("1"), ord("1"), str(1) + "1", ya da başka bir şey. Ayrıca izin vermez (1,2,3) + [4,5,6]o halde olabilir bir sonuç türünü seçmek için kurallar tanımlayabilirsiniz o sonucu türünü seçmek için kurallar tanımlar gibi, 1 + 1.0.

Diğer diller aynı fikirde değildir ve birçok örtük dönüşüm gerçekleştirmiştir. Ne kadar çok içerirlerse, o kadar az belirgin olurlar. Kahvaltıdan önce "tamsayı promosyonları" ve "olağan aritmetik dönüşümler" için C standardındaki kuralları ezberlemeyi deneyin!


+1 lenYalnızca bir türle çalışabilen başlangıç ​​varsayımının temelde nasıl kusurlu olduğunu göstermek için.
KChaloux

2
@KChaloux: Aslında benim örnekte str, chrve hextüm iade aynı tür yap! Yapıcısı sadece int alabilecek farklı bir tür düşünmeye çalışıyordum, ama henüz hiçbir şey bulamadım. İdeal bazı konteyner olurdu ;-) biraz anlaşılmaz görünüyor. Xlen(X(100)) == 100numpy.zeros
Steve Jessop

2
Olası sorunlara ilişkin örnekler için şuna bakın: docs.google.com/document/d/… ve destroyallsoftware.com/talks/wat
Jens Schauder

1
Örtülü dönüşüm (bu zorlama denir düşünüyorum) olması gibi kötü şeyler neden olabilir a == b, b == cfakat a == cgerçek olmayan.
bgusach

Mükemmel cevap. Söylemek istediğim her şey, sadece daha iyi ifade! Benim sadece küçük kelime oyunu, C tamsayı tanıtımlarının JavaScript zorlama kurallarını ezberlemeye veya kafanızı örtük kurucuların dikkatsiz kullanımı ile bir C ++ kod tabanı etrafına sarmaya kıyasla oldukça basit olmasıdır.
GrandOpener

28

Anladığım kadarıyla, örtük dönüşümler hatalara neden olabilir.

Bir kelimeniz eksik: örtük dönüşümler çalışma zamanı hatalarına neden olabilir .

Gösterdiğiniz gibi basit bir vaka için, ne demek istediğinizi gayet açık. Ancak diller vakalarda çalışamaz. Kurallarla çalışmaları gerekiyor. Diğer birçok durumda, programcının bir hata yapıp yapmadığını (yanlış tür kullanarak) veya programcı kodun yapmayı düşündüklerini yapmak istediği açık değildir.

Kod yanlış kabul edilirse, bir çalışma zamanı hatası alırsınız. Takip etmek sıkıcı olduklarından, birçok dil size dağınık olduğunuzu söyleme ve bilgisayara gerçekten ne demek istediğinizi söylemenize izin verme tarafına doğru (hatayı düzeltin veya açık bir şekilde dönüştürme yapın). Diğer diller tahmin yapar, çünkü stilleri hata ayıklaması daha kolay olan hızlı ve kolay koda sahiptir.

Dikkat edilmesi gereken bir şey, örtük dönüşümlerin derleyiciyi biraz daha karmaşık hale getirmesidir. Döngüler hakkında daha dikkatli olmanız gerekir (hadi A'dan B'ye bu dönüşümü deneyelim; işe yaramayan ayy, ama B'den A'ya bir dönüşüm var! Ve sonra C ve D boyutlu döngüler için endişelenmeniz gerekiyor ). örtük dönüşümlerden kaçınmak için küçük bir motivasyon.


Çoğunlukla, yalnızca bir türle çalışabilen ve dönüşümü açık hale getiren işlevlerden bahsediyordu. Birden çok türle çalışacak olan ne olurdu ve koyduğunuz tür bir fark yaratır? print ("295") = print (295), böylece değişkenler dışında bir fark yaratmaz. 3D paragraf dışında söyledikleriniz mantıklı ... Tekrar edebilir misiniz?
Quelklef

30
@PieCrust - 123 + "456", "123456" veya 579 istediniz mi? Programlama dilleri bağlam yapmazlar, bu yüzden ekleme işleminin yapıldığı bağlamı bilmeleri gerekeceğinden "anlamaları" zordur. Hangi paragraf belirsiz?
Telastyn

1
Ayrıca, belki de temel-10 olarak yorumlamak istediğiniz şey değildir. Evet, örtük dönüşümler iyi bir şeydir , ancak yalnızca bir hatayı gizleyemedikleri yerlerde.
Tekilleştirici

13
@PieCrust: Dürüst olmak gerekirse asla len(100)geri dönmeyi beklemezdim 3. Ben temsili bit (veya bayt) sayısını hesaplamak için çok daha sezgisel bulurdum 100.
user541686

3
Ben @Mehrdad beraberim, sizin örnekten bunun açıktır sen o% 100 açıktır düşünüyorum len(100)size sayısının 100. ondalık temsil karakter miktarını vermelidir Ama farkında iken sen yapma aslında varsayımların bir ton var onları. Onaltılık gösterimin bit sayısı, bayt veya karakterlerinin ( 64-> 2 karakter) neden döndürülmesi gerektiğine dair güçlü argümanlar yapabilirsiniz len().
asontu

14

Örtük dönüşümler yapmak oldukça mümkündür. Başın belada olduğu durum, bir şeyin hangi şekilde çalışması gerektiğini bilmediğin zamandır.

Bunun bir örneği, +operatörün farklı zamanlarda farklı şekillerde çalıştığı Javascript'te görülebilir .

>>> 4 + 3
7
>>> "4" + 3
43
>>> 4 + "3"
43

Bağımsız değişkenlerden biri bir dize ise, +işleç bir dize birleşimidir, aksi takdirde ekleme yapılır.

Bir bağımsız değişken verilirse ve bunun bir dize mi yoksa tamsayı mı olduğunu bilmiyorsanız ve buna bir ekleme yapmak istiyorsanız, biraz karışıklık olabilir.

Bununla başa çıkmanın bir başka yolu, Temel mirastır (perl'in izlediği - bkz. Programlama Zor, Komut Dosyasına Girelim ... )

Basic'te, lenişlev yalnızca bir String'de çağrılmayı gerektirir ( visual basic için dokümanlar : "Geçerli herhangi bir String ifadesi veya değişken adı. İfade Object türündeyse, Len işlevi boyutu dosyaya FilePut işlevi. ").

Perl bu bağlam kavramını takip eder. İçin tiplerinin örtük dönüşüm ile JavaScript var karışıklık +operatörü bazen ilave olarak bazen birleştirme perl'de olmaz çünkü +olduğunu her zaman ekleme ve .olduğunu her zaman birleştirme.

Skaler bağlamda bir şey kullanılırsa, bu bir skalerdir (örneğin, bir listeyi skaler olarak kullanmak, liste uzunluğuna karşılık gelen bir sayı gibi davranır). Bir dize operatörü kullanırsanız ( eqeşitlik testi cmpiçin, dize karşılaştırması için) skaler, bir dizemiş gibi kullanılır. Benzer şekilde, bir matematik bağlamında bir şey kullanılmışsa ( ==eşitlik testi ve <=>sayısal karşılaştırma için), skaler sanki bir sayıymış gibi kullanılır.

Tüm programlama için temel kural "kişiyi en az şaşırtan şeyi yapın" dır. Bu, orada sürprizlerin olmadığı anlamına gelmez, ancak çaba kişiye en az sürpriz yapmaktır.

Perl - php'nin yakın bir kuzenine giderken, bir operatörün dize veya sayısal bağlamlarda bir şey üzerinde hareket edebileceği ve davranışın insanlar için şaşırtıcı olabileceği durumlar vardır. ++Operatör buna bir örnektir. Rakamlarda, tam olarak beklendiği gibi davranır. Bir dizge üzerinde "aa"çalışırken, dizeyi artırır ( $foo = "aa"; $foo++; echo $foo;yazdırır ab). Aynı zamanda o kadar devredilir azartırılır zaman olur ba. Bu henüz şaşırtıcı değil.

$foo = "3d8";
echo "$foo\n";
$foo++;
echo "$foo\n";
$foo++;
echo "$foo\n";
$foo++;
echo "$foo\n";

( ideone )

Bu çıktı:

3d8
3d9
3e0
4

Örtük dönüşümlerin ve aynı dizede farklı davranan operatörlerin tehlikelerine hoş geldiniz. (Kod biraz farklı blok Perl kolları - bu karar "3d8"zaman ++operatör uygulanır baştan sayısal bir değerdir ve gider 4hemen ( ideone ) - bu davranışı, anlatılan perlop: Otomatik arttırma ve Otomatik eksiltme )

Şimdi, neden bir dil bir şekilde bir şey yapıyor, diğeri ise başka bir şekilde tasarımcıların tasarım düşüncelerine ulaşıyor. Perl'in felsefesi: Bunu yapmanın birden fazla yolu var - ve bu işlemlerden bazılarını yapmanın birkaç yolunu düşünebilirim. Öte yandan, Python'un PEP 20 - Python'un Zen'i (diğer şeylerin yanı sıra) şunları ifade eden bir felsefesi vardır: "Bunu yapmanın tek ve tercihen tek bir yolu olmalı."

Bu tasarım farklılıkları farklı dillere yol açmıştır. Python'da bir sayının uzunluğunu almanın bir yolu vardır. Örtük dönüşüm bu felsefeye aykırıdır.

İlgili okuma: Ruby neden Fixnum'u String'e dönüştürmüyor?


IMHO, iyi bir dil / çerçeve genellikle ortak köşe vakalarını farklı şekilde ele alan birçok şey yapmalıdır (ortak bir köşe örneğini bir dilde veya çerçevede bir kez işlemek 1000 programda 1000 kereden daha iyi); iki operatörün çoğu zaman aynı şeyi yapması, kötü bir şey olarak düşünülmemelidir, eğer davranışları farklı olduğunda, her varyasyonun faydaları olacaktır. İki sayısal değişkeni karşılaştırmak zor olmamalı xve ybir denklik ilişkisi ya da sıralama sağlayacak şekilde değil, ama IIRC Python'un karşılaştırma operatörleri ...
supercat

... ne denklik ilişkileri ne de sıralama uygular ve Python bunu yapan uygun operatörler sağlamaz.
supercat

12

ColdFusion bunların çoğunu yaptı. Örtük dönüşümlerinizi işlemek için bir dizi kural tanımlar, tek bir değişken türüne sahiptir ve işte gidiyorsunuz.

Sonuç, 6'ya "4a" eklemenin 6.16667 olduğu toplam anarşi.

Neden? İki değişkenin ilki bir sayı olduğu için sonuç sayısal olacaktır. "4a" bir tarih olarak ayrıştırılır ve "04:00" olarak görülür. 04:00, 4: 00/24: 00 veya günün 1 / 6'sıdır (0.16667). 6'ya ekleyin ve 6.16667 olsun.

Listelerde virgülün varsayılan ayırıcı karakterleri bulunur, bu nedenle virgül içeren bir listeye öğe eklerseniz, yalnızca iki öğe eklersiniz. Ayrıca, listeler gizlice dizgidir, bu nedenle yalnızca 1 öğe içeriyorlarsa tarih olarak ayrıştırılabilirler.

Dize karşılaştırması, her iki dizenin önce tarihlere ayrıştırılıp ayrıştırılamayacağını denetler. Tarih türü olmadığından bunu yapar. Rakamlar, sekizli gösterim ve booleans ("doğru" ve "evet" vb.) İçeren sayılar ve dize için aynı şey

Hızlı başarısız olmak ve bir hata bildirmek yerine, ColdFusion sizin için veri tipi dönüşümlerini işleyecektir. Ve iyi bir şekilde değil.

Tabii ki, DateCompare gibi açık işlevleri çağırarak örtük dönüşümleri düzeltebilirsiniz ... ancak daha sonra örtük dönüştürmenin "faydalarını" kaybettiniz.


ColdFusion için bu, geliştiricilerin güçlendirilmesine yardımcı olmanın bir yoludur. Özellikle tüm bu geliştiriciler HTML olduğunda (ColdFusion etiketlerle çalışır, buna CFML denir, daha sonra komut dosyası eklediler <cfscript>, burada her şey için etiket eklemenize gerek yoktur). Ve sadece bir şeyin çalışmasını istediğinizde iyi çalışır. Ancak, olayların daha kesin bir şekilde gerçekleşmesi gerektiğinde, bir hata olmuş gibi görünen herhangi bir şey için örtük dönüşümler yapmayı reddeden bir dile ihtiyacınız vardır.


1
vay, "tam bir anarşi" gerçekten!
hoosierEE

7

Örtük dönüşümlerin, açıkça aramadan önce int'i bir dizeye dönüştürmeyi int a = 100; len(a)düşündüğünüz açık olan işlemler için iyi bir fikir olabileceğini söylüyorsunuz .

Ama bu aramalar olabileceğini unutuyorsun sözdizimsel kesin ama geçmek anlamına programcı tarafından yapılan bir yazım hatası temsil edebilir a1, hangi olan bir dize. Bu tartışmalı bir örnektir, ancak değişken adlar için otomatik tamamlama sağlayan IDE'lerle bu hatalar meydana gelir.

Tür sistemi, hatalardan kaçınmamıza yardımcı olmayı amaçlar ve daha katı tür denetlemeyi seçen diller için örtük dönüşümler bunu zayıflatır.

== tarafından gerçekleştirilen tüm örtük dönüşümlerle Javascript'in sıkıntılarını kontrol edin, o kadar ki çoğu artık no-implicit-conversion === operatörüne bağlı kalmanızı öneriyor.


6

Bir an için ifadenizin bağlamını düşünün. Bunun işe yarayabileceği "tek yol" olduğunu söylüyorsunuz, ancak bunun böyle çalışacağından gerçekten emin misiniz? Peki buna ne dersin:

def approx_log_10(s):
    return len(s)
print approx_log_10(3.5)  # "3" is probably not what I'm expecting here...

Diğerlerinin de belirttiği gibi, çok özel örneğinizde, derleyicinin ne demek istediğini sezmesi basit görünüyor. Ancak daha karmaşık programların daha geniş bir bağlamında, bir dize girmek mi, yoksa başka bir değişken için bir değişken adı yazmak mı, yoksa yanlış işlev veya düzinelerce diğer potansiyel mantık hatasından herhangi birini yazmak mı istediğiniz genellikle açık değildir. .

Tercüman / derleyicinin ne demek istediğini tahmin etmesi durumunda, bazen sihirli bir şekilde çalışır ve daha sonra diğer zamanlarda gizemli bir şekilde işe yaramayan bir şeyi hata ayıklamak için saatlerce harcıyorsun. Ortalama bir durumda, ifadenin mantıklı olmadığı ve gerçekte kastettiğiniz şeyi düzeltmek için birkaç saniye harcadığınız söylenmesi çok daha güvenlidir.


3

Örtük dönüşümler, çalışmak için gerçek bir acı olabilir. PowerShell'de:

$a = $(dir *.xml) # typeof a is a list, because there are two XML files in the folder.
$a = $(dir *.xml) # typeof a is a string, because there is one XML file in the folder.

Aniden iki kat daha fazla test gerekli ve örtük dönüşüm olmadan iki kat daha fazla hata var.


2

Niyetinizi açıkça ortaya koydukları için açık dökümler önemlidir. Her şeyden önce açık dökümler kullanmak, kodunuzu okuyan birine bir hikaye anlatır. Yaptıklarınızı kasıtlı olarak yaptığınızı ortaya koyarlar. Üstelik aynı şey derleyici için de geçerlidir. Örneğin, C # 'da geçersizdir

double d = 3.1415926;
// code elided
int i = d;

Oyuncular, kolayca bir hata olabilecek gevşek hassasiyeti sağlayacaktır. Bu nedenle derleyici derlemeyi reddeder. Açık bir kadro kullanarak derleyiciye: "Hey, ne yaptığımı biliyorum." Ve gidecek: "Tamam!" Ve derleyin.


1
Aslında, çoğu açık kadroyu örtük olanlardan daha fazla sevmiyorum; IMHO, tek şey (X)y"Y'nin X'e dönüştürülebilir olduğunu düşünüyorum; mümkünse dönüşümü yapın veya değilse bir istisna atın"; Bir dönüşüm başarılı olursa, bir sonuç değeri türünden dönüştürme hakkında herhangi bir özel kuralları bilmek zorunda kalmadan * ne olacağını tahmin etmek mümkün olmalıdır ytürüne X. -1.5 ve 1.5'i tamsayılara dönüştürmesi istenirse, bazı diller (-2,1) verir; bazıları (-2,2), bazıları (-1,1) ve bazıları (-1,2). C'nin kuralları neredeyse belirsiz olsa da ...
supercat

1
... bu tür bir dönüşüm, döküm yoluyla yöntemden daha uygun bir şekilde gerçekleştirilecek gibi görünüyor (çok kötü .NET etkili yuvarlak ve dönüştürme işlevlerini içermiyor ve Java'lar biraz aptalca aşırı yüklenmiş).
supercat

Derleyiciye " Sanırım ne yaptığımı biliyorum" diyorsunuz.
gnasher729

@ gnasher729 İçinde bazı gerçekler var :)
Paul Kertscher

1

Örtük dönüşümleri / yayınları veya bazen belirtildiği gibi zayıf yazmayı destekleyen diller, sizin için her zaman amaçladığınız veya beklediğiniz davranışla eşleşmeyen varsayımlar yapar. Dil tasarımcıları, belirli bir dönüşüm veya dönüşüm dizisi için yaptığınızla aynı düşünce sürecine sahip olmuş olabilirler ve bu durumda asla bir sorun görmezsiniz; ancak yapmadılarsa, programınız başarısız olur.

Ancak, başarısızlık belli olabilir veya olmayabilir. Bu örtülü yayınlar çalışma zamanında oluştuğundan, programınız mutlu bir şekilde çalışır ve yalnızca programınızın çıktısına veya sonucuna baktığınızda bir sorun görürsünüz. Açık dökümler gerektiren bir dil (bazıları bunu güçlü yazma olarak ifade eder), program yürütmeye başlamadan önce size bir hata verir;

Geçen gün bir arkadaşım 2 yaşındaki çocuğuna 20 + 20'nin ne olduğunu sordu ve 2020'yi yanıtladı. Arkadaşıma "O bir Javascript programcısı olacak" dedim.

Javascript'te:

20+20
40

20+"20"
"2020"

Böylece örtülü kalıpların yaratabileceği sorunları ve bunun neden düzeltilebilecek bir şey olmadığını görebilirsiniz. Tartışacağım düzeltme açık dökümler kullanmaktı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.