"अमूर्त ओवर" का क्या अर्थ है?


95

अक्सर स्काला साहित्य में, मैं "अमूर्त ओवर" वाक्यांश का सामना करता हूं, लेकिन मैं इरादे को नहीं समझता। उदाहरण के लिए , मार्टिन ओडस्की लिखते हैं

आप मानकों (या "कार्यों") को मापदंडों के रूप में पारित कर सकते हैं, या आप उन पर सार कर सकते हैं। आप पैरामीटर के रूप में प्रकार निर्दिष्ट कर सकते हैं, या आप उन पर सार कर सकते हैं।

एक अन्य उदाहरण के रूप में, "ऑब्जर्वर पैटर्न का निरूपण" पेपर में,

हमारे घटना धाराओं का एक परिणाम यह है कि हम प्रथम श्रेणी के मूल्यों पर आधारित हैं और हम उन पर अमल कर सकते हैं।

मैंने पढ़ा है कि पहला क्रम "प्रकार पर अमूर्त", जबकि भिक्षु "टाइप कंस्ट्रक्टर पर अमूर्त"। और हम केक पैटर्न पेपर में इस तरह के वाक्यांश भी देखते हैं । ऐसे कई उदाहरणों में से एक को उद्धृत करने के लिए:

सार प्रकार के सदस्य ठोस प्रकार के घटकों पर अमूर्त करने के लिए लचीला तरीका प्रदान करते हैं।

यहां तक ​​कि प्रासंगिक स्टैक ओवरफ्लो प्रश्न इस शब्दावली का उपयोग करते हैं। "मानकीकृत प्रकार पर अस्तित्वगत सार नहीं हो सकता ..."

तो ... वास्तव में "सार" का क्या मतलब है?

जवाबों:


124

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

उदाहरण के लिए, एक प्रोग्राम है जो संख्याओं का योग लेता है पर विचार करें 1, 2और 3:

val sumOfOneTwoThree = 1 + 2 + 3

यह कार्यक्रम बहुत दिलचस्प नहीं है, क्योंकि यह बहुत सार नहीं है। हम एक ही प्रतीक के तहत संख्या की सभी सूचियों को एकीकृत करके, हमारे द्वारा संक्षेपित संख्याओं पर सार कर सकते हैं ns:

def sumOf(ns: List[Int]) = ns.foldLeft(0)(_ + _)

और हम विशेष रूप से परवाह नहीं करते हैं कि यह एक सूची है या तो। सूची एक विशिष्ट प्रकार का कंस्ट्रक्टर है (एक प्रकार लेता है और एक प्रकार देता है), लेकिन हम टाइप कंस्ट्रक्टर पर यह निर्दिष्ट करके सार कर सकते हैं कि हम कौन सी आवश्यक विशेषता चाहते हैं (कि इसे फोल्ड किया जा सकता है):

trait Foldable[F[_]] {
  def foldl[A, B](as: F[A], z: B, f: (B, A) => B): B
}

def sumOf[F[_]](ns: F[Int])(implicit ff: Foldable[F]) =
  ff.foldl(ns, 0, (x: Int, y: Int) => x + y)

और हमारे पास किसी भी अन्य चीज के Foldableलिए निहित उदाहरण Listहो सकते हैं।

implicit val listFoldable = new Foldable[List] {
  def foldl[A, B](as: List[A], z: B, f: (B, A) => B) = as.foldLeft(z)(f)
}

val sumOfOneTwoThree = sumOf(List(1,2,3))

क्या अधिक है, हम ऑपरेशन और ऑपरेंड्स के प्रकार दोनों पर अमूर्त कर सकते हैं :

trait Monoid[M] {
  def zero: M
  def add(m1: M, m2: M): M
}

trait Foldable[F[_]] {
  def foldl[A, B](as: F[A], z: B, f: (B, A) => B): B
  def foldMap[A, B](as: F[A], f: A => B)(implicit m: Monoid[B]): B =
    foldl(as, m.zero, (b: B, a: A) => m.add(b, f(a)))
}

def mapReduce[F[_], A, B](as: F[A], f: A => B)
                         (implicit ff: Foldable[F], m: Monoid[B]) =
  ff.foldMap(as, f)

अब हमारे पास कुछ सामान्य है। विधि mapReduceकिसी भी F[A]दिए को मोड़ देगी जो हम साबित कर सकते हैं कि Fवह तह है और वह Aएक मोनॉयड है या एक में मैप किया जा सकता है। उदाहरण के लिए:

case class Sum(value: Int)
case class Product(value: Int)

implicit val sumMonoid = new Monoid[Sum] {
  def zero = Sum(0)
  def add(a: Sum, b: Sum) = Sum(a.value + b.value)
}

implicit val productMonoid = new Monoid[Product] {
  def zero = Product(1)
  def add(a: Product, b: Product) = Product(a.value * b.value)
}

val sumOf123 = mapReduce(List(1,2,3), Sum)
val productOf456 = mapReduce(List(4,5,6), Product)

हम monoids और foldables पर अमूर्त है


