ฉันมีแบบฝึกหัดที่ฉันต้องกำหนดประเภทเพื่อแสดงรายการที่มีค่า 0 ถึง 5 ก่อนอื่นฉันคิดว่าฉันสามารถแก้ปัญหาแบบนี้ซ้ำได้:
data List a = Nil | Content a (List a)
แต่ฉันไม่คิดว่านี่เป็นวิธีที่ถูกต้อง คุณช่วยให้อาหารฉันด้วยความคิดได้ไหม
ฉันมีแบบฝึกหัดที่ฉันต้องกำหนดประเภทเพื่อแสดงรายการที่มีค่า 0 ถึง 5 ก่อนอื่นฉันคิดว่าฉันสามารถแก้ปัญหาแบบนี้ซ้ำได้:
data List a = Nil | Content a (List a)
แต่ฉันไม่คิดว่านี่เป็นวิธีที่ถูกต้อง คุณช่วยให้อาหารฉันด้วยความคิดได้ไหม
คำตอบ:
ฉันจะไม่ตอบแบบฝึกหัดของคุณสำหรับแบบฝึกหัดดีกว่าที่จะหาคำตอบด้วยตัวคุณเอง แต่นี่เป็นคำใบ้ที่จะนำคุณไปสู่คำตอบ: คุณสามารถกำหนดรายการที่มีองค์ประกอบ 0 ถึง 2 องค์ประกอบ
data List a = None | One a | Two a a
ตอนนี้ลองคิดดูว่าคุณจะขยายองค์ประกอบนี้เป็นห้าองค์ประกอบได้อย่างไร
การแก้ปัญหาแบบเรียกซ้ำนั้นเป็นเรื่องปกติและเป็นเรื่องดีใน Haskell แต่มันก็ค่อนข้างยากที่จะ จำกัด จำนวนขององค์ประกอบ ดังนั้นสำหรับวิธีแก้ปัญหาอย่างง่าย ๆอันดับแรกให้พิจารณาเรื่องที่โง่ แต่ทำงานโดย bradm
ด้วยโซลูชันแบบเรียกซ้ำเคล็ดลับคือการส่งผ่านตัวแปร“ ตัวนับ” แบบเรียกซ้ำลงไปจากนั้นปิดใช้งานการรวมองค์ประกอบเพิ่มเติมเมื่อคุณไปถึงค่าสูงสุดที่อนุญาต สิ่งนี้สามารถทำได้ด้วย GADT:
{-# LANGUAGE GADTs, DataKinds, KindSignatures, TypeInType, StandaloneDeriving #-}
import Data.Kind
import GHC.TypeLits
infixr 5 :#
data ListMax :: Nat -> Type -> Type where
Nil :: ListMax n a
(:#) :: a -> ListMax n a -> ListMax (n+1) a
deriving instance (Show a) => Show (ListMax n a)
แล้วก็
*Main> 0:#1:#2:#Nil :: ListMax 5 Int
0 :# (1 :# (2 :# Nil))
*Main> 0:#1:#2:#3:#4:#5:#6:#Nil :: ListMax 5 Int
<interactive>:13:16: error:
• Couldn't match type ‘1’ with ‘0’
Expected type: ListMax 0 Int
Actual type: ListMax (0 + 1) Int
• In the second argument of ‘(:#)’, namely ‘5 :# 6 :# Nil’
In the second argument of ‘(:#)’, namely ‘4 :# 5 :# 6 :# Nil’
In the second argument of ‘(:#)’, namely ‘3 :# 4 :# 5 :# 6 :# Nil’
เพื่อความสมบูรณ์ฉันขอเพิ่มวิธีการทางเลือก "น่าเกลียด" ซึ่งเป็นพื้นฐานที่ค่อนข้างง่าย
จำได้ว่าMaybe a
เป็นชนิดที่มีค่าเป็นในรูปแบบNothing
หรือสำหรับบางคนJust x
x :: a
ดังนั้นโดยการตีความค่าข้างต้นใหม่เราสามารถพิจารณาMaybe a
ว่าเป็น "ประเภทลิสต์ที่ถูก จำกัด " ซึ่งลิสต์อาจมีองค์ประกอบที่เป็นศูนย์หรือหนึ่งองค์ประกอบ
ตอนนี้(a, Maybe a)
เพียงเพิ่มองค์ประกอบอีกหนึ่งรายการดังนั้นจึงเป็น "ประเภทรายการ" ที่รายการสามารถมีองค์ประกอบหนึ่ง ( (x1, Nothing)
) หรือสอง ( (x1, Just x2)
)
ดังนั้นจึงMaybe (a, Maybe a)
เป็น "ประเภทรายการ" ที่รายการสามารถมีองค์ประกอบศูนย์ ( Nothing
), หนึ่ง ( Just (x1, Nothing)
) หรือสอง ( (Just (x1, Just x2)
) องค์ประกอบ
ตอนนี้คุณควรจะสามารถเข้าใจวิธีการดำเนินการต่อไป ให้ฉันเครียดอีกครั้งว่านี่ไม่ใช่วิธีแก้ปัญหาที่สะดวกในการใช้ แต่เป็น (IMO) การออกกำลังกายที่ดีที่จะเข้าใจมันต่อไป
ด้วยการใช้คุณสมบัติขั้นสูงของ Haskell เราสามารถพูดคุยด้านบนโดยใช้ประเภทตระกูล:
type family List (n :: Nat) (a :: Type) :: Type where
List 0 a = ()
List n a = Maybe (a, List (n-1) a)
a
ในEither () (a, Either () (a, Either () (a, Either () ())))
... พีชคณิตประเภทที่น่าสนใจ, foldr (.) id (replicate 3 $ ([0] ++) . liftA2 (+) [1]) $ [0] == [0,1,2,3]
.