Haskell เปรียบเทียบทั่วไปกับ Char

Nov 06 2020

ฉันต้องการสร้างฟังก์ชัน 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' ไม่ใช่ทั่วไป)

คำตอบ

3 leftaroundabout Nov 06 2020 at 02:43

การตีความคำถามที่เป็นไปได้อย่างหนึ่งคือคุณต้องการให้ฟังก์ชันบอกว่าอาร์กิวเมนต์เป็น 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
3 JonPurdy Nov 06 2020 at 04:05

ในตัวของมันเองฟังก์ชันประเภท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]
1 JohnF.Miller Nov 06 2020 at 02:37

และสิ่งที่คุณชอบทำถ้า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