อะไรคือความแตกต่างระหว่างจุด(.)
และเครื่องหมายดอลลาร์($)
?
ดังที่ฉันเข้าใจแล้วพวกเขาทั้งคู่เป็นน้ำตาลประโยคโดยไม่จำเป็นต้องใช้วงเล็บ
อะไรคือความแตกต่างระหว่างจุด(.)
และเครื่องหมายดอลลาร์($)
?
ดังที่ฉันเข้าใจแล้วพวกเขาทั้งคู่เป็นน้ำตาลประโยคโดยไม่จำเป็นต้องใช้วงเล็บ
คำตอบ:
$
ผู้ประกอบการคือการหลีกเลี่ยงวงเล็บ สิ่งใดที่ปรากฏขึ้นหลังจากนั้นจะมีความสำคัญมากกว่าสิ่งใด ๆ
ตัวอย่างเช่นสมมติว่าคุณมีบรรทัดที่อ่านว่า:
putStrLn (show (1 + 1))
หากคุณต้องการกำจัดวงเล็บเหล่านั้นบรรทัดใด ๆ ต่อไปนี้จะทำสิ่งเดียวกัน:
putStrLn (show $ 1 + 1)
putStrLn $ show (1 + 1)
putStrLn $ show $ 1 + 1
วัตถุประสงค์หลักของ.
ผู้ปฏิบัติงานคือเพื่อหลีกเลี่ยงวงเล็บ แต่เป็นการเชื่อมโยงฟังก์ชัน มันช่วยให้คุณผูกเอาท์พุทของสิ่งใดก็ตามที่ปรากฏทางด้านขวากับอินพุตของสิ่งที่ปรากฏทางด้านซ้าย ซึ่งมักส่งผลให้มีวงเล็บน้อยลง แต่ทำงานต่างกัน
กลับไปที่ตัวอย่างเดิม:
putStrLn (show (1 + 1))
(1 + 1)
ไม่มีอินพุตและดังนั้นจึงไม่สามารถใช้กับ.
ผู้ให้บริการได้show
สามารถใช้และส่งกลับInt
String
putStrLn
สามารถใช้และกลับString
IO ()
คุณสามารถเชื่อมโยง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 x
f (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
เป็นเช่นเดียวกับการสร้างฟังก์ชั่นที่ส่งผ่านผลของการโต้แย้งของตนผ่านไปเพื่อg
f
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 สิ่งเล็ก ๆ น้อย ๆ เช่นนั้นไม่ได้รับความยืดหยุ่นเท่ากันกับผู้ปฏิบัติงานอารมณ์เสียที่เทียบเท่า (อันนี้ทำให้ฉันสงสัยว่า $! สมควรได้รับการรักษาแบบเดียวกันหรือไม่)