Haskell'de üs alma


91

Birisi bana Haskell Prelude'un üs alma için neden iki ayrı işlevi tanımladığını söyleyebilir mi (yani ^ve **)? Yazı sisteminin bu tür bir tekrarlamayı ortadan kaldırması gerektiğini sanıyordum.

Prelude> 2^2
4
Prelude> 4**0.5
2.0

Yanıtlar:


130

Orada üç üs alma işlemlerinin aslında şunlardır: (^), (^^)ve (**). ^negatif olmayan ^^tamsayı üssü, tamsayı üssüdür ve **kayan noktalı üssüdür:

(^) :: (Num a, Integral b) => a -> b -> a
(^^) :: (Fractional a, Integral b) => a -> b -> a
(**) :: Floating a => a -> a -> a

Bunun nedeni tip güvenliğidir: sayısal işlemlerin sonuçları genellikle girdi bağımsız değişken (ler) i ile aynı türe sahiptir. Ama Intbir kayan noktalı kuvvete yükseltip türden bir sonuç alamazsınız Int. Ve böylece tür sistemi bunu yapmanızı engeller: (1::Int) ** 0.5bir tür hatası üretir. Aynısı için de geçerli (1::Int) ^^ (-1).

Bunu ifade etmenin başka bir yolu: Numtürler altında kapatılır ^(çarpımsal tersi olması gerekmez), Fractionaltürler altında kapatılır ^^, Floatingtürler altında kapatılır **. Örneği olmadığı Fractionaliçin Int, onu negatif bir güce yükseltemezsiniz.

İdeal olarak, ikinci argüman ^statik olarak negatif olmayacak şekilde sınırlandırılır (şu anda 1 ^ (-2)bir çalışma zamanı istisnası atar). Ancak .tw dosyasında doğal sayıların türü yoktur Prelude.


31

Haskell'in tür sistemi, üç üs alma operatörünü bir olarak ifade edecek kadar güçlü değil. Gerçekten isteyeceğiniz şey şuna benzer:

class Exp a b where (^) :: a -> b -> a
instance (Num a,        Integral b) => Exp a b where ... -- current ^
instance (Fractional a, Integral b) => Exp a b where ... -- current ^^
instance (Floating a,   Floating b) => Exp a b where ... -- current **

Örnek seçiminin Haskell'in şu anda izin verdiğinden daha akıllı olması gerektiğinden, çok parametreli tür sınıf uzantısını açsanız bile bu gerçekten işe yaramaz.


4
Bununla ilgili ifade uygulanabilir değil mi? IIRC, haskell, çok parametreli bir tür sınıfının ikinci parametresinin kesinlikle birinci parametre tarafından belirlenmesine yönelik bir seçeneğe sahiptir. Bunun ötesinde çözülemeyen başka bir sorun var mı?
RussellStewart

2
@singular Hala doğru. İlk argüman ikinciyi belirlemez, örneğin üssün hem Intve olmasını istersiniz Integer. Bu üç örnek bildirimine sahip olabilmek için, örnek çözümlemesinin geri izleme kullanması gerekir ve hiçbir Haskell derleyicisi bunu gerçekleştirmez.
ağustos

7
Does "tür sistemi yeterince güçlü değil" argümanı hala 2015 Mart itibariyle tutun?
Erik Kaplun

3
Kesinlikle önerdiğim gibi yazamazsınız, ancak onu kodlamanın bir yolu olabilir.
ağustos

2
Hiçbir yeni Haskell Raporu 2010 yılından bu yana çıktı olarak @ErikAllik muhtemelen standart Haskell için yaptığı
Martin Capodici

10

İki operatörü tanımlamaz - üçü tanımlar! Rapordan:

Üç adet iki argümanlı üs alma işlemi vardır: ( ^) herhangi bir sayıyı negatif olmayan bir tamsayı kuvvetine ^^yükseltir , ( ) kesirli bir sayıyı herhangi bir tamsayı kuvvetine yükseltir ve ( **) iki kayan noktalı argüman alır. Değeri x^0ya da x^^0herhangi biri için 1 xsıfır dahil olmak üzere; 0**ytanımsız.

Bu, ikisi kesin sonuçlar ( ^ve ^^) verirken **yaklaşık sonuçlar veren üç farklı algoritma olduğu anlamına gelir . Hangi operatörü kullanacağınızı seçerek, hangi algoritmayı çağıracağınızı seçersiniz.


4

^ikinci argümanının bir Integral. Eğer yanılmıyorsam, integral bir üs ile çalıştığınızı biliyorsanız, uygulama daha verimli olabilir. Ayrıca, 2 ^ (1.234)tabanınız bir integral 2 olmasına rağmen, gibi bir şey istiyorsanız , sonucunuz kesinlikle kesirli olacaktır. Üs alma işlevinize hangi türlerin girip çıkacağı üzerinde daha sıkı kontrol sahibi olmak için daha fazla seçeneğiniz vardır.

Haskell'in tip sistemi, C'ler, Python'lar veya Lisp'ler gibi diğer tip sistemlerle aynı amaca sahip değildir. Ördek yazma, Haskell zihniyetinin (neredeyse) tam tersidir.


4
Haskell tipi zihniyetin ördek yazmanın tersi olduğuna tamamen katılmıyorum. Haskell türü sınıflar ördek yazmaya oldukça benzer. class Duck a where quack :: a -> Quackbir ördekten ne beklediğimizi tanımlar ve sonra her örnek bir ördek gibi davranabilecek bir şeyi belirtir.
ağustos

9
@augustss Nereden geldiğini anlıyorum. Ancak ördek yazmanın arkasındaki gayri resmi slogan "ördek gibi görünüyorsa, ördek gibi davranıyorsa ve ördek gibi şarlatansa ördek gibi davranıyorsa, o zaman ördek" dir. Haskell'de, bir örneği ilan edilmedikçe bir ördek değildir Duck.
Dan Burton

1
Bu doğru, ama Haskell'den bekleyeceğim şey bu. İstediğin her şeyi ördek yapabilirsin ama bu konuda açık olmalısın. Ördek olmasını istemediğimiz bir şeyi yanıltmak istemiyoruz.
ağustos

Haskell'in bir şeyler yapma şekli ile ördek yazması arasında daha belirgin bir fark var. Evet, herhangi bir türe Duck sınıfını verebilirsiniz, ancak bu bir Duck değildir. Dolandırabilir, elbette ama yine de somut olarak, türü ne olursa olsun. Hâlâ Ördekler listeniz olamaz. Ördeklerin bir listesini kabul eden ve çeşitli ördek sınıflarını karıştırıp eşleştiren bir işlev çalışmayacaktır. Bu bakımdan Haskell, "Ördek gibi şarlatansa, o zaman bir ördek" demenize izin vermiyor. Haskell'de, tüm Ördekleriniz aynı türden Quackers olmalıdır. Bu aslında ördek yazmadan oldukça farklı.
mmachenry

Karışık ördeklerin bir listesine sahip olabilirsiniz, ancak Varoluşsal Niceleme'nin uzantısına ihtiyacınız var.
Bolpat
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.