จุด 'const' ใน Haskell Prelude คืออะไร?


95

เมื่อมองผ่าน Haskell Prelude ฉันเห็นฟังก์ชัน const :

const x _ = x

ดูเหมือนจะไม่พบสิ่งที่เกี่ยวข้องกับฟังก์ชันนี้

ประเด็นคืออะไร? ใครช่วยยกตัวอย่างได้ไหมว่าฟังก์ชันนี้จะใช้งานได้ที่ไหน


10
ตัวอย่าง: backgroundColor :: Text -> ColorสำหรับฉันbackgroundColor = const White
Zhen

คำตอบ:


86

มันมีประโยชน์สำหรับการส่งผ่านไปยังฟังก์ชันลำดับที่สูงขึ้นเมื่อคุณไม่ต้องการความยืดหยุ่นทั้งหมด ตัวอย่างเช่นตัวดำเนินการลำดับ monadic >>สามารถกำหนดในรูปของตัวดำเนินการผูก monadic เป็น

x >> y = x >>= const y

ค่อนข้างเรียบร้อยกว่าการใช้แลมด้า

x >> y = x >>= \_ -> y

และคุณยังสามารถใช้งานได้โดยไม่มีจุด

(>>) = (. const) . (>>=)

แม้ว่าฉันจะไม่แนะนำเป็นพิเศษในกรณีนี้


9
+1. นอกจากนี้ยังเกิดขึ้นบ่อยครั้งเมื่อใช้ตัวรวมตัวแยกวิเคราะห์
Fred Foo

50
อ่ามันเป็น 'ตัวสร้างฟังก์ชัน' มากกว่า - ฉันใช้มันกับอาร์กิวเมนต์เดียวและมันให้ฟังก์ชัน (รับหนึ่งอาร์กิวเมนต์) ที่จะคืนค่าคงที่ ดังนั้นmap (const 42) [1..5]ผลลัพธ์ใน[42, 42, 42, 42, 42].
stusmith

2
Stusmith: คุณเข้าใจแล้ว constมีประโยชน์สำหรับการใช้กับอาร์กิวเมนต์เดียวเพื่อให้ได้ฟังก์ชันที่จำเป็น (เช่นผ่านไปยังmap )
Conal

9
@stusmith: คุณสามารถใช้มันในรูปแบบที่น่าสนใจ: head = foldr const (error "Prelude.head: empty list")
rampion

27

เพื่อเพิ่มการตอบที่ตรง hammar ยอดเยี่ยม: ฟังก์ชั่นที่อ่อนน้อมถ่อมตนชอบconstและidมีประโยชน์จริงๆเป็นฟังก์ชั่นการสั่งซื้อที่สูงขึ้นสำหรับเหตุผลเดียวกันกับที่พวกเขามีพื้นฐานในCombinator แคลคูลัส

ไม่ใช่ว่าฉันคิดว่าฟังก์ชั่นโหมโรงของ haskell ถูกจำลองขึ้นอย่างมีสติตามระบบที่เป็นทางการหรืออะไรก็ได้ เพียงแค่การสร้างนามธรรมที่สมบูรณ์ใน haskell นั้นง่ายมากดังนั้นคุณมักจะเห็นสิ่งต่างๆทางทฤษฎีเหล่านี้กลายเป็นประโยชน์ในทางปฏิบัติ

ปลั๊กไร้ยางอาย แต่ฉันเขียนบล็อกเกี่ยวกับวิธีการที่อินสแตนซ์การใช้งาน(->)เป็นจริงSและตัวKรวมกันที่นี่ถ้าเป็นสิ่งที่คุณชอบ


8
ผู้ผสม SKI มีอิทธิพลต่อการโหมโรงอย่างแน่นอน ฉันจำได้ว่าเถียงกับ Joe Fasel ว่าควรรวม S combinator หรือไม่
สิงหาคม

4
อนึ่ง((->) e)ยังเป็นผู้อ่าน monad - ด้วยReaderและเหมือนเป็นเพียงการnewtypeห่อหุ้ม - และaskฟังก์ชั่นก็เป็นidเช่นนั้นนั่นคือตัวIรวมกันเช่นกัน ถ้าคุณดูที่พื้นฐานแทน BCKW Haskell แกงเดิมB, KและWมีfmap, returnและjoinตามลำดับ
CA McCann

1
ลิงค์บล็อกในคำตอบตาย ตอนนี้ควรชี้ที่นี่: brandon.si/code/…
nsxt

24

ดูเหมือนจะไม่พบสิ่งที่เกี่ยวข้องกับฟังก์ชันนี้

