เมื่อมองผ่าน Haskell Prelude ฉันเห็นฟังก์ชัน const
:
const x _ = x
ดูเหมือนจะไม่พบสิ่งที่เกี่ยวข้องกับฟังก์ชันนี้
ประเด็นคืออะไร? ใครช่วยยกตัวอย่างได้ไหมว่าฟังก์ชันนี้จะใช้งานได้ที่ไหน
เมื่อมองผ่าน Haskell Prelude ฉันเห็นฟังก์ชัน const
:
const x _ = x
ดูเหมือนจะไม่พบสิ่งที่เกี่ยวข้องกับฟังก์ชันนี้
ประเด็นคืออะไร? ใครช่วยยกตัวอย่างได้ไหมว่าฟังก์ชันนี้จะใช้งานได้ที่ไหน
คำตอบ:
มันมีประโยชน์สำหรับการส่งผ่านไปยังฟังก์ชันลำดับที่สูงขึ้นเมื่อคุณไม่ต้องการความยืดหยุ่นทั้งหมด ตัวอย่างเช่นตัวดำเนินการลำดับ monadic >>
สามารถกำหนดในรูปของตัวดำเนินการผูก monadic เป็น
x >> y = x >>= const y
ค่อนข้างเรียบร้อยกว่าการใช้แลมด้า
x >> y = x >>= \_ -> y
และคุณยังสามารถใช้งานได้โดยไม่มีจุด
(>>) = (. const) . (>>=)
แม้ว่าฉันจะไม่แนะนำเป็นพิเศษในกรณีนี้
map (const 42) [1..5]
ผลลัพธ์ใน[42, 42, 42, 42, 42]
.
const
มีประโยชน์สำหรับการใช้กับอาร์กิวเมนต์เดียวเพื่อให้ได้ฟังก์ชันที่จำเป็น (เช่นผ่านไปยังmap
)
head = foldr const (error "Prelude.head: empty list")
เพื่อเพิ่มการตอบที่ตรง hammar ยอดเยี่ยม: ฟังก์ชั่นที่อ่อนน้อมถ่อมตนชอบconst
และid
มีประโยชน์จริงๆเป็นฟังก์ชั่นการสั่งซื้อที่สูงขึ้นสำหรับเหตุผลเดียวกันกับที่พวกเขามีพื้นฐานในCombinator แคลคูลัส
ไม่ใช่ว่าฉันคิดว่าฟังก์ชั่นโหมโรงของ haskell ถูกจำลองขึ้นอย่างมีสติตามระบบที่เป็นทางการหรืออะไรก็ได้ เพียงแค่การสร้างนามธรรมที่สมบูรณ์ใน haskell นั้นง่ายมากดังนั้นคุณมักจะเห็นสิ่งต่างๆทางทฤษฎีเหล่านี้กลายเป็นประโยชน์ในทางปฏิบัติ
ปลั๊กไร้ยางอาย แต่ฉันเขียนบล็อกเกี่ยวกับวิธีการที่อินสแตนซ์การใช้งาน(->)
เป็นจริงS
และตัวK
รวมกันที่นี่ถ้าเป็นสิ่งที่คุณชอบ
((->) e)
ยังเป็นผู้อ่าน monad - ด้วยReader
และเหมือนเป็นเพียงการnewtype
ห่อหุ้ม - และask
ฟังก์ชั่นก็เป็นid
เช่นนั้นนั่นคือตัวI
รวมกันเช่นกัน ถ้าคุณดูที่พื้นฐานแทน BCKW Haskell แกงเดิมB
, K
และW
มีfmap
, return
และjoin
ตามลำดับ
ดูเหมือนจะไม่พบสิ่งที่เกี่ยวข้องกับฟังก์ชันนี้
คำตอบอื่น ๆ อีกมากมายกล่าวถึงแอปพลิเคชันที่ค่อนข้างลึกลับ (อย่างน้อยสำหรับผู้มาใหม่) const
การใช้งานของ นี่คือวิธีง่ายๆ: คุณสามารถใช้const
เพื่อกำจัดแลมด้าที่ใช้อาร์กิวเมนต์สองตัวโยนอันแรกออกไป แต่ทำสิ่งที่น่าสนใจกับอันที่สอง
ตัวอย่างต่อไปนี้ (ไม่มีประสิทธิภาพ แต่ให้คำแนะนำ) การดำเนินการlength
,
length' = foldr (\_ acc -> 1 + acc) 0
สามารถเขียนใหม่เป็นไฟล์
length' = foldr (const (1+)) 0
ซึ่งอาจจะหรูหรากว่า
การแสดงออกconst (1+)
เป็นจริงความหมายเทียบเท่ากับ\_ acc -> 1 + acc
เพราะมันจะใช้เวลาหนึ่งอาร์กิวเมนต์โยนมันออกไปและส่งกลับส่วน (1+)
ตัวอย่างง่ายๆสำหรับการใช้เป็นconst
Data.Functor.(<$)
ด้วยฟังก์ชั่นนี้คุณสามารถพูดได้ว่า: ฉันมี functor ที่มีบางสิ่งที่น่าเบื่ออยู่ในนั้น แต่ฉันต้องการมีสิ่งที่น่าสนใจอื่น ๆ อยู่ในนั้นแทนโดยไม่ต้องเปลี่ยนรูปร่างของ functor เช่น
import Data.Functor
42 <$ Just "boring"
--> Just 42
42 <$ Nothing
--> Nothing
"cool" <$ ["nonsense","stupid","uninteresting"]
--> ["cool","cool","cool"]
คำจำกัดความคือ:
(<$) :: a -> f b -> f a
(<$) = fmap . const
หรือเขียนโดยไม่มีจุดหมาย:
cool <$ uncool = fmap (const cool) uncool
คุณจะเห็นวิธีการconst
ใช้ที่นี่เพื่อ "ลืม" เกี่ยวกับอินพุต
การใช้งานอีกประการหนึ่งคือการใช้ฟังก์ชันสมาชิกคลาสที่มีอาร์กิวเมนต์จำลองซึ่งไม่ควรประเมิน (ใช้เพื่อแก้ไขประเภทที่ไม่ชัดเจน) ตัวอย่างที่อาจอยู่ใน Data.bits:
instance Bits Int where
isSigned = const True
bitSize = const wordSize
...
โดยใช้ const เราพูดอย่างชัดเจนว่าเรากำลังกำหนดค่าคงที่
โดยส่วนตัวแล้วฉันไม่ชอบการใช้พารามิเตอร์ดัมมี่ แต่ถ้าใช้ในคลาสนี่เป็นวิธีที่ดีในการเขียนอินสแตนซ์
const
อาจเป็นเพียงการนำไปใช้งานร่วมกับฟังก์ชันอื่น ๆ นี่คือตัวอย่างที่ฉันค้นพบ
สมมติว่าเราต้องการเขียนโครงสร้างของ 2-tuples ไปยังโครงสร้างอื่นของ 2-tuples ฉันอาจแสดงออกเช่นนี้:
((a,b),(c,d)) ⇒ (a,(c,(5,a)))
ฉันสามารถให้คำจำกัดความแบบตรงไปตรงมาด้วยการจับคู่รูปแบบ:
f ((a,b),(c,d)) = (a,(c,(5,a)))
จะเป็นอย่างไรถ้าฉันต้องการโซลูชันที่ไม่มีจุดหมาย (โดยปริยาย) สำหรับการเขียนซ้ำประเภทนี้ การคิดและการเล่นซอในภายหลังคำตอบคือเราสามารถแสดงการเขียนซ้ำ(&&&), const, (.), fst, snd
ได้ โปรดทราบว่า(&&&)
มาจากControl.Arrow
.
คำตอบของตัวอย่างโดยใช้ฟังก์ชันเหล่านี้คือ:
(fst.fst &&& (fst.snd &&& (const 5 &&& fst.fst)))
สังเกตความคล้ายคลึงกันด้วย(a,(c,(5,a)))
. จะเกิดอะไรขึ้นถ้าเราแทนที่&&&
ด้วย,
? จากนั้นจะอ่าน:
(fst.fst, (fst.snd, (const 5, fst.fst)))
สังเกตว่าa
องค์ประกอบแรกขององค์ประกอบแรกเป็นอย่างไรและนั่นคือfst.fst
โครงการใด สังเกตว่าc
องค์ประกอบแรกขององค์ประกอบที่สองเป็นอย่างไรและนั่นคือfst.snd
โครงการใด นั่นคือตัวแปรกลายเป็นเส้นทางไปยังแหล่งที่มา
const
ช่วยให้เราสามารถแนะนำค่าคงที่ น่าสนใจว่าชื่อมีความหมายอย่างไร!
จากนั้นผมก็ทั่วไปความคิดนี้กับ applicative เพื่อให้คุณสามารถเขียนฟังก์ชันใด ๆ ในลักษณะที่ไม่มีจุดหมาย (ตราบใดที่คุณมีการวิเคราะห์กรณีที่สามารถใช้ได้เป็นฟังก์ชั่นเช่นmaybe
, either
, bool
) อีกครั้งconst
มีบทบาทในการแนะนำค่าคงที่ คุณสามารถดูงานนี้ได้ในไฟล์ Data.Function.Tacitแพ็คเกจ
เมื่อคุณเริ่มต้นอย่างเป็นนามธรรมที่เป้าหมายแล้วมุ่งสู่การนำไปปฏิบัติคุณจะประหลาดใจกับคำตอบ กล่าวคือฟังก์ชันใดฟังก์ชันหนึ่งอาจมีความลึกลับเหมือนกับฟันเฟืองใด ๆ ในเครื่องจักร อย่างไรก็ตามหากคุณดึงกลับมาเพื่อนำเครื่องทั้งหมดเข้าสู่มุมมองคุณสามารถเข้าใจบริบทที่จำเป็นต้องใช้ฟันเฟืองนั้น
สมมติว่าคุณต้องการสร้างรายการที่Nothings
เท่ากับความยาวของสตริง เมื่อconst
ส่งคืนอาร์กิวเมนต์แรกไม่ว่าจะเป็นข้อที่สองคุณสามารถทำได้:
listOfNothings :: String -> [Maybe Char]
listOfNothings = (map . const) Nothing
หรือเพิ่มเติมอย่างชัดเจน:
listOfNothing st = map (const Nothing) st
สมมติว่าคุณต้องการหมุนเวียนรายการ นี่เป็นวิธีสำนวนในการทำ Haskell:
rotate :: Int -> [a] -> [a]
rotate _ [] = []
rotate n xs = zipWith const (drop n (cycle xs)) xs
ฟังก์ชันนี้จะบีบสองอาร์เรย์ด้วยฟังก์ชัน const
อันแรกเป็นอาร์เรย์แบบวัฏจักรที่ไม่มีที่สิ้นสุดส่วนที่สองเป็นอาร์เรย์ที่คุณเริ่มต้นด้วย
const
ทำหน้าที่ตรวจสอบขอบเขตและใช้อาร์เรย์เดิมเพื่อยุติอาร์เรย์แบบวนรอบ
ดูเหมือนจะไม่พบสิ่งที่เกี่ยวข้องกับฟังก์ชันนี้
สมมติว่าคุณต้องการสร้างลำดับต่อมาทั้งหมดของรายการที่กำหนด
สำหรับองค์ประกอบรายการแต่ละรายการ ณ จุดที่กำหนดคุณสามารถเลือก True (รวมไว้ในลำดับต่อมาปัจจุบัน) หรือ False (ไม่ต้องรวมไว้) สามารถทำได้โดยใช้ฟังก์ชันfilterM
แบบนี้:
λ> import Control.Monad
λ> :t filterM
filterM :: Applicative m => (a -> m Bool) -> [a] -> m [a]
λ>
ตัวอย่างเช่นเราต้องการ subsequences [1..4]
ทั้งหมดของ
λ> filterM (const [True, False]) [1..4]
[[1,2,3,4],[1,2,3],[1,2,4],[1,2],[1,3,4],[1,3],[1,4],[1],[2,3,4],[2,3],[2,4],[2],[3,4],[3],[4],[]]
λ>
backgroundColor :: Text -> Color
สำหรับฉันbackgroundColor = const White