سؤال

في العالم الحقيقي هاسكل, ، يصفون المجمعات مثل هذا:

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

ثم بعد ذلك يذكرون ذلك maybeIO الوظيفة عبارة عن أداة مجمعة ويبدو توقيع نوعها كما يلي:

maybeIO :: IO a -> IO (Maybe a)

لكن كل ما أستطيع رؤيته هو ذلك maybeIO هي دالة تأخذ قيمة ملفوفة في IO monad وترجع قيمة في IO monad.إذن كيف تصبح هذه الوظيفة مجمعة؟

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

المحلول

هناك بالفعل شيئين يمكن أن نعنيهما عندما نقول combinator.الكلمة مثقلة بعض الشيء.

  1. نعني عادةً الوظيفة التي "تجمع" الأشياء.على سبيل المثال، تأخذ وظيفتك ملفًا IO القيمة ويبني قيمة أكثر تعقيدًا.باستخدام هذه "المجمعات" يمكننا الجمع وإنشاء مجمع جديد IO القيم من عدد قليل نسبيا من الوظائف البدائية لإنشاء IO قيم.

    على سبيل المثال، بدلًا من إنشاء دالة تقرأ 10 ملفات، نستخدمها mapM_ readFile.أدوات التجميع هنا هي وظائف نستخدمها لدمج القيم وبناءها

  2. المصطلح الأكثر صرامة في علوم الكمبيوتر هو "دالة بدون متغيرات حرة".لذا

     -- The primitive combinators from a famous calculus, SKI calculus.
     id a         = a -- Not technically primitive, genApp const const
     const a b    = a
     genApp x y z = x z (y z)
    

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

تلدر:عادةً عندما نقول أداة الدمج، فإننا نشير إلى فكرة أكثر عمومية تسمى "نمط الدمج" حيث يكون لدينا عدد قليل من الوظائف البدائية والكثير من الوظائف المعرفة من قبل المستخدم لبناء قيم أكثر تعقيدًا.

نصائح أخرى

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

على سبيل المثال، إذا كنت تريد حساب الجذر التربيعي لرقم مقربًا إلى أقرب عدد صحيح، فيمكنك كتابة هذه الدالة على النحو التالي:

approxSqrt x = round (sqrt x)

قد تدرك أيضًا أن ما نفعله هنا حقًا هو أخذ وظيفتين وبناء وظيفة أكثر تعقيدًا باستخدامهما كعناصر بناء.ومع ذلك، نحتاج إلى نوع من الغراء لوضعهما معًا، وهذا الغراء موجود (.):

approxSqrt = round . sqrt

لذا فإن عامل تكوين الوظيفة هو عبارة عن أداة دمج للوظائف - فهو يجمع بين الوظائف لإنشاء وظائف جديدة.مثال آخر هو أنك ربما تريد قراءة كل سطر من الملف في القائمة.يمكنك القيام بذلك بالطريقة الواضحة:

do
  contents <- readFile "my_file.txt"
  let messages = lines contents
  ...

لكن!ماذا سنفعل إذا كانت لدينا وظيفة تقرأ الملف وترجع محتوياته كسلاسل؟ثم يمكننا أن نفعل

do
  messages <- readFileLines "my_file.txt"
  ...

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

readFileLines = fmap lines . readFile

هنا نستخدم اثنين من المجمعات!نحن نستخدم ال (.) من قبل، و fmap هو في الواقع أداة دمج مفيدة جدًا أيضًا.نقول أنه "يرفع" عملية حسابية خالصة إلى IO monad، وما نعنيه حقًا هو ذلك lines لديه نوع التوقيع

lines :: String -> [String]

لكن fmap lines لديه التوقيع

fmap lines :: IO String -> IO [String]

لذا fmap يكون مفيدًا عندما تريد الجمع بين الحسابات النقية وحسابات الإدخال والإخراج.


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

لم يتم تعريف "Combinator" بدقة عند استخدامه في Haskell.من الأصح استخدامه للإشارة إلى الوظائف التي تأخذ وظائف أخرى كوسيطات حساب التفاضل والتكامل الموحد ولكن في مصطلحات هاسكل غالبًا ما يتم تحميلها بشكل زائد لتعني أيضًا وظيفة "تعديل" أو "مجموعة"، خاصة وظيفة Functor أو Monad.في هذه الحالة، قد تقول أن أداة الدمج هي دالة "تتخذ بعض الإجراءات أو القيمة في السياق وترجع إجراءً أو قيمة جديدة معدلة في السياق".

المثال الخاص بك، maybeIO غالبا ما يسمى optional

optional :: Alternative f => f a -> f (Maybe a)
optional fa = (Just <$> fa) <|> pure Nothing

ولها طبيعة شبيهة بالدمج لأنها تتطلب الحساب f a ويعدلها بشكل عام لتعكس الفشل في قيمتها.

السبب وراء تسمية هذه المجمعات أيضًا يتعلق بكيفية استخدامها.مكان نموذجي لنرى optional (وحقيقة، Alternative بشكل عام) موجود في مكتبات المجمع اللغوي.هنا، نحن نميل إلى بناء المحللين الأساسيين باستخدام Simple Parserمثل

satisfy :: (Char -> Bool) -> Parser Char
anyChar    = satisfy (const True)
whitespace = satisfy isSpace
number     = satisfy isNumeric

ومن ثم "تعديل" سلوكهم باستخدام "المجمعات"

-- the many and some combinators
many :: Alternative f => f a -> f [a] -- zero or more successes
some :: Alternative f => f a -> f [a] -- one  or more successes

many f = some f <|> pure []
some f = (:) <$> f <*> many f

-- the void combinator forgets what's inside the functor
void :: Functor f => f a -> f ()
void f = const () <$> f

-- from the external point of view, this is another "basic" Parser
-- ... but we know it's actually built from an even more basic one
-- and the judicious application of a few "combinators"
blankSpace = Parser ()
blankSpace = void (many whitespace)

word :: Parser String
word = many (satisfy $ not . isSpace)

في كثير من الأحيان نطلق أيضًا على الوظائف التي تجمع بين وظائف متعددة/Functors/Monads "المجمعات" أيضًا، وهو ما ربما يكون له معنى ذاكري

-- the combine combinator
combine :: Applicative f => f a -> f b -> f (a, b)
combine fa fb = (,) <$> fa <*> fb

-- the ignore-what's-next combinator
(<*) :: Applicative f => f a -> f b -> f a
fa <* fb = const <$> fa <*> fb

-- the do-me-then-forget-me combinator
(*>) :: Applicative f => f a -> f b -> f b
fa *> fb = flip const <$> fa <*> fb

line = Parser String
line = many (satisfy $ \c -> c /= '\n') <* satisfy (=='\n')

لكن في نهاية المطاف، تتعلق أدوات التجميع بقصد واجهة برمجة التطبيقات (API) واستخدامها أكثر من دلالتها الصارمة.ستشاهد بشكل متكرر مكتبات تم إنشاؤها من "أجزاء أساسية" مثل الوظائف أو satisfy والتي يتم بعد ذلك تعديلها ودمجها مع مجموعة من "المجمعات".ال Parser المثال أعلاه هو مثال جوهري، ولكن هذا نمط هاسكل شائع جدًا.

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