คำตอบอื่น ๆ อีกมากมายกล่าวถึงแอปพลิเคชันที่ค่อนข้างลึกลับ (อย่างน้อยสำหรับผู้มาใหม่) constการใช้งานของ นี่คือวิธีง่ายๆ: คุณสามารถใช้constเพื่อกำจัดแลมด้าที่ใช้อาร์กิวเมนต์สองตัวโยนอันแรกออกไป แต่ทำสิ่งที่น่าสนใจกับอันที่สอง

ตัวอย่างต่อไปนี้ (ไม่มีประสิทธิภาพ แต่ให้คำแนะนำ) การดำเนินการlength,

length' = foldr (\_ acc -> 1 + acc) 0

สามารถเขียนใหม่เป็นไฟล์

length' = foldr (const (1+)) 0

ซึ่งอาจจะหรูหรากว่า

การแสดงออกconst (1+)เป็นจริงความหมายเทียบเท่ากับ\_ acc -> 1 + accเพราะมันจะใช้เวลาหนึ่งอาร์กิวเมนต์โยนมันออกไปและส่งกลับส่วน (1+)


5
ฉันใช้เวลา 5 นาทีในการทำความเข้าใจว่ามันทำงานอย่างไร :)
Mukesh Soni

22

ตัวอย่างง่ายๆสำหรับการใช้เป็น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ใช้ที่นี่เพื่อ "ลืม" เกี่ยวกับอินพุต


15

การใช้งานอีกประการหนึ่งคือการใช้ฟังก์ชันสมาชิกคลาสที่มีอาร์กิวเมนต์จำลองซึ่งไม่ควรประเมิน (ใช้เพื่อแก้ไขประเภทที่ไม่ชัดเจน) ตัวอย่างที่อาจอยู่ใน Data.bits:

instance Bits Int where
  isSigned = const True
  bitSize  = const wordSize
  ...

โดยใช้ const เราพูดอย่างชัดเจนว่าเรากำลังกำหนดค่าคงที่

โดยส่วนตัวแล้วฉันไม่ชอบการใช้พารามิเตอร์ดัมมี่ แต่ถ้าใช้ในคลาสนี่เป็นวิธีที่ดีในการเขียนอินสแตนซ์


อาร์กิวเมนต์พร็อกซีนั้นดีกว่ามากและเมื่อกำหนดเป้าหมาย GHC ล่าสุดแอปพลิเคชันประเภทจะทำเคล็ดลับอย่างเรียบร้อย
dfeuer

2

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แพ็คเกจ

เมื่อคุณเริ่มต้นอย่างเป็นนามธรรมที่เป้าหมายแล้วมุ่งสู่การนำไปปฏิบัติคุณจะประหลาดใจกับคำตอบ กล่าวคือฟังก์ชันใดฟังก์ชันหนึ่งอาจมีความลึกลับเหมือนกับฟันเฟืองใด ๆ ในเครื่องจักร อย่างไรก็ตามหากคุณดึงกลับมาเพื่อนำเครื่องทั้งหมดเข้าสู่มุมมองคุณสามารถเข้าใจบริบทที่จำเป็นต้องใช้ฟันเฟืองนั้น


2

สมมติว่าคุณต้องการสร้างรายการที่Nothingsเท่ากับความยาวของสตริง เมื่อconstส่งคืนอาร์กิวเมนต์แรกไม่ว่าจะเป็นข้อที่สองคุณสามารถทำได้:

listOfNothings :: String -> [Maybe Char]
listOfNothings = (map . const) Nothing

หรือเพิ่มเติมอย่างชัดเจน:

listOfNothing st = map (const Nothing) st

0

สมมติว่าคุณต้องการหมุนเวียนรายการ นี่เป็นวิธีสำนวนในการทำ Haskell:

rotate :: Int -> [a] -> [a] rotate _ [] = [] rotate n xs = zipWith const (drop n (cycle xs)) xs

ฟังก์ชันนี้จะบีบสองอาร์เรย์ด้วยฟังก์ชัน constอันแรกเป็นอาร์เรย์แบบวัฏจักรที่ไม่มีที่สิ้นสุดส่วนที่สองเป็นอาร์เรย์ที่คุณเริ่มต้นด้วย

const ทำหน้าที่ตรวจสอบขอบเขตและใช้อาร์เรย์เดิมเพื่อยุติอาร์เรย์แบบวนรอบ

ดู: หมุนรายการใน Haskell


0

ดูเหมือนจะไม่พบสิ่งที่เกี่ยวข้องกับฟังก์ชันนี้

สมมติว่าคุณต้องการสร้างลำดับต่อมาทั้งหมดของรายการที่กำหนด

สำหรับองค์ประกอบรายการแต่ละรายการ ณ จุดที่กำหนดคุณสามารถเลือก 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],[]]
 λ> 
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.