टाइप-आधारित आक्रमणकारियों के लिए कार्यात्मक प्रोग्रामिंग उत्तर क्या है?


9

मुझे पता है कि आक्रमणकारियों की अवधारणा कई प्रोग्रामिंग प्रतिमानों में मौजूद है। उदाहरण के लिए, लूप आक्रमणकारी ओओ, कार्यात्मक और प्रक्रियात्मक प्रोग्रामिंग में प्रासंगिक हैं।

हालांकि, ओओपी में पाया जाने वाला एक बहुत ही उपयोगी प्रकार एक विशेष प्रकार के डेटा का एक अपरिवर्तनीय है। यह मैं शीर्षक में "टाइप-आधारित हमलावर" कह रहा हूं। उदाहरण के लिए, एक Fractionप्रकार का एक हो सकता है numeratorऔर denominatorउनके gcd हमेशा होता है कि 1 अपरिवर्तनीय साथ, (यानी अंश एक कम रूप में है)। मैं केवल कुछ प्रकार के एनकैप्सुलेशन के द्वारा इसकी गारंटी दे सकता हूं, इसके डेटा को स्वतंत्र रूप से सेट नहीं करने देता। बदले में, मुझे कभी यह जांचने की ज़रूरत नहीं है कि क्या यह कम हो गया है, इसलिए मैं समानता जांच जैसे एल्गोरिदम को सरल बना सकता हूं।

दूसरी ओर, अगर मैं केवल एनकैप्सुलेशन के Fractionमाध्यम से इस गारंटी को प्रदान किए बिना एक प्रकार की घोषणा करता हूं, तो मैं इस प्रकार के किसी भी कार्य को सुरक्षित रूप से नहीं लिख सकता हूं, जो यह मानता है कि अंश कम हो गया है, क्योंकि भविष्य में कोई और साथ आ सकता है और एक रास्ता जोड़ सकता है गैर-कम अंश की पकड़

आम तौर पर, इस तरह के अपरिवर्तनीय की कमी के कारण हो सकता है:

  • पूर्व-स्थितियों के रूप में अधिक जटिल एल्गोरिदम को कई स्थानों पर जांच / सुनिश्चित किया जाना चाहिए
  • DRY उल्लंघन के रूप में इन दोहराया पूर्व स्थितियों में एक ही अंतर्निहित ज्ञान का प्रतिनिधित्व करते हैं (कि अपरिवर्तनीय सच होना चाहिए)
  • संकलन-समय की गारंटी के बजाय रनटाइम विफलताओं के माध्यम से पूर्व-शर्तों को लागू करना

तो मेरा सवाल यह है कि इस तरह के अपरिवर्तनीय के लिए कार्यात्मक प्रोग्रामिंग का जवाब क्या है। क्या कमोबेश यही काम करने का एक कार्यात्मक-मुहावरेदार तरीका है? या कार्यात्मक प्रोग्रामिंग का कोई पहलू है जो लाभ को कम प्रासंगिक बनाता है?


कई कार्यात्मक भाषाएं यह तुच्छ रूप से कर सकती हैं ... स्काला, एफ #, और अन्य भाषाएँ जो ओओपी के साथ अच्छी तरह से खेलती हैं, लेकिन हास्केल भी ... मूल रूप से कोई भी भाषा जो आपको प्रकारों को परिभाषित करने की अनुमति देती है और उनका व्यवहार इस बात का समर्थन करता है।
AK_

@AK_ मैं अवगत हूं F # ऐसा कर सकता है (हालांकि IIRC के लिए इसे कुछ मामूली घेरा-कूद की आवश्यकता होती है) और अनुमान लगाया कि स्काला एक अन्य क्रॉस-प्रतिमान भाषा के रूप में हो सकती है। दिलचस्प है कि हास्केल यह कर सकता है- एक लिंक मिला? मैं वास्तव में जिस चीज की तलाश कर रहा हूं वह कार्यात्मक-मुहावरेदार उत्तर है, बजाय विशिष्ट भाषाओं के जो एक सुविधा प्रदान करते हैं। लेकिन निश्चित रूप से चीजों के बजाय फजी और व्यक्तिपरक हो सकता है एक बार जब आप मुहावरा क्या बात करना शुरू कर देते हैं, यही कारण है कि मैंने इसे सवाल से छोड़ दिया।
बेन आरोनसन

