Math.Sqrt () neden statik bir işlevdir?


31

Statik ve örnek metotlarla ilgili bir tartışmada her zaman, Sqrt()bunun statik metot yerine sayı türlerinin örnek metodu olması gerektiğini düşünüyorum . Neden? Belli ki bir değer üzerinde çalışıyor.

 // looks wrong to me
 var y = Math.Sqrt(x);
 // looks better to me
 var y = x.Sqrt();

Değer türleri açıkça, birçok ülkede olduğu gibi, bir örnek yöntemi olduğu gibi, örnek yöntemlere sahip olabilir ToString().

Yorumlardan bazı soruları cevaplamak için: Neden 1.Sqrt()yasal olmamalı ? 1.ToString()olduğunu.

Bazı diller değer türleri üzerinde yöntemlere sahip değildir, ancak bazı diller olabilir. Java, ECMAScript, C # ve Python (bunlarla birlikte) dahil bunlar hakkında konuşuyorum __str__(self). Aynısı ceil(), floor()vb. Gibi diğer fonksiyonlar için de geçerlidir .


18
Hangi dilde bunu öneriyorsunuz? Misiniz 1.sqrt()geçerli olması?

20
Birçok dilde (örneğin, java) çiftler ilkeldir (performans nedeniyle) bu yüzden yöntemleri yoktur
Richard Tingle

45
Öyleyse sayısal türler, kendilerine uygulanabilecek her matematiksel işlevle şişirilmeli mi?
D Stanley

19
FWIW Bence çok daha doğal Sqrt(x)görünüyor bence Bu, bazı dillerde sınıfla işlevi hazırlama anlamına gelirse, ben bununla iyiyim. O Eğer vardı bir örnek yöntemi, daha sonra o olduğunu belirtmek için daha uygun olacağı dönen bir değeri yerine modifiye örneği. x.Sqrt()x.GetSqrt()
D Stanley

23
Bu soru şu anki haliyle dilden agnostik olamaz. Budur kök sorunun.
RisingDarkness

Yanıtlar:


20

Tamamen bir dil tasarımı seçimidir. Ayrıca, ilkel türlerin altında yatan uygulamaya ve buna bağlı performans değerlendirmelerine de bağlıdır.

.NET, a üzerinde çalışan ve a döndüren yalnızca bir statik Math.Sqrtyöntem içerirdoubledouble . Ona ilettiğiniz herhangi bir şey a veya a’ya yükseltilmelidir double.

double sqrt2 = Math.Sqrt(2d);

Öte yandan, bu işlemleri aşağıdaki gibi fonksiyonlar olarak gösteren Rust'a sahipsiniz :

let sqrt2 = 2.0f32.sqrt();
let higher = 2.0f32.max(3.0f32);

Ancak Rust'un evrensel işlev çağrısı sözdizimi de vardır (birisi daha önce bahsetti), böylece ne istersen onu seçebilirsin.

let sqrt2 = f32::sqrt(2.0f32);
let higher = f32::max(2.0f32, 3.0f32);

1
.NET'te uzatma yöntemleri yazabileceğinize dikkat edin, bu yüzden gerçekten bu uygulamanın nasıl görünmesini x.Sqrt()istiyorsanız, yapılabilir. public static class DoubleExtensions { public static double Sqrt( this double self) { return Math.Sqrt(self); } }
Zachary Dow

1
Den

65

Diyelim ki yeni bir dil tasarlıyoruz ve Sqrtbir örnek yöntem olmak istiyoruz . Bu yüzden doublesınıfa bakıyoruz ve tasarlamaya başlıyoruz. Belli ki hiçbir girişi yoktur (örnek hariç) ve a döndürür double. Kodu yazıp test ediyoruz. Mükemmellik.

Fakat bir tamsayının karekökünü almak da geçerlidir ve herkesi bir karekök almak için çifte dönüşmeye zorlamak istemiyoruz. Bu yüzden taşınıyoruz intve tasarlamaya başlıyoruz. Ne geri döner? Biz olabilir bir dönüş intve mükemmel kareler için işe yarar yapabilir veya en yakın sonucu yuvarlak int(şimdilik uygun yuvarlama yöntemiyle ilgili tartışmaları görmezden). Peki ya biri tam sayı olmayan bir sonuç isterse? İki yöntemimiz olması durumunda - biri diğeri intdiğeri double( diğeri adı değiştirmeden mümkün değildir). Bu yüzden geri dönmesi gerektiğine karar veriyoruz double. Şimdi uyguluyoruz. Ancak uygulama bizim için kullandığımızla aynıdır.double. Kopyala ve yapıştır mı yapıyoruz? Örneği a olarak mı kullanıyoruz doubleve bu örnek yöntemini mi çağırıyoruz ? Neden mantığı her iki sınıftan da erişilebilen bir kütüphane yöntemine koymuyorsunuz. Kütüphaneyi Mathve işlevi arayacağız Math.Sqrt.