@coubeatceshop कोड REPL ठीक चलता है। आप किस स्केला का उपयोग कर रहे हैं, और आपको क्या त्रुटि मिली?
डैनियल सी। सोबरल

1
अगर आप दो अंतिम उदाहरणों में से एक Setया किसी अन्य प्रकार के फोल्डेबल टाइप बनाते हैं, तो @Apocalisp यह दिलचस्प होगा । एक Stringऔर संगति के साथ एक उदाहरण भी बहुत अच्छा होगा।
डैनियल सी। सोबरल

1
सुंदर जवाब, रनर। धन्यवाद! मैंने डैनियल के सुझाव का पालन किया, और बिना किसी नक्शे में बदलाव किए बिना निहित सेट-फोल्डेबल और कॉन्केंटमोनॉइड का पालन किया। मैं इसे अच्छी तरह से करने के लिए अपने रास्ते पर हूँ।
मॉर्गन क्रेयटन

6
मुझे यह प्राप्त करने में एक पल लगा कि अंतिम 2 पंक्तियों में आप इस तथ्य का लाभ उठाते हैं कि सम और उत्पाद के साथी ऑब्जेक्ट, क्योंकि वे लागू (इंट) को परिभाषित करते हैं, उन्हें Int => सम और इंट => उत्पाद के रूप में माना जाता है संकलक। बहुत अच्छा!
क्राइस न्यूटीकॉम्बे

अच्छा लेख :)! आपके अंतिम उदाहरण में, मोनॉयड निहित तर्क अनावश्यक लगता है। यह सरल है: gist.github.com/cvogt/9716490
cvogt

11

पहले सन्निकटन के लिए, किसी चीज़ पर "अमूर्त" करने में सक्षम होने का मतलब है कि सीधे उस चीज़ का उपयोग करने के बजाय, आप इसका एक पैरामीटर बना सकते हैं, या अन्यथा इसका उपयोग "गुमनाम" कर सकते हैं।

स्काला आपको वर्गों, विधियों और मूल्यों के प्रकार पैरामीटर, और मानों के सार (या अनाम) प्रकारों की अनुमति देकर आपको प्रकारों पर अमूर्त करने की अनुमति देता है।

स्कैला आपको फ़ंक्शन मापदंडों के तरीकों की अनुमति देकर कार्यों पर अमूर्त करने की अनुमति देता है।

स्काला आपको संरचनात्मक रूप से परिभाषित होने की अनुमति देकर सुविधाओं पर अमूर्त करने की अनुमति देता है।

स्काला आपको उच्च-प्रकार के प्रकार के मापदंडों की अनुमति देकर, प्रकार के मापदंडों पर अमूर्त करने की अनुमति देता है।

स्काला आपको एक्सट्रैक्टर्स बनाने की अनुमति देकर डेटा एक्सेस पैटर्न पर अमूर्त करने की अनुमति देता है।

स्काला आपको "उन चीज़ों पर अमल करने की अनुमति देता है, जिन्हें कुछ और के रूप में इस्तेमाल किया जा सकता है", पैरामीटर के रूप में निहित रूपांतरण की अनुमति देकर। हास्केल इसी तरह की कक्षाओं के साथ करता है।

स्केल (अभी तक) आपको कक्षाओं में अमूर्त करने की अनुमति नहीं देता है। आप किसी कक्षा को किसी चीज़ में पास नहीं कर सकते, और फिर नई वस्तुओं को बनाने के लिए उस कक्षा का उपयोग कर सकते हैं। अन्य भाषाएं कक्षाओं में अमूर्तता की अनुमति देती हैं।