ऐसे मामलों के लिए जहां पूर्व-संकलन को संकलन-समय पर चेक नहीं किया जा सकता है, कंस्ट्रक्टर में जांच करना मुहावरेदार है। एक PrimeNumberवर्ग पर विचार करें । यह प्रत्येक ऑपरेशन के लिए primality के लिए कई निरर्थक जांच करने के लिए बहुत महंगा होगा, लेकिन यह एक प्रकार का परीक्षण नहीं है जो संकलन-समय पर किया जा सकता है। (संचालन का एक बहुत आप रूढ़ अंक पर प्रदर्शन करने के लिए कहते हैं, गुणन चाहते हैं, एक नहीं बनाते बंद पोस्टिंग टिप्पणी के बाद से मैं कार्यात्मक प्रोग्रामिंग अपने आप को पता नहीं है के रूप में, यानी परिणाम शायद प्रधानमंत्री की गारंटी नहीं है
rwong

एक प्रतीत होता है असंबंधित प्रश्न, लेकिन ... क्या आरोही या इकाई परीक्षण अधिक महत्वपूर्ण हैं?
rwong

@rwong हाँ, कुछ अच्छे उदाहरण हैं। मैं वास्तव में 100% स्पष्ट नहीं हूं कि आप किस अंतिम बिंदु पर गाड़ी चला रहे हैं।
बेन आरोनसन

जवाबों:


2

कुछ कार्यात्मक भाषाओं जैसे OCaml में अंतर्निहित डेटा प्रकारों को लागू करने के लिए अंतर्निहित तंत्र होते हैं इसलिए कुछ अपरिवर्तनीयों को लागू करते हैं । जिन भाषाओं में इस तरह के तंत्र नहीं होते हैं, वे उपयोगकर्ताओं पर भरोसा करते हैं, "कालीन के नीचे नहीं देख" पर आक्रमणकारियों को लागू करने के लिए।

OCaml में सार डेटा प्रकार

OCaml में, किसी प्रोग्राम को स्ट्रक्चर करने के लिए मॉड्यूल का उपयोग किया जाता है। एक मॉड्यूल में एक कार्यान्वयन और एक हस्ताक्षर है , बाद वाले मॉड्यूल में परिभाषित मूल्यों और प्रकारों का एक प्रकार है, जबकि पूर्व वास्तविक परिभाषा प्रदान करता है। .c/.hसी प्रोग्रामर्स के परिचित डिप्टीच की तुलना में यह शिथिल हो सकता है।

एक उदाहरण के रूप में, हम Fractionमॉड्यूल को इस तरह लागू कर सकते हैं :

# module Fraction = struct
  type t = Fraction of int * int
  let rec gcd a b =
    match a mod b with
    | 0 -> b
    | r -> gcd b r

  let make a b =
   if b = 0 then
     invalid_arg "Fraction.make"
   else let d = gcd (abs a) (abs b) in
     Fraction(a/d, b/d)

  let to_string (Fraction(a,b)) =
    Printf.sprintf "Fraction(%d,%d)" a b

  let add (Fraction(a1,b1)) (Fraction(a2,b2)) =
    make (a1*b2 + a2*b1) (b1*b2)

  let mult (Fraction(a1,b1)) (Fraction(a2,b2)) =
    make (a1*a2) (b1*b2)
end;;

module Fraction :
  sig
    type t = Fraction of int * int
    val gcd : int -> int -> int
    val make : int -> int -> t
    val to_string : t -> string
    val add : t -> t -> t
    val mult : t -> t -> t
  end

इस परिभाषा को अब इस तरह इस्तेमाल किया जा सकता है:

# Fraction.add (Fraction.make 8 6) (Fraction.make 14 21);;
- : Fraction.t = Fraction.Fraction (2, 1)

कोई भी सीधे निर्मित किए गए सुरक्षा जाल को दरकिनार करते हुए प्रकार के भिन्न मानों का उत्पादन कर सकता है Fraction.make:

# Fraction.Fraction(0,0);;
- : Fraction.t = Fraction.Fraction (0, 0)

इसे रोकने के लिए, इस प्रकार की ठोस परिभाषा को छिपाना संभव Fraction.tहै:

# module AbstractFraction : sig
  type t
  val make : int -> int -> t
  val to_string : t -> string
  val add : t -> t -> t
  val mult : t -> t -> t
end = Fraction;;

module AbstractFraction :
sig
  type t
  val make : int -> int -> t
  val to_string : t -> string
  val add : t -> t -> t
  val mult : t -> t -> t
end

फ़ंक्शन AbstractFraction.tका उपयोग करने के लिए एक बनाने का एकमात्र तरीका है AbstractFraction.make

स्कीम में सार डेटा प्रकार

स्कीम की भाषा में अमूर्त डेटा प्रकारों के समान तंत्र नहीं है जैसा कि OCaml करता है। यह एनकैप्सुलेशन प्राप्त करने के लिए उपयोगकर्ता "कालीन के नीचे नहीं देख" पर निर्भर करता है।

योजना में, यह fraction?इनपुट को मान्य करने का अवसर देने वाले मूल्यों को पहचानने जैसे विधेय को परिभाषित करने के लिए प्रथागत है । मेरे अनुभव में प्रमुख उपयोग उपयोगकर्ता को उसके इनपुट को मान्य करने देने के लिए है, यदि वह प्रत्येक लाइब्रेरी कॉल में इनपुट को मान्य करने के बजाय एक मूल्य बनाता है।

हालांकि लौटाए गए मूल्यों के अमूर्त को लागू करने के लिए कई रणनीतियां हैं, जैसे कि एक क्लोजर वापस करना जो कि लागू होने पर मूल्य का पैदावार करता है या लाइब्रेरी द्वारा प्रबंधित एक पूल में मूल्य का संदर्भ लौटाता है - लेकिन मैंने उनमें से किसी को भी अभ्यास में कभी नहीं देखा।


+1 यह भी ध्यान देने योग्य है कि सभी ओओ भाषाएँ एनकैप्सुलेशन को लागू नहीं करती हैं।
माइकल शॉ

5

एनकैप्सुलेशन एक ऐसी सुविधा नहीं है जो OOP के साथ आई है। कोई भी भाषा जो उचित रूपांतरकरण का समर्थन करती है, उसके पास है।

यहाँ मोटे तौर पर आप इसे हास्केल में कैसे करते हैं:

-- Rational.hs
module Rational (
    -- This is the export list. Functions not in this list aren't visible to importers.
    Rational, -- Exports the data type, but not its constructor.
    ratio,
    numerator,
    denominator
    ) where

data Rational = Rational Int Int

-- This is the function we provide for users to create rationals
ratio :: Int -> Int -> Rational
ratio num den = let (num', den') = reduce num den
                 in Rational num' den'

-- These are the member accessors
numerator :: Rational -> Int
numerator (Rational num _) = num

denominator :: Rational -> Int
denominator (Rational _ den) = den

reduce :: Int -> Int -> (Int, Int)
reduce a b = let g = gcd a b
             in (a `div` g, b `div` g)

अब, एक तर्कसंगत बनाने के लिए, आप अनुपात फ़ंक्शन का उपयोग करते हैं, जो अपरिवर्तनीय को लागू करता है। क्योंकि डेटा अपरिवर्तनीय है, आप बाद में अपरिवर्तनीय का उल्लंघन नहीं कर सकते।

इससे आपको कुछ खर्च करना पड़ता है, हालांकि: उपयोगकर्ता के लिए समान डिकंस्ट्रक्टिंग डिक्लेनेटर और संख्यात्मक उपयोग के रूप में उपयोग करना संभव नहीं है।


4

आप इसे उसी तरह कार्य करें: एक बनाने के निर्माता है कि बाधा लागू करता है और कि निर्माता का उपयोग करने के लिए जब भी आप एक नया मान बनाने के लिए सहमत हैं।

multiply lhs rhs = ReducedFraction (lhs.num * rhs.num) (lhs.denom * rhs.denom)

लेकिन कार्ल, OOP में आप की जरूरत नहीं है इस बात से सहमत निर्माता का उपयोग करें। क्या सचमे?

class Fraction:
  ...
  Fraction multiply(Fraction lhs, Fraction rhs):
    Fraction result = lhs.clone()
    result.num *= rhs.num
    result.denom *= rhs.denom
    return result

वास्तव में, एफपी में इस तरह के दुरुपयोग के अवसर कम हैं। आप है अचल स्थिति की वजह से पिछले निर्माता डाल करने के लिए,। काश, लोग अक्षम सहयोगियों के खिलाफ किसी प्रकार के संरक्षण के रूप में, या बाधाओं को संप्रेषित करने की आवश्यकता को कम करने के रूप में इनकैप्सुलेशन के बारे में सोचना बंद कर देते। यह ऐसा नहीं करता है। यह सिर्फ उन जगहों को सीमित करता है जहां आपको जांच करनी है। अच्छे एफपी प्रोग्रामर भी एनकैप्सुलेशन का उपयोग करते हैं। यह कुछ प्रकार के संशोधनों को बनाने के लिए कुछ पसंदीदा कार्यों को संप्रेषित करने के रूप में आता है।


उदाहरण के लिए, C # में कोड लिखना संभव है (और मुहावरेदार), उदाहरण के लिए, जो आपने वहां किया है वह अनुमति नहीं देता है। और मुझे लगता है कि एक एकल वर्ग के बीच एक स्पष्ट रूप से स्पष्ट अंतर है जो किसी भी व्यक्ति द्वारा लिखे गए एक अपरिवर्तनीय और हर फ़ंक्शन को लागू करने के लिए जिम्मेदार है, कहीं भी एक ही प्रकार का उपयोग करता है जो एक ही रूपांतर लागू करता है।
बेन आरोनसन

@BenAaronson "लागू करने" और " अशुभ " प्रचारित करने के बीच अंतर को नोटिस करता है।
rwong

1
+1। यह तकनीक एफपी में और भी अधिक शक्तिशाली है क्योंकि अपरिवर्तनीय मूल्य नहीं बदलते हैं; इस प्रकार आप उनके बारे में चीजों को "एक बार और सभी के लिए" प्रकारों का उपयोग करके साबित कर सकते हैं। यह परिवर्तनशील वस्तुओं के साथ संभव नहीं है क्योंकि जो उनके लिए सच है वह बाद में सच नहीं हो सकता है; सबसे अच्छा आप रक्षात्मक रूप से वस्तु की स्थिति की फिर से जांच कर सकते हैं।
डोभाल

@ डोभाल मैं इसे नहीं देख रहा हूं। उस सबसे (?) प्रमुख OO भाषाओं को अलग रखने से चर को अपरिवर्तनीय बनाने का एक तरीका है। OO में मेरे पास है: एक उदाहरण बनाएँ, फिर मेरा कार्य उस उदाहरण के मानों को इस तरह से परिवर्तित करता है, जो अपरिवर्तनीय के अनुरूप हो भी सकता है और नहीं भी। एफपी में मेरे पास है: एक उदाहरण बनाएं, फिर मेरा कार्य अलग-अलग मूल्यों के साथ एक दूसरा उदाहरण बनाता है जो कि इनवैलिड के अनुरूप हो सकता है या नहीं। मैं यह नहीं देखता कि अपरिवर्तनीयता ने मुझे किस तरह से आश्वस्त करने में मदद की है कि मेरा अपरिवर्तनीय प्रकार के सभी उदाहरणों के अनुरूप है
बेन आरोनसन

2
@BenAaronson अपरिवर्तनशीलता आपको यह साबित करने में मदद नहीं करेगी कि आपने अपने प्रकार को सही तरीके से लागू किया है (अर्थात सभी कार्य कुछ अपरिवर्तनीय को संरक्षित करते हैं।) मैं जो कह रहा हूं वह आपको मूल्यों के बारे में तथ्यों का प्रचार करने की अनुमति देता है। आप कुछ हालत (जैसे यह संख्या सम है) को एक प्रकार से (निर्माणकर्ता में इसके लिए जाँच करके) सांकेतिक शब्दों में बदलना और उत्पादित मूल्य इस बात का प्रमाण है कि मूल मूल्य ने स्थिति को संतुष्ट किया है। परिवर्तनशील वस्तुओं के साथ आप वर्तमान स्थिति की जांच करते हैं और परिणाम को बूलियन में रखते हैं। यह बूलियन केवल तब तक के लिए अच्छा है जब तक कि वस्तु उत्परिवर्तित न हो ताकि स्थिति झूठी हो।
डोभाल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.