سؤال

ما هو الفرق بين النقطة (.) وعلامة الدولار ($)?

كما أفهمها، فهي كلاهما من السكر النحوي لعدم الحاجة إلى استخدام الأقواس.

هل كانت مفيدة؟

المحلول

ال $ المشغل هو لتجنب الأقواس. أي شيء يظهر بعد أن تأخذ الأسبقية على أي شيء يأتي من قبل.

على سبيل المثال، دعنا نقول أنك حصلت على خط يقرأ:

putStrLn (show (1 + 1))

إذا كنت ترغب في التخلص من هؤلاء الأقواس، فإن أي من الأسطر التالية سيقوم أيضا بنفس الشيء:

putStrLn (show $ 1 + 1)
putStrLn $ show (1 + 1)
putStrLn $ show $ 1 + 1

الغرض الأساسي من . المشغل ليس لتجنب الأقواس، ولكن لوظائف السلسلة. يتيح لك ربط إخراج كل ما يظهر على اليمين في إدخال كل ما يظهر على اليسار. هذا عادة ما يؤدي أيضا إلى عدد أقل من قوسين، ولكن يعمل بشكل مختلف.

العودة إلى نفس المثال:

putStrLn (show (1 + 1))
  1. (1 + 1) ليس لديه مدخلات، وبالتالي لا يمكن استخدامها مع . المشغل أو العامل.
  2. show يمكن أن تأخذ Int والعودة أ String.
  3. putStrLn يمكن أن تأخذ أ String والعودة IO ().

يمكنك السلسلة show ل putStrLn مثله:

(putStrLn . show) (1 + 1)

إذا كان هذا هو الكثير من الأقواس التي ترقبها، تخلص منهم $ المشغل أو العامل:

putStrLn . show $ 1 + 1

نصائح أخرى

لديهم أنواع مختلفة وتعريفات مختلفة:

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، كل ما ولكن يمكن استبدال النهائي النهائي .

لاحظ أيضا ذلك ($) يكون وظيفة الهوية المتخصصة لأنواع العمل. وبعد تبدو وظيفة الهوية مثل هذا:

id :: a -> a
id x = x

في حين ($) يشبه هذا:

($) :: (a -> b) -> (a -> b)
($) = id

لاحظ أنني قمت بإضافة قوسين إضافية عمدا في توقيع النوع.

يستخدم ل ($) عادة ما يتم القضاء عليها عن طريق إضافة الأقواس (ما لم يتم استخدام المشغل في قسم). على سبيل المثال: f $ g x يصبح f (g x).

يستخدم ل (.) غالبا ما يكون أصعب قليلا استبدال؛ وعادة ما يحتاجون إلى Lambda أو إدخال معلمة لوظيفة صريحة. علي سبيل المثال:

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"

إذا كنا نستخدم فقط المرتبة الثالثة مرة واحدة، فيمكننا تجنب تسمية ذلك باستخدام LambDA:

Prelude> map (\x -> head $ tail $ tail x) ["asdf", "qwer", "1234"]
"de3"

أخيرا، يتيح لك التركيب تجنب Lambda:

Prelude> map (head . tail . tail) ["asdf", "qwer", "1234"]
"de3"

النسخة القصيرة والحلوة:

  • ($) يدعو الوظيفة التي هي حجة اليد اليسرى على القيمة التي هي حجة اليد اليمنى.
  • (.) يلحق الوظيفة التي هي حجة اليد اليسرى على الوظيفة وهي حجةها اليمنى.

تطبيق واحد مفيد وأخذني بعض الوقت لمعرفة من وصف قصير جدا في تعلم لك haskell: حيث:

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) .... وبعد من الواضح أن المتغيرات الأخيرة ستكون أكثر قابلية للقراءة لمعظم الناس.

... أو يمكنك تجنب . و $ الإنشاءات باستخدام خطوط الأنابيب:

third xs = xs |> tail |> tail |> head

وذلك بعد أن أضفت في وظيفة المساعد:

(|>) x y = y x

طريقة رائعة لمعرفة المزيد عن أي شيء (أي وظيفة) هي أن تتذكر أن كل شيء وظيفة! يساعد المعطور العام، ولكن في حالات محددة مثل المشغلين، فإنه يساعد في تذكر هذه الخدعة الصغيرة:

:t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c

و

:t ($)
($) :: (a -> b) -> a -> b

فقط تذكر أن تستخدم :t بحرية، ولف المشغلين الخاص بك في ()!

حكمي بسيط (أنا مبتدئ جدا):

  • لا تستخدم . إذا كنت ترغب في تمرير المعلمة (استدعاء الوظيفة)، و
  • لا تستخدم $ إذا لم يكن هناك معلمة حتى الآن (إنشاء وظيفة)

إنه

show $ head [1, 2]

لكن ابدا:

show . head [1, 2]

haskell: الفرق بين . (نقطة) و $ (علامة الدولار)

ما هو الفرق بين النقطة (.) وعلامة الدولار ($)؟ كما أفهمها، فهي كلاهما من السكر النحوي لعدم الحاجة إلى استخدام الأقواس.

هم انهم ليس السكر النحوي لعدم الحاجة إلى استخدام الأقواس - فهي وظائف، - infixed، وبالتالي قد نسميها المشغلين لهم.

مؤلف موسيقى، (.), ، وعند استخدامه.

(.) هو وظيفة تأليف. وبالتالي

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

عندما لا نفعل.

أعتقد مثالا قصيرا على المكان الذي ستستخدمه . و لا $ سوف تساعد في توضيح الأشياء.

double x = x * 2
triple x = x * 3
times6 = double . triple

:i times6
times6 :: Num c => c -> c

لاحظ أن times6 هي وظيفة تم إنشاؤها من تكوين الوظائف.

جميع الإجابات الأخرى جيدة جدا. ولكن هناك تفاصيل صالحة للاستخدام المهمة حول كيفية تعايم GHC $، أن مدقق نوع GHC يسمح Instatiarion مع أنواع مرتبة أعلى / كمية. إذا نظرت إلى نوع $ id على سبيل المثال، ستجد أنها ستأخذ دالة حجتها هي نفسها وظيفة متعددة الجنسية. أشياء صغيرة مثل تلك ليست نفس المرونة مع مشغل اضطراب مكافئ. (هذا فعلا يجعلني أتساءل عما إذا كان $! يستحق نفس العلاج أم لا)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top