("टाइप कंस्ट्रक्टर्स पर मोनड्स सार" केवल बहुत ही प्रतिबंधात्मक तरीके से सच है। जब तक आप अपने "अहा! मैं समझ नहीं लेते !!"

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


1
आप उस कक्षा की नई वस्तुओं को पलटने के लिए एक Manifest, या एक भी पास कर सकते हैं Class, और प्रतिबिंब का उपयोग कर सकते हैं ।
डैनियल सी। सोबरल

6

एक अमूर्तता सामान्यीकरण का एक प्रकार है।

http://en.wikipedia.org/wiki/Abstraction

न केवल स्काला में, बल्कि कई भाषाओं में जटिलता को कम करने के लिए (या कम से कम एक पदानुक्रम बनाने के लिए ऐसे तंत्रों की आवश्यकता है जो जानकारी को टुकड़ों को समझने में आसान बनाते हैं)।

एक वर्ग एक साधारण डेटा प्रकार पर अमूर्तता है। यह एक बुनियादी प्रकार की तरह है, लेकिन वास्तव में उन्हें सामान्यीकृत करता है। तो एक वर्ग एक साधारण डेटा प्रकार से अधिक है, लेकिन इसके साथ कई चीजें समान हैं।

जब वह कहता है "अमूर्तता से अधिक" उसका मतलब वह प्रक्रिया है जिसके द्वारा आप सामान्यीकरण करते हैं। इसलिए यदि आप मापदंडों के रूप में तरीकों पर अमूर्त कर रहे हैं तो आप ऐसा करने की प्रक्रिया को सामान्य कर रहे हैं। उदाहरण के लिए, फ़ंक्शंस के पास करने के तरीकों के बजाय आप इसे संभालने के लिए कुछ प्रकार के सामान्यीकृत तरीके बना सकते हैं (जैसे कि सभी तरीकों को पारित नहीं करना लेकिन इससे निपटने के लिए एक विशेष प्रणाली का निर्माण करना)।

इस मामले में वह विशेष रूप से एक समस्या को अमूर्त करने और समस्या के समाधान की तरह एक ऊप बनाने की प्रक्रिया का मतलब है। C में अमूर्त करने की बहुत कम क्षमता है (आप इसे कर सकते हैं लेकिन यह वास्तविक रूप से गड़बड़ हो जाता है और भाषा सीधे इसका समर्थन नहीं करती है)। यदि आपने इसे C ++ में लिखा है, तो आप समस्या की जटिलता को कम करने के लिए oop अवधारणाओं का उपयोग कर सकते हैं (ठीक है, यह एक ही जटिलता है लेकिन अवधारणा आमतौर पर आसान है (कम से कम एक बार आप सार के संदर्भ में सोचना सीख लें)।

उदाहरण के लिए, यदि मुझे एक विशेष डेटा प्रकार की आवश्यकता होती है, जो एक इंट की तरह होता है, लेकिन यह कहता है कि मैं एक नए प्रकार का निर्माण करके उस पर अमूर्त कर सकता हूं जिसे इंट की तरह इस्तेमाल किया जा सकता है, लेकिन उन गुणों की मुझे आवश्यकता थी। इस तरह की बात करने के लिए मैं जिस प्रक्रिया का उपयोग करूंगा उसे "अमूर्त" कहा जाएगा।


5

यहाँ मेरा संकीर्ण शो है और व्याख्या बताइए। यह आत्म-व्याख्यात्मक है और आरईपीएल में चलता है।

class Parameterized[T] { // type as a parameter
  def call(func: (Int) => Int) = func(1)  // function as a parameter
  def use(l: Long) { println(l) } // value as a parameter
}

val p = new Parameterized[String] // pass type String as a parameter
p.call((i:Int) => i + 1) // pass function increment as a parameter
p.use(1L) // pass value 1L as a parameter


abstract class Abstracted { 
  type T // abstract over a type
  def call(i: Int): Int // abstract over a function
  val l: Long // abstract over value
  def use() { println(l) }
}

class Concrete extends Abstracted { 
  type T = String // specialize type as String
  def call(i:Int): Int = i + 1 // specialize function as increment function
  val l = 1L // specialize value as 1L
}

val a: Abstracted = new Concrete
a.call(1)
a.use()

1
कोड में बहुत अधिक "अमूर्त" विचार बहुत शक्तिशाली है, फिर भी इस भाषा को
आज़माएंगे

2

अन्य उत्तर पहले से ही एक अच्छा विचार देते हैं कि किस प्रकार के सार मौजूद हैं। आइए एक-एक करके उद्धरणों पर जाएँ, और एक उदाहरण दें:

आप मानकों (या "कार्यों") को मापदंडों के रूप में पारित कर सकते हैं, या आप उन पर सार कर सकते हैं। आप पैरामीटर के रूप में प्रकार निर्दिष्ट कर सकते हैं, या आप उन पर सार कर सकते हैं।

एक पैरामीटर के रूप में पास फ़ंक्शन: List(1,-2,3).map(math.abs(x))स्पष्ट absरूप से यहां पैरामीटर के रूप में पारित किया गया है। mapअपने आप में एक फ़ंक्शन पर अमूर्त होता है जो प्रत्येक सूची तत्व के साथ एक निश्चित विशिष्ट चीज़ करता है। val list = List[String]()एक प्रकार का पैरामेटर (स्ट्रिंग) निर्दिष्ट करता है। आप एक संग्रह प्रकार है जो अमूर्त प्रकार सदस्यों के बजाय का उपयोग करता है लिख सकते हैं: val buffer = Buffer{ type Elem=String }। एक अंतर यह है कि आपको लिखना है def f(lis:List[String])...लेकिन def f(buffer:Buffer)..., इसलिए तत्व का प्रकार दूसरी विधि में "छिपा हुआ" है।

हमारे घटना धाराओं का एक परिणाम यह है कि हम प्रथम श्रेणी के मूल्यों पर आधारित हैं और हम उन पर अमल कर सकते हैं।

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

सार प्रकार के सदस्य ठोस प्रकार के घटकों पर अमूर्त करने के लिए लचीला तरीका प्रदान करते हैं।

उपरोक्त बफ़र वर्ग इसके लिए पहले से एक उदाहरण है।


0

उपरोक्त उत्तर एक उत्कृष्ट व्याख्या प्रदान करते हैं, लेकिन इसे एक वाक्य में संक्षेप में कहें, तो मैं कहूंगा:

किसी चीज पर अमल करना बहुत ही उसी तरह की उपेक्षा करना है, जहां वह अप्रासंगिक है

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.