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
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:
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 Int
bir 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.5
bir tür hatası üretir. Aynısı için de geçerli (1::Int) ^^ (-1)
.
Bunu ifade etmenin başka bir yolu: Num
türler altında kapatılır ^
(çarpımsal tersi olması gerekmez), Fractional
türler altında kapatılır ^^
, Floating
türler altında kapatılır **
. Örneği olmadığı Fractional
iç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
.
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.
Int
ve 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.
İ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ğerix^0
ya dax^^0
herhangi biri için 1x
sıfır dahil olmak üzere;0**y
tanı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.
^
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.
class Duck a where quack :: a -> Quack
bir ördekten ne beklediğimizi tanımlar ve sonra her örnek bir ördek gibi davranabilecek bir şeyi belirtir.
Duck
.