الاختيار بين البدائل في Datatype Haskell Algebraic
-
13-09-2019 - |
سؤال
عند النوع X
يعرف ب:
data X =
X { sVal :: String } |
I { iVal :: Int } |
B { bVal :: Bool }
وأريد Int
داخل X
القيمة، إذا كان هناك واحد، خلاف ذلك صفر.
returnInt :: X -> Int
كيف يمكنني تحديد نوع X
حجة ل returnInt
هو؟
المحلول
فقط لتوضيح نقطة هنا، اسمحوا لي بإعادة كتابة نوع البيانات الخاص بك لتجنب الغموض في معنى X:
data SomeType = X { myString :: String} | I {myInt :: Int} | B {myBool :: Bool}
في هذا التعريف، لا توجد أنواع X، I و B. س، أنا و ب منشئون يخلقون قيمة من النوع Sometype
وبعد لاحظ ما يحدث عندما تسأل GHCI ما هو نوع أي قيمة مصنوعة من منشئ النوع هذه:
*Main> :t (I 5)
(I 5) :: Sometype
*Main> :t (B False)
(B False) :: Sometype
انهم ينتمون الى نفس النوع!
تماما كما يمكنك استخدام X، I و B لبناء أنواع، يمكنك استخدام نمط المطابقة لتنفصل النوع، مثل القيام به في الإجابات الأخرى أعلاه:
returnInt :: SomeType -> Int
returnInt (I x) = x -- if the pattern matches (I x) then return x
returnInt _ = error "I need an integer value, you moron" -- throw an error otherwise
فقط تذكر أن مطابقة النمط يحدث بالترتيب: إذا كانت القيمة تطابق النموذج في بعض السطر، فإن الأنماط الموجودة في الأسطر الموجودة أدناه لن يتم تنفيذها.
لاحظ أنه عند تحديد نوعك كما فعلت، استخدم ما يسمى بناء جملة سجل (انظر فقط هنا: http://en.wikibooks.org/wiki/haskell/more_on_datatypes. )، حصلت على وظائف من هذا القبيل مجانا !!
حاول النظر في نوع القيم، على سبيل المثال:
*Main> :t myInt
myInt :: SomeType -> Int
وانظر إلى ما تفعله هذه الوظيفة:
*Main> myInt (I 5)
5
*Main> myInt (B False)
*** Exception: No match in record selector Main.myInt
هذا هو بالضبط سلوك returnInt
أعلاه المعرفة. رسالة الخطأ الغريبة تخبرك فقط أن الوظيفة لا تعرف كيفية التعامل مع عضو من النوع في وقت عام لا تتطابق (I x)
.
إذا حددت نوعك باستخدام بناء الجملة الأكثر شيوعا:
data SomeType2 = X String | I Int | B Bool
ثم تفقد تلك الوظائف السارية لطيفة.
رسائل الخطأ تنهي تنفيذ البرنامج. هذا مزعج في بعض الأحيان. إذا كنت بحاجة إلى سلوك أكثر أمانا لوظائفك إجابة GBACON هي طريقة القيام بذلك فقط. تعرف على Maybe a
اكتب واستخدامها للتعامل مع هذا النوع من الحساب الذي يحتاج إلى إرجاع بعض القيمة أو إرجاع أي شيء (جرب هذا: http://en.wikibooks.org/wiki/haskell/hierarchical_libraries/maybe. ).
نصائح أخرى
استخدام مطابقة نمط.
returnInt :: X -> Int
returnInt (I x) = x
returnInt _ = 0
استخدم تعريفا أكثر مرونة لجميع ممكن X
القيم:
returnInt :: X -> Maybe Int
returnInt (I i) = Just i
returnInt _ = Nothing
ثم يمكنك استخدام maybe
بالنسبة للتخلف عن التخلف عنها، قد تكون هناك قيمة صالحة (هذا يعرف باسم مشكلة semippredicate.):
*Main> maybe 0 id (returnInt $ X "")
0
*Main> maybe 0 id (returnInt $ I 123)
123
*Main> maybe (-1) id (returnInt $ X "yo")
-1
في المقابل، وظائف جزئية استثناءات وقت تشغيل المخاطر:
*Main> let returnInt (I i) = i
*Main> :t returnInt
returnInt :: X -> Int
*Main> returnInt (B True)
*** Exception: <interactive>:1:4-22: Non-exhaustive patterns in function returnInt
إذا كنت تشعر بالضفادع حقا، فيمكنك استخدامها MonadPlus
returnInt :: (MonadPlus m) => X -> m Int
returnInt (I i) = return i
returnInt _ = mzero
للحصول على المزيد من المرونة:
*Main> maybe 0 id (returnInt $ X "")
0
*Main> maybe 0 id (returnInt $ I 123)
123
*Main> returnInt (I 123) `mplus` returnInt (I 456) :: [Int]
[123,456]
إعطاء وظيفة مثل هذا:
returnInt :: X -> Int
returnInt x = {- some integer -}
...نوع من x
دائما X
. وبعد ما تهتم به هو ما إذا كان x
يستخدم X
, I
أو B
نوع المنشئ.
استخدم نمط المطابقة لإخبار الفرق:
returnInt :: X -> Int
returnInt (X _) = error "needed an Int, got a String"
returnInt (I { iVal = n }) = n
returnInt (B _) = error "needed an Int, got a Bool"