อะไรคือความแตกต่างระหว่างจุด(.)และเครื่องหมายดอลลาร์($)?
ดังที่ฉันเข้าใจแล้วพวกเขาทั้งคู่เป็นน้ำตาลประโยคโดยไม่จำเป็นต้องใช้วงเล็บ
อะไรคือความแตกต่างระหว่างจุด(.)และเครื่องหมายดอลลาร์($)?
ดังที่ฉันเข้าใจแล้วพวกเขาทั้งคู่เป็นน้ำตาลประโยคโดยไม่จำเป็นต้องใช้วงเล็บ
คำตอบ:
$ผู้ประกอบการคือการหลีกเลี่ยงวงเล็บ สิ่งใดที่ปรากฏขึ้นหลังจากนั้นจะมีความสำคัญมากกว่าสิ่งใด ๆ
ตัวอย่างเช่นสมมติว่าคุณมีบรรทัดที่อ่านว่า:
putStrLn (show (1 + 1))
หากคุณต้องการกำจัดวงเล็บเหล่านั้นบรรทัดใด ๆ ต่อไปนี้จะทำสิ่งเดียวกัน:
putStrLn (show $ 1 + 1)
putStrLn $ show (1 + 1)
putStrLn $ show $ 1 + 1
วัตถุประสงค์หลักของ.ผู้ปฏิบัติงานคือเพื่อหลีกเลี่ยงวงเล็บ แต่เป็นการเชื่อมโยงฟังก์ชัน มันช่วยให้คุณผูกเอาท์พุทของสิ่งใดก็ตามที่ปรากฏทางด้านขวากับอินพุตของสิ่งที่ปรากฏทางด้านซ้าย ซึ่งมักส่งผลให้มีวงเล็บน้อยลง แต่ทำงานต่างกัน
กลับไปที่ตัวอย่างเดิม:
putStrLn (show (1 + 1))
(1 + 1)ไม่มีอินพุตและดังนั้นจึงไม่สามารถใช้กับ.ผู้ให้บริการได้showสามารถใช้และส่งกลับIntStringputStrLnสามารถใช้และกลับStringIO ()คุณสามารถเชื่อมโยงshowกับputStrLnสิ่งนี้:
(putStrLn . show) (1 + 1)
หากวงเล็บนั้นมากเกินไปสำหรับความชอบของคุณให้กำจัดพวกมันด้วย$โอเปอเรเตอร์:
putStrLn . show $ 1 + 1
putStrLn . show . (+1) $ 1จะเทียบเท่า คุณถูกต้องในตัวดำเนินการมัด (ทั้งหมด?) ส่วนใหญ่นั้นเป็นฟังก์ชั่น
map ($3)ผมสงสัยว่าทำไมไม่มีใครเคยกล่าวถึงการใช้งานเช่น ฉันหมายถึงฉันส่วนใหญ่ใช้$เพื่อหลีกเลี่ยงวงเล็บเช่นกัน แต่มันก็ไม่เหมือนที่พวกเขามีสำหรับ
map ($3)Num a => [(a->b)] -> [b]เป็นหน้าที่ของประเภท มันใช้รายการของฟังก์ชั่นรับตัวเลข, ใช้ 3 กับพวกเขาทั้งหมดและรวบรวมผลลัพธ์
พวกเขามีประเภทที่แตกต่างกันและคำจำกัดความที่แตกต่างกัน:
infixr 9 .
(.) :: (b -> c) -> (a -> b) -> (a -> c)
(f . g) x = f (g x)
infixr 0 $
($) :: (a -> b) -> a -> b
f $ x = f x
($)มีวัตถุประสงค์เพื่อแทนที่แอปพลิเคชันฟังก์ชั่นปกติ แต่มีความสำคัญที่แตกต่างกันเพื่อช่วยหลีกเลี่ยงวงเล็บ (.)มีไว้สำหรับการรวมฟังก์ชันสองฟังก์ชันเข้าด้วยกันเพื่อสร้างฟังก์ชันใหม่
ในบางกรณีพวกเขาสามารถใช้แทนกันได้ แต่นี่ไม่เป็นความจริงโดยทั่วไป ตัวอย่างทั่วไปที่พวกเขาอยู่คือ:
f $ g $ h $ x
==>
f . g . h $ x
ในคำอื่น ๆ ในห่วงโซ่ของ$s ทั้งหมด แต่สุดท้ายจะถูกแทนที่ด้วย.
xเป็นฟังก์ชั่นล่ะ? คุณสามารถใช้.เป็นตัวสุดท้ายได้หรือไม่?
xในบริบทนี้แล้วใช่ - แต่แล้ว "สุดท้าย" xใครจะได้รับการนำไปใช้อย่างอื่นที่ไม่ใช่ หากคุณไม่ได้สมัครxก็ไม่ต่างกับxคุณค่า
นอกจากนี้ยังทราบว่า($)เป็นฟังก์ชั่นเอกลักษณ์เฉพาะประเภทฟังก์ชั่น ฟังก์ชันตัวตนมีลักษณะดังนี้:
id :: a -> a
id x = x
ในขณะที่($)มีลักษณะเช่นนี้:
($) :: (a -> b) -> (a -> b)
($) = id
โปรดทราบว่าฉันตั้งใจเพิ่มวงเล็บพิเศษในลายเซ็นประเภท
การใช้($)สามารถกำจัดได้โดยเพิ่มวงเล็บ (เว้นแต่ผู้ใช้จะใช้ในส่วน) เช่นจะกลายเป็นf $ g xf (g x)
การใช้(.)มักจะยากที่จะเปลี่ยนเล็กน้อย พวกเขามักจะต้องการแลมบ์ดาหรือแนะนำพารามิเตอร์ฟังก์ชันที่ชัดเจน ตัวอย่างเช่น:
f = g . h
กลายเป็น
f x = (g . h) x
กลายเป็น
f x = g (h x)
หวังว่านี่จะช่วยได้!
($) อนุญาตให้ฟังก์ชั่นถูกล่ามโซ่ไว้ด้วยกันโดยไม่ต้องเพิ่มวงเล็บเพื่อควบคุมลำดับการประเมิน:
Prelude> head (tail "asdf")
's'
Prelude> head $ tail "asdf"
's'
ผู้ประกอบการเขียน(.)สร้างฟังก์ชั่นใหม่โดยไม่ต้องระบุข้อโต้แย้ง:
Prelude> let second x = head $ tail x
Prelude> second "asdf"
's'
Prelude> let second = head . tail
Prelude> second "asdf"
's'
ตัวอย่างด้านบนเป็นตัวอย่างเนื้อหา แต่ไม่ได้แสดงความสะดวกสบายในการใช้องค์ประกอบ นี่คือการเปรียบเทียบอื่น:
Prelude> let third x = head $ tail $ tail x
Prelude> map third ["asdf", "qwer", "1234"]
"de3"
หากเราใช้ครั้งที่สามเพียงครั้งเดียวเราสามารถหลีกเลี่ยงการตั้งชื่อโดยใช้แลมบ์ดา:
Prelude> map (\x -> head $ tail $ tail x) ["asdf", "qwer", "1234"]
"de3"
ในที่สุดองค์ประกอบก็ช่วยให้เราหลีกเลี่ยงแลมบ์ดาได้:
Prelude> map (head . tail . tail) ["asdf", "qwer", "1234"]
"de3"
เวอร์ชั่นสั้นและหวาน:
($) เรียกใช้ฟังก์ชันซึ่งเป็นอาร์กิวเมนต์ซ้ายมือของค่าซึ่งเป็นอาร์กิวเมนต์ขวา(.) ประกอบด้วยฟังก์ชันซึ่งเป็นอาร์กิวเมนต์ซ้ายมือบนฟังก์ชันซึ่งเป็นอาร์กิวเมนต์ขวาแอปพลิเคชันหนึ่งที่มีประโยชน์และใช้เวลาพอสมควรในการหาคำอธิบายสั้น ๆจากการเรียนรู้เกี่ยวกับฮาสเคล : ตั้งแต่:
f $ x = f x
และวงเล็บด้านขวามือของนิพจน์ที่มีโอเปอเรเตอร์ infix แปลงเป็นฟังก์ชันคำนำหน้าเราสามารถเขียน($ 3) (4+)คล้ายกัน(++", world") "hello"ได้
ทำไมทุกคนจะทำเช่นนี้? สำหรับรายการฟังก์ชั่นเช่น ทั้งสอง:
map (++", world") ["hello","goodbye"]`
และ:
map ($ 3) [(4+),(3*)]
จะสั้นกว่าหรือmap (\x -> x ++ ", world") ... map (\f -> f 3) ...เห็นได้ชัดว่ารุ่นหลังจะสามารถอ่านได้มากขึ้นสำหรับคนส่วนใหญ่
$3โดยไม่มีที่ว่าง หากเปิดใช้งานเทมเพลตแฮสเคลล์สิ่งนี้จะถูกแยกวิเคราะห์เป็นรอยต่อในขณะที่$ 3หมายถึงสิ่งที่คุณพูดเสมอ โดยทั่วไปดูเหมือนว่าจะมีแนวโน้มใน Haskell ที่จะ "ขโมย" บิตของไวยากรณ์โดยยืนยันว่าผู้ประกอบการบางรายมีช่องว่างรอบตัวพวกเขาที่จะได้รับการปฏิบัติเช่นนี้
Haskell: ความแตกต่างระหว่าง
.(dot) และ$(เครื่องหมายดอลลาร์)อะไรคือความแตกต่างระหว่างจุด
(.)และเครื่องหมายดอลลาร์($)? ดังที่ฉันเข้าใจแล้วพวกเขาทั้งคู่เป็นน้ำตาลประโยคโดยไม่จำเป็นต้องใช้วงเล็บ
พวกเขาไม่ได้เป็นน้ำตาล syntactic เพราะไม่จำเป็นต้องใช้วงเล็บ - พวกเขามีฟังก์ชั่น - ผสมดังนั้นเราจึงอาจเรียกพวกเขาว่าผู้ประกอบการ
(.)และใช้เมื่อใด(.)เป็นฟังก์ชั่นการเขียน ดังนั้น
result = (f . g) x
เป็นเช่นเดียวกับการสร้างฟังก์ชั่นที่ส่งผ่านผลของการโต้แย้งของตนผ่านไปเพื่อgf
h = \x -> f (g x)
result = h x
ใช้(.)เมื่อคุณไม่มีข้อโต้แย้งเพื่อส่งผ่านไปยังฟังก์ชันที่คุณต้องการเขียน
($)และเมื่อใดที่จะใช้($)เป็นฟังก์ชั่นการใช้งานที่สัมพันธ์กันทางขวาโดยมีลำดับความสำคัญต่ำ ดังนั้นมันจึงคำนวณสิ่งต่าง ๆ ทางด้านขวาของมันก่อน ดังนั้น,
result = f $ g x
เหมือนกับขั้นตอนนี้ (ซึ่งมีความสำคัญเนื่องจาก Haskell ประเมินอย่างขี้เกียจมันจะเริ่มประเมินfก่อน):
h = f
g_x = g x
result = h g_x
หรือสั้นกระชับ:
result = f (g x)
ใช้($)เมื่อคุณมีตัวแปรทั้งหมดเพื่อประเมินผลก่อนที่คุณจะใช้ฟังก์ชันก่อนหน้านี้กับผลลัพธ์
เราสามารถเห็นสิ่งนี้ได้โดยการอ่านซอร์สสำหรับแต่ละฟังก์ชั่น
นี่คือแหล่งที่มาของ(.):
-- | Function composition.
{-# INLINE (.) #-}
-- Make sure it has TWO args only on the left, so that it inlines
-- when applied to two functions, even if there is no final argument
(.) :: (b -> c) -> (a -> b) -> a -> c
(.) f g = \x -> f (g x)
และนี่คือที่มาสำหรับ($):
-- | Application operator. This operator is redundant, since ordinary
-- application @(f x)@ means the same as @(f '$' x)@. However, '$' has
-- low, right-associative binding precedence, so it sometimes allows
-- parentheses to be omitted; for example:
--
-- > f $ g $ h x = f (g (h x))
--
-- It is also useful in higher-order situations, such as @'map' ('$' 0) xs@,
-- or @'Data.List.zipWith' ('$') fs xs@.
{-# INLINE ($) #-}
($) :: (a -> b) -> a -> b
f $ x = f x
ใช้การจัดวางองค์ประกอบเมื่อคุณไม่จำเป็นต้องประเมินฟังก์ชันทันที บางทีคุณอาจต้องการส่งผ่านฟังก์ชั่นที่เป็นผลมาจากการแต่งเพลงไปยังฟังก์ชั่นอื่น
ใช้แอปพลิเคชันเมื่อคุณระบุอาร์กิวเมนต์ทั้งหมดเพื่อประเมินผลเต็มรูปแบบ
สำหรับตัวอย่างของเรามันน่าจะเหมาะสมกว่าที่จะทำ
f $ g x
เมื่อเรามีx(หรือมากกว่าgอาร์กิวเมนต์ของ) และทำ:
f . g
เมื่อเราทำไม่ได้
... หรือคุณสามารถหลีกเลี่ยง.และ$สร้างโดยใช้pipelining :
third xs = xs |> tail |> tail |> head
หลังจากที่คุณเพิ่มเข้าไปในฟังก์ชั่นตัวช่วย:
(|>) x y = y x
$ตัวดำเนินการของ Haskell ใช้งานได้เหมือน F # <|มากกว่า|>ปกติโดยทั่วไปใน Haskell คุณจะต้องเขียนฟังก์ชันข้างต้นดังนี้: third xs = head $ tail $ tail $ xsหรือบางทีอย่างthird = head . tail . tailนั้นซึ่งในไวยากรณ์ F # -style จะเป็นดังนี้:let third = List.head << List.tail << List.tail
วิธีที่ยอดเยี่ยมในการเรียนรู้เพิ่มเติมเกี่ยวกับสิ่งใด (ฟังก์ชั่นใด ๆ ) คือการจำไว้ว่าทุกสิ่งเป็นฟังก์ชั่น! มนต์ทั่วไปนั้นช่วยได้ แต่ในบางกรณีเช่นตัวดำเนินการมันช่วยให้จำเคล็ดลับเล็กน้อยนี้ได้:
:t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
และ
:t ($)
($) :: (a -> b) -> a -> b
เพียงจำไว้ว่าให้ใช้:tอย่างอิสระและห่อผู้ให้บริการของคุณไว้()!
กฎของฉันก็เรียบง่าย (ฉันก็เริ่มด้วย):
.หากคุณต้องการส่งผ่านพารามิเตอร์ (เรียกใช้ฟังก์ชัน) และ$ถ้าไม่มีพารามิเตอร์ (เขียนฟังก์ชั่น)นั่นคือ
show $ head [1, 2]
แต่ไม่เคย:
show . head [1, 2]
ฉันคิดว่าเป็นตัวอย่างสั้น ๆ ว่าคุณจะใช้ที่ใด.และ$จะไม่ช่วยชี้แจงสิ่งต่าง ๆ
double x = x * 2
triple x = x * 3
times6 = double . triple
:i times6
times6 :: Num c => c -> c
โปรดทราบว่าtimes6เป็นฟังก์ชั่นที่สร้างขึ้นจากองค์ประกอบของฟังก์ชั่น
คำตอบอื่น ๆ ทั้งหมดค่อนข้างดี แต่มีรายละเอียดการใช้งานที่สำคัญเกี่ยวกับวิธีที่ ghc ปฏิบัติต่อ $ ว่าตัวตรวจสอบประเภท ghc อนุญาตให้ติดตั้ง instatiarion ที่มีประเภทอันดับ / ปริมาณที่สูงขึ้น ถ้าคุณดูที่ประเภทของ $ idตัวอย่างคุณจะพบว่ามันจะใช้ฟังก์ชั่นที่มีอาร์กิวเมนต์เป็นฟังก์ชัน polymorphic สิ่งเล็ก ๆ น้อย ๆ เช่นนั้นไม่ได้รับความยืดหยุ่นเท่ากันกับผู้ปฏิบัติงานอารมณ์เสียที่เทียบเท่า (อันนี้ทำให้ฉันสงสัยว่า $! สมควรได้รับการรักษาแบบเดียวกันหรือไม่)