Haskell เปรียบเทียบทั่วไปกับ Char
ฉันต้องการสร้างฟังก์ชัน Haskell ที่มีอินพุตทั่วไป แต่ส่งออกบูลีนขึ้นอยู่กับว่าตัวเลขทั่วไปตรงกับตัวเลข / อักขระบางอย่างตามบรรทัดของ:
checkFunct :: a -> Bool
checkFunct x = (elem x ['a','b','c'])
วิธีนี้จะไม่ได้ผลเพราะคุณไม่สามารถเปรียบเทียบ Char กับทั่วไปได้ ฉันมองหามานานแล้วและฉันยังใหม่กับ Haskell ดังนั้นจึงไม่รู้ว่าจะแก้ไขอย่างไร หมายเหตุ: ระบุไว้เป็นพิเศษว่าอินพุตต้องเป็น -> บูล
ตัวอย่างเช่นรหัสนี้ทำงานได้อย่างสมบูรณ์:
checkFunct :: Char -> String
p x = elem x ["a","b","c"]
อย่างไรก็ตามฉันต้องการ 'a' insteaed ของ Char ขออภัยในความสับสน (เห็นได้ชัดว่า 'a' ไม่ใช่ทั่วไป)
คำตอบ
การตีความคำถามที่เป็นไปได้อย่างหนึ่งคือคุณต้องการให้ฟังก์ชันบอกว่าอาร์กิวเมนต์เป็น a Charและอยู่ในรายการหรือไม่ คุณไม่สามารถทำเช่นนั้นที่มีลายเซ็นa -> Boolคนเดียวเนื่องจากข้อมูลประเภทไม่สามารถใช้ได้กับฟังก์ชั่น แต่คุณสามารถต้องการข้อมูลประเภทอย่างชัดเจน: มีชั้นเฉพาะสำหรับการนี้
import Data.Typeable
checkFunct :: Typeable a => a -> Bool
checkFunct x = case cast x of
Just xc -> xc `elem` ['a','b','c']
Nothing -> False
(โปรดทราบว่าไม่เหมือนในภาษาอื่น ๆcastไม่ได้แปลงประเภทจริงๆแต่จะจับคู่ว่าเป็นประเภทเป้าหมายตั้งแต่แรกหรือไม่)
*Main> checkFunct 'b'
True
*Main> checkFunct 'f'
False
*Main> checkFunct 37
False
*Main> checkFunct "a"
False
ในตัวของมันเองฟังก์ชันประเภทa -> Boolซึ่งย่อมาจากforall a. a -> Boolไม่ได้ใช้งานมากนักเนื่องจากต้องใช้งานได้“ ทั้งหมดa” ตามที่ผู้โทรระบุดังนั้นจึงไม่ได้รับอนุญาตให้รู้อะไรaเลย นั่นเป็นเหตุผลที่คุณได้รับคำตอบโดยทั่วไปว่า“ คุณไปที่นั่นจากที่นี่ไม่ได้”
(อันที่จริงมีฟังก์ชันทั้งหมดที่แท้จริงเพียงสองฟังก์ชันสำหรับประเภทนี้: alwaysTrue _ = TrueและalwaysFalse _ = False.)
แต่ฉันคาดเดาตามความคิดเห็นของคุณ:
ฉันต้องการฟังก์ชันที่มีและอินพุต
:: a -> Boolและจำเป็นต้องคำนวณว่าaเป็นรายการในรายการที่ระบุหรือไม่
คำถามของคุณอยู่ในบริบทของฟังก์ชันเช่นนี้:
contains :: (a -> Bool) -> [a] -> Bool
ตอนนี้เราสามารถทำสิ่งที่เป็นประโยชน์ได้แล้ว! ดูประเภทนี้ย่อมาจากforall a. (a -> Bool) -> [a] -> Boolซึ่งกล่าวคือมันยังทำงานกับค่าของประเภทใด ๆaแต่อีกครั้งผู้เรียกของฟังก์ชันนี้เป็นผู้เลือกaและส่งผ่านเพรดิเคตของประเภทa -> Bool สำหรับบางaประเภทที่เฉพาะเจาะจงเช่นเดียวกับ รายการaค่า ดังนั้นcontainsไม่ได้รู้อะไรเกี่ยวกับaแต่ก็ไม่ทราบว่ามันสามารถใช้กริยากับองค์ประกอบของรายการเนื่องจากพวกเขากำลังรับประกันว่าจะเป็นประเภทเดียวกัน
ตัวอย่างเช่นคุณสามารถเรียกใช้ฟังก์ชันนี้โดยใช้อาร์กิวเมนต์ประเภทChar -> Boolซึ่งกำหนดaเป็นCharหรือประเภทใดประเภทหนึ่งInt -> Boolซึ่งกำหนดaเป็นIntและอื่น ๆ :
isABC :: Char -> Bool
isABC x = x `elem` "abc"
-- contains :: (a -> Bool) -> [a] -> Bool
-- contains isABC :: [Char] -> Bool
-- contains isABC "yeah" == True
-- contains isABC "nope" == False
is123 :: Int -> Bool
is123 x = x >= 1 && x <= 3
-- contains is123 :: [Int] -> Bool
-- contains is123 [] == False
-- contains is123 [0] == False
-- contains is123 [0, 1] == True
หากคุณต้องการเขียนcontainsฟังก์ชันนี้ด้วยตัวเองคุณสามารถใช้คำจำกัดความแบบวนซ้ำโดยมีโครงสร้างทั่วไปนี้:
contains :: (a -> Bool) -> [a] -> Bool
contains predicate list = case list of
-- How can you test the current element with the predicate?
-- What should you do when the predicate returns true?
-- How can you test the rest of the list?
current : rest -> _
-- What should you return if you’ve reached the end?
[] -> _
ขีดล่างเรียกว่าช่องพิมพ์ซึ่งคุณสามารถรวมไว้ในโปรแกรมของคุณเพื่อขอให้ GHC บอกประเภทที่คาดหวังไว้ที่นั่นและให้คำแนะนำเกี่ยวกับฟังก์ชันและตัวแปรในขอบเขตที่อาจเหมาะสม if _ then _ else _ในขณะที่คุณคิดออกวิธีการกรอกข้อมูลในรายละเอียดเพิ่มเติมของโปรแกรมของคุณคุณสามารถใช้หลุมมากขึ้นเพื่อขอประเภทของชิ้นส่วนเฉพาะเจาะจงมากขึ้นเช่นคุณอาจเปลี่ยนหลุมแรกด้วย
สิ่งที่อาจเป็นประโยชน์ในที่นี้คือTypeApplicationsส่วนขยาย (เปิดใช้งานใน GHCi ด้วย:set -XTypeApplicationsหรือในไฟล์ต้นฉบับด้วย{-# LANGUAGE TypeApplications #-}) ซึ่งช่วยให้คุณสามารถเขียนอาร์กิวเมนต์ประเภทอย่างชัดเจนเช่นใน GHCi:
> :set -XTypeApplications
> contains :: (a -> Bool) -> [a] -> Bool; contains = error "TODO: implement 'contains'"
> :type contains @Char
contains @Char :: (Char -> Bool) -> [Char] -> Bool
> :type contains @Int
contains @Int :: (Int -> Bool) -> [Int] -> Bool
> :t map
map :: (a -> b) -> [a] -> [b]
> :t map @Double
map @Double :: (Double -> b) -> [Double] -> [b]
> :t map @Double @Int
map @Double @Int :: (Double -> Int) -> [Double] -> [Int]
และสิ่งที่คุณชอบทำถ้าaเป็นอย่างนั้นb -> FreeT (PipeF a b)?
ประเภทของคุณบอกว่าฉันสามารถให้คุณทำงานอะไรก็ได้และคุณจะคิดว่าBoolมีเพียงสองวิธีในการเขียนฟังก์ชันนี้ checkFunct _ = TrueและcheckFunct _ = False
คุณต้องบอกคอมไพเลอร์เพิ่มเติมเล็กน้อยเกี่ยวกับสิ่งที่คุณคาดหวังเป็นอินพุต
จากการใช้งานตัวอย่างของคุณฉันจะเดาว่าคุณอาจต้องการดูว่าการแสดงสตริงของ x อยู่ในรายการหรือไม่?
คุณสามารถเขียน:
checkFunct :: Show a => a -> Bool
checkFunct x = (elem (show x) ["a","b","c"])
Show a =>กล่าวว่าสิ่งที่ฉันมอบให้เป็น input เพื่อฟังก์ชั่นนี้show aจะต้องกลับสตริง
หรือบางทีคุณอาจต้องการเขียนชั้นเรียนของคุณเอง
class ToChar a where
toChar :: a -> Char
จากนั้นคุณสามารถเขียน:
checkFunct :: ToChar a=> a -> Bool
checkFunct x = (elem (toChar x) ['a','b','c'])
ในการใช้ 'checkFunct' คุณจะต้องจัดเตรียมการใช้งาน toChar สำหรับประเภทใด ๆ ที่คุณต้องการใช้กับฟังก์ชันนั้น
import Data.Char
instance ToChar Int where
toChar = chr