ใครช่วยบอกหน่อยได้ไหมว่าเหตุใด Haskell Prelude จึงกำหนดฟังก์ชันแยกกันสองฟังก์ชันสำหรับการยกกำลัง (เช่น^และ**) ฉันคิดว่าระบบประเภทควรจะกำจัดการทำซ้ำประเภทนี้
Prelude> 2^2
4
Prelude> 4**0.5
2.0
ใครช่วยบอกหน่อยได้ไหมว่าเหตุใด Haskell Prelude จึงกำหนดฟังก์ชันแยกกันสองฟังก์ชันสำหรับการยกกำลัง (เช่น^และ**) ฉันคิดว่าระบบประเภทควรจะกำจัดการทำซ้ำประเภทนี้
Prelude> 2^2
4
Prelude> 4**0.5
2.0
คำตอบ:
มีจริงยกกำลังสามผู้ประกอบการ: (^), และ(^^) เป็นเลขชี้กำลังอินทิกรัลที่ไม่เป็นลบคือการยกกำลังจำนวนเต็มและเป็นการยกกำลังจุดลอยตัว:(**)^^^**
(^) :: (Num a, Integral b) => a -> b -> a
(^^) :: (Fractional a, Integral b) => a -> b -> a
(**) :: Floating a => a -> a -> a
เหตุผลคือความปลอดภัยของประเภท: ผลลัพธ์ของการดำเนินการเชิงตัวเลขโดยทั่วไปจะมีประเภทเดียวกับอาร์กิวเมนต์อินพุต แต่คุณไม่สามารถเพิ่มIntกำลังทศนิยมและรับผลลัพธ์ของประเภทIntได้ ดังนั้นระบบประเภทจึงป้องกันไม่ให้คุณทำสิ่งนี้: (1::Int) ** 0.5สร้างข้อผิดพลาดประเภท (1::Int) ^^ (-1)เดียวกันจะไปสำหรับ
อีกวิธีหนึ่งที่จะนำนี้: Numประเภทจะปิดใต้^(พวกเขาไม่จำเป็นต้องมีความผกผัน) Fractionalประเภทอยู่ภายใต้การปิด^^, ประเภทที่อยู่ภายใต้ปิดFloating **เนื่องจากไม่มีFractionalอินสแตนซ์สำหรับIntคุณจึงไม่สามารถยกระดับเป็นพลังลบได้
ตามหลักการแล้วอาร์กิวเมนต์ที่สองของ^จะถูก จำกัด แบบคงที่ให้ไม่เป็นลบ (ปัจจุบัน1 ^ (-2)มีข้อยกเว้นรันไทม์) แต่ไม่มีประเภทของจำนวนธรรมชาติในPrelude.
ระบบประเภทของ Haskell ไม่มีประสิทธิภาพเพียงพอที่จะแสดงตัวดำเนินการเลขชี้กำลังทั้งสามเป็นหนึ่งเดียว สิ่งที่คุณต้องการจริงๆมีดังนี้:
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 **
สิ่งนี้ใช้ไม่ได้จริงแม้ว่าคุณจะเปิดส่วนขยายคลาสประเภทหลายพารามิเตอร์เนื่องจากการเลือกอินสแตนซ์จะต้องฉลาดกว่าที่ Haskell อนุญาตในปัจจุบัน
Int Integerเพื่อให้สามารถประกาศอินสแตนซ์ทั้งสามนี้ได้ความละเอียดของอินสแตนซ์ต้องใช้การย้อนรอยและไม่มีคอมไพเลอร์ Haskell ดำเนินการดังกล่าว
มันไม่ได้กำหนดตัวดำเนินการสองตัว - มันกำหนดสามตัว! จากรายงาน:
มีการดำเนินการยกกำลังสองอาร์กิวเมนต์สามรายการ: (
^) ยกจำนวนใด ๆ ให้เป็นเลขกำลังจำนวนเต็มที่ไม่เป็นลบ, (^^) ยกจำนวนเศษส่วนเป็นกำลังจำนวนเต็มใด ๆ และ (**) รับอาร์กิวเมนต์ทศนิยมสองตัว ค่าx^0หรือx^^0เท่ากับ 1 สำหรับค่าใด ๆxรวมถึงศูนย์0**yไม่ได้กำหนด
ซึ่งหมายความว่ามีอัลกอริทึมที่แตกต่างกันสามแบบซึ่งสองวิธีให้ผลลัพธ์ที่แน่นอน ( ^และ^^) ในขณะที่**ให้ผลลัพธ์โดยประมาณ การเลือกโอเปอเรเตอร์ที่จะใช้คุณจะเลือกอัลกอริทึมที่จะเรียกใช้
^ต้องการอาร์กิวเมนต์ที่สองเป็นIntegralไฟล์. ถ้าฉันจำไม่ผิดการใช้งานจะมีประสิทธิภาพมากขึ้นถ้าคุณรู้ว่าคุณกำลังทำงานกับเลขชี้กำลังอินทิกรัล นอกจากนี้หากคุณต้องการบางอย่างเช่น2 ^ (1.234)แม้ว่าฐานของคุณจะเป็นอินทิกรัล 2 ผลลัพธ์ของคุณจะเป็นเศษส่วน คุณมีตัวเลือกมากขึ้นเพื่อให้คุณสามารถควบคุมประเภทที่จะเข้าและออกจากฟังก์ชันเลขชี้กำลังของคุณได้อย่างรัดกุมยิ่งขึ้น
ระบบประเภทของ Haskell ไม่มีเป้าหมายเดียวกันกับระบบประเภทอื่น ๆ เช่น C's, Python's หรือ Lisp's การพิมพ์เป็ด (เกือบ) ตรงข้ามกับความคิดของ Haskell
class Duck a where quack :: a -> Quackกำหนดสิ่งที่เราคาดหวังจากเป็ดจากนั้นแต่ละอินสแตนซ์จะระบุสิ่งที่สามารถทำตัวเหมือนเป็ดได้
Duck.