Neden Math.Sqrtstatik bir fonksiyondur ?:

  • Çünkü uygulama, sayısal türden bağımsız olarak aynıdır.
  • Belirli bir örneği etkilemediğinden (bir değer alır ve bir sonuç döndürür)
  • Sayısal türler bu işlevselliğe bağlı olmadığından, ayrı bir sınıfta olması mantıklıdır.

Başka argümanları bile ele almadık:

  • O adlı olmalıdır GetSqrto zamandan beri döndüren örneğini değiştirerek daha yeni bir değer doğrusu?
  • Ne hakkında Square? Abs? Trunc? Log10? Ln? Power? Factorial? Sin? Cos? ArcTan?

6
Değil sevinçleri ait söz 1.sqrt()vs 1.1.sqrt()bunlar aynı temel sınıfa var mı (çirkin görünüyor, ghads)? sqrt()Yöntemi için sözleşme nedir ?

5
@MichaelT Nice örneği. Neyin 1.1.Sqrttemsil edildiğini anlamak beni dört okur aldı . Zeki.
D Stanley

17
Statik sınıfın asıl sebebinizle nasıl yardım ettiği bu cevaba gelmiyor. Math sınıfınızda çift Sqrt (int) ve çift Sqrt (çift) varsa, iki seçeneğiniz vardır: int'yi bir ikiye dönüştürün, sonra çift sürüme çağırın ya da yöntemi uygun değişikliklerle kopyalayıp yapıştırın (varsa) ). Ancak bunlar, örnek sürüm için tanımladığınız seçeneklerin aynısıdır. Diğer sebepleriniz (özellikle üçüncü mermi noktanız) Daha fazlasını kabul ediyorum.
Ben Aaronson,

14
-1 Bu cevap saçma ne yok her bu statik olma ilgisi var? Sen bu aynı sorulara yol ya cevapları karar olacak gidiyoruz (Bu örnek yöntemleri için olacağından daha "[statik fonksiyonu ile] uygulaması aynıdır" ve artık en azından doğru yanlış, ya da ..)
BlueRaja - Danny Pflughoeft 3:15

20
"Uygulama, temel sayısal türden bağımsız olarak aynıdır" tamamen saçmadır. Karekök fonksiyonunun uygulamalarının, korkunç derecede verimsiz olmaları için birlikte çalıştıkları türe göre önemli ölçüde farklılık göstermesi gerekir.
R. ..

25

Matematiksel işlemler genellikle performansa duyarlıdır. Bu nedenle, derleme zamanında tamamen çözülebilen (ve optimize edilmiş veya sıralı) statik yöntemler kullanmak isteyeceğiz. Bazı diller, statik olarak gönderilen yöntemleri belirtmek için herhangi bir mekanizma sunmaz. Ayrıca, birçok dilin nesne modelinde olduğu gibi “ilkel” türler için kabul edilemez olan ek yük hafızası vardır double.

Birkaç dil, yöntem çağırma sözdizimini kullanan, ancak aslında statik olarak gönderilen işlevleri tanımlamamızı sağlar. C # 3.0 veya sonraki sürümlerinde uzatma yöntemleri bir örnektir. C ++ ilkel türlerde yöntemleri desteklemese de, sanal olmayan yöntemler (örneğin, C ++ yöntemlerinin varsayılanı) başka bir durumdur. Elbette, herhangi bir çalışma zamanı ek yükü olmadan, çeşitli yöntemlerle ilkel bir türü süsleyen C ++ 'ta kendi sarmalayıcı sınıfınızı oluşturabilirsiniz. Ancak, değerleri bu sarıcı türüne el ile dönüştürmeniz gerekir.

Sayısal türlerinde yöntemleri tanımlayan birkaç dil vardır. Bunlar genellikle her şeyin bir nesne olduğu oldukça dinamik dillerdir. Burada performans, kavramsal zarafetin ikincil bir değerlendirmesidir, ancak bu diller genellikle sayı mücadelesi için kullanılmaz. Bununla birlikte, bu diller, ilkel işlemler üzerindeki işlemleri "kutudan çıkarabilen" bir optimize ediciye sahip olabilir.


Teknik kaygılar dışında, böyle bir yönteme dayalı matematik arayüzünün iyi bir arayüz olup olmadığını düşünebiliriz. İki sorun ortaya çıkar:

  • matematiksel gösterim, yöntemlere değil operatörlere ve işlevlere dayanır. Gibi bir ifade 42.sqrtbirçok kullanıcı için çok daha yabancı görünecektir sqrt(42). Matematik ağırlıklı bir kullanıcı olarak, nokta-yöntem-çağrı sözdizimi yerine kendi işleçlerimi yaratmayı tercih ederim.
  • Tek Sorumluluk İlkesi, bir türün parçası olan işlem sayısını, temel işlemlerle sınırlandırmamızı teşvik eder. Çarpma ile karşılaştırıldığında, kareköküne oldukça nadiren ihtiyacınız var. Dil özellikle daha sonra temel öğeler sağlayan istatistiki anlysis öngörüldüğü takdirde (örneğin operasyonlar gibi mean, median, variance, std, normalizesayısal listeleri veya sayılar için Gama fonksiyonu üzerine) yararlı olabilir. Genel amaçlı bir dil için, bu sadece arayüzü azaltır. Gerekli olmayan işlemleri ayrı bir ad alanına atamak, türü kullanıcıların çoğu için daha erişilebilir hale getirir.

Python, ağır sayıdaki çıtırtı için kullanılan her şey bir nesne diline iyi bir örnektir. NumPy skalerlerinde aslında onlarca ve onlarca yöntem vardır, ancak sqrt hala bunlardan biri değildir. Bunların çoğu gibi şeylerdir transposeve meansadece gerçek işgücü veri yapısı olan NumPy dizileriyle tek tip bir arayüz sağlamak için vardırlar.
user2357112, 18

7
@ user2357112: NumPy'nin kendisi C ve Cython karışımı içinde Python yapıştırıcısıyla yazılmıştır. Aksi takdirde, asla olduğu kadar hızlı olamaz.
Kevin

1
Bence bu cevap, yıllar boyunca ulaşılan bir tür gerçek dünya uzlaşmasına oldukça yaklaştığını düşünüyorum. Öte yandan gerçekten Net anlamda yapabilmek için yapar LINQ uzantıları olarak "Merhaba Dünya" .Max () bizi tanır VE IntelliSense çok visibile yapar. Bonus puanları: Sonuç nedir? Bonus Bonus, Unicode ... 'un sonucu nedir?
Andyz Smith,

15

Bir ton özel amaçlı matematik işlevi olduğu ve her matematik türünü bir yardımcı sınıfına koyduğunuz bu işlevlerin tümü (veya rastgele bir alt kümesi) ile doldurmak yerine, motive olurdum. Aksi takdirde, otomatik tamamlama araç ipucunuzu kirletirsiniz veya insanları her zaman iki yerde aramaya zorlarsınız. (Mı sinüye olmak kadar önemli Doubleya onu olduğu Mathgibi kendilenmiş birlikte sınıfa htanve exp1p?)

Bir başka pratik sebep, farklı yöntemler ve hassas değişimlerle sayısal yöntemler uygulamanın farklı yolları olabileceğidir. Java Mathda vardır ve aynı zamanda vardır StrictMath.


Dil tasarımcılarının otomatik tamamlama araç ipuçlarını umursamadıklarını umuyorum. Ayrıca, ne oluyor Math.<^space>? Bu otomatik tamamlama araç ipucu da kirlenir. Tersine, ikinci paragrafınızın muhtemelen burada daha iyi cevaplardan biri olduğunu düşünüyorum.
Qix

@Qix Onlar yaparlar. Bununla birlikte, buradaki diğer insanlar buna "arayüzün şişirilmiş hale getirilmesi" diyebilir.
Aleksandr Dubinsky

6

Burada oyunda meraklı bir simetri olduğunu doğru gözlemlediniz.

İster söylemesem sqrt(n)ya n.sqrt()da gerçekten fark etmesem de, ikisi de aynı şeyi ifade eder ve hangisini tercih ederseniz, her şeyden çok kişisel zevk meselesidir.

Bu nedenle, iki sözdizimini birbiriyle değiştirilebilmesi için bazı dil tasarımcılarının güçlü bir argümanı olmasının nedeni de budur. D programlama dili zaten buna Uniform Function Call Syntax adı verilen bir özellik altında izin veriyor . Benzer bir özellik, C ++ 'da standardizasyon için de önerilmiştir . Mark Amery'nin dediği gibi , Python da buna izin veriyor.

Bu sorunsuz değil. Bunun gibi temel bir sözdizimi değişikliğinin tanıtılması, var olan kod için geniş kapsamlı sonuçlara sahiptir ve elbette, onlarca yıldır, iki sözdizimi farklı şeyleri tanımlamak olarak düşünmek için eğitilmiş geliştiriciler arasında tartışmalı tartışmalar konusudur.

Sanırım ikisinin birleşmesinin uzun vadede mümkün olup olmadığını yalnızca zaman gösterecek, ancak kesinlikle ilginç bir konudur.


Python bu iki sözdizimini zaten desteklemektedir. Statik olmayan her yöntem selfilk parametresi olarak alır ve yöntemi sınıfın bir özelliği yerine bir örneğin özelliği olarak çağırdığınızda, örnek örtülü olarak ilk argüman olarak iletilir. Dolayısıyla yazabilirim "foo".startswith("f")ya str.startswith("foo", "f")da yazabilirim my_list.append(x)ya da yazabilirim list.append(my_list, x).
Mark Amery

@MarkAmery İyi nokta. Bu, D veya C ++ önerilerinin yaptığı kadar sert değil, ancak genel fikre uyuyor. Gösterdiğin için teşekkürler!
ComicSansMS 5:15

3

D Stanley'in cevabına ek olarak, polimorfizm hakkında düşünmelisin. Math.Sqrt gibi yöntemler her zaman aynı değeri aynı girişe döndürmelidir. Yöntemi statik hale getirmek, bu noktayı netleştirmek için iyi bir yoldur, çünkü statik yöntemler geçersiz kılınamaz.

ToString () - yönteminden bahsettiniz. Burada bu yöntemi geçersiz kılmak isteyebilirsiniz, bu nedenle (sub) sınıfı, ana sınıfı olarak String olarak başka bir şekilde temsil edilir. Yani bunu bir örnek yöntem yapıyorsun.


2

Peki, Java'da her temel tip için bir sarmalayıcı var.
Ve temel tipler sınıf tipi değildir ve üye fonksiyonlara sahip değildir.

Yani, aşağıdaki seçeneklere sahipsiniz:

  1. Tüm bu yardımcı fonksiyonları bir benzeri forma-sınıfında toplayın Math.
  2. İlgili sarıcı üzerinde statik bir işlev yapın.
  3. Karşılık gelen sarıcı üzerinde bir üye işlevi yapın.
  4. Java kurallarını değiştirin.

Seçenek 4'ü ekarte edelim, çünkü ... Java Java ve buna benzer şekilde istekli olduklarını savunuyorlar.

Nesneleri tahsis oldukça ucuz olsa da, ücretsiz değil, ve tekrar tekrar yapıyor çünkü Şimdi, biz de seçeneği 3 eleyebilmemiz gelmez kadar ekleyin.

İki aşağı, biri hala öldürmek için: Seçenek 2 de kötü bir fikir, çünkü her bir işlevin her tür için uygulanması gerektiği , biri boşlukları doldurmak için dönüştürmenin genişletilmesine güvenememesi gerektiği ya da tutarsızlıkların gerçekten acıtacağı anlamına geliyor.
Ve baktığımızda , özellikle ilgili olanlardan daha küçük olan türleri için çok fazla boşluk java.lang.Mathvar .intdouble

Böylece, sonuçta açık bir galibi seçenek olanıdır, hepsini fayda-işlev sınıfında bir yerde toplarlar.

Seçenek 4'e geri döndüğünüzde, bu yöndeki bir şey aslında çok daha sonra oldu: Derleyiciden, adları uzun süredir çözerken istediğiniz sınıfın tüm statik üyelerini düşünmesini isteyebilirsiniz. import static someclass.*;

Diğer taraftan, diğer dillerde bu sorun yoktur; çünkü ya serbest işlevlere (isteğe bağlı olarak ad alanlarını kullanarak) ya da daha az küçük tiplere karşı önyargıları yoktur.


1
Math.min()Tüm sarıcı tiplerinde varyasyonları uygulama zevkini düşünün .

4 numaralı inandırıcı buluyorum. Math.sqrt()Java'nın geri kalanıyla aynı zamanda yaratılmıştı, bu nedenle sqrt () 'a karar verildiğinde, Math"böyle hoşuna giden" Java kullanıcılarının tarihsel bir ataleti yoktu. Bununla ilgili bir sorun olmamakla birlikte sqrt(), aşırı yüklenme davranışı Math.round()iğrençtir. Üye sözdizimini tip değerleri ile kullanabilmek floatve doublebu problemden kaçınmak.
supercat,

2

Açıkça bahsetmediğim bir nokta (buna itiraz etmemesine rağmen), karekökün “türetilmiş” bir işlem olarak düşünülebileceğidir: uygulama bizim için sağlamazsa, kendimiz yazabiliriz.

Soru dil tasarımı ile etiketlendiğinden, bazı dil-onaylı açıklamalar yapabiliriz. Birçok dilin farklı felsefeleri olmasına rağmen, değişmezleri korumak için enkapsülasyon kullanmak paradigmalar arasında çok yaygındır; yani türünün önerdiği gibi davranmayan bir değere sahip olmamak için.

Örneğin, makine kelimelerini kullanan bazı tamsayılar uygulamayız, muhtemelen bir şekilde temsili kapsıyoruz (örneğin, bit kaymalarının işaret değiştirmesini önlemek için), ancak aynı zamanda, bu gibi işlemleri gerçekleştirmek için hala bu bitlere erişmemiz gerekiyor. ilave.

Bazı diller bunu sınıflar ve özel yöntemlerle uygulayabilir:

class Int {
    public Int add(Int x) {
      // Do something with the bits
    }
    private List<Boolean> getBits() {
      // ...
    }
}

Bazı modül sistemleri ile:

signature INT = sig
  type int
  val add : int -> int -> int
end

structure Word : INT = struct
  datatype int  = (* ... *)
  fun add x y   = (* Do something with the bits *)
  fun getBits x = (* ... *)
end

Bazı sözcüksel kapsamı:

(defun getAdder ()
   (let ((getBits (lambda (x) ; ...
         (add     (lambda (x y) ; Do something with the bits
     'add))

Ve bunun gibi. Bununla birlikte, karekök uygulamak için bu mekanizmaların hiçbirine ihtiyaç duyulmaz: sayısal bir türün ortak arayüzü kullanılarak uygulanabilir ve dolayısıyla kapsüllenmiş uygulama detaylarına erişmesi gerekmez.

Bu nedenle kareköklerin konumu, dilin ve kütüphane tasarımcısının felsefesine / zevklerine iner. Bazı (örneğin o bir örnek yöntemi olun), bazı ilkel operasyonların aynı düzeyde koymak tercih edebilir o "iç" sayısal değerleri koymak tercih edebilir (bu bir örnek yöntemini anlamına gelebilir, ya da yaşayan anlamına gelebilir dışında ancak sayısal değerler içindeki bazı "yardımcı" fonksiyonları bir koleksiyon koymak seçebilirsiniz) Tek başına fonksiyonu veya statik yöntem olarak aynı modül / sınıf / ad, örneğin bazı üçüncü taraf kütüphanelere bunu temsilci seçebilir.


Hiçbir şey bir dilin statik bir yöntemin üye sözdizimini kullanarak (C # veya vb.net eklentisi yöntemlerinde olduğu gibi) veya üye dizisinin bir varyasyonuyla çağrılmasını engelleyemez (çift noktalı sözdizimini görmeyi çok isterdim, Intellisense'in yalnızca birincil argüman için uygun olan fonksiyonları listeleyebilme avantajlarını listeleyebilmesine izin vermek, ancak gerçek üye operatörleri ile belirsizlikten kaçınmak için).
supercat,

-2

Java ve C # ToString, sınıf hiyerarşisinin kökü olan bir nesne yöntemidir, böylece her nesne ToString yöntemini uygular. Bir Tamsayı türü için, ToString'in uygulanmasının bu şekilde çalışması doğaldır.

Demek akıl yürütme yanlışsın. Değer türlerinin ToString'i uygulamasının nedeni, bazı kişilerin hoşuna gitmemesidir: hey, Değer türleri için bir ToString yöntemine sahip olalım. Çünkü ToString zaten orada ve çıktısı için "en doğal" şey.


1
Elbette bir karardır, yani objectbir ToString()yönteme sahip olma kararıdır . Bu, "bazı insanlar gibiydi: hey, Değer türleri için bir ToString yöntemine sahip olalım" sözlerinde.
Residuum

-3

String.substring'in aksine, Number.sqrt gerçekten sayının bir niteliği değil, numaranıza dayalı yeni bir sonuçtur. Numaranızı bir kare alma işlevine geçirmenin daha sezgisel olduğunu düşünüyorum.

Dahası, Math nesnesi başka statik üyeler içerir ve onları bir araya toplayıp tek tip bir şekilde kullanmak daha mantıklıdır.


3
Sayaç örneği: BigInteger.pow () .
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.