स्काला के सभी प्रतीकात्मक ऑपरेटरों का क्या मतलब है?


402

स्काला सिंटैक्स में बहुत सारे प्रतीक हैं। चूंकि इन प्रकार के नामों को खोज इंजन का उपयोग करना मुश्किल है, इसलिए उनकी एक व्यापक सूची उपयोगी होगी।

स्काला में सभी प्रतीक क्या हैं, और उनमें से प्रत्येक क्या करता है?

विशेष रूप से, मैं जानना चाहते हैं के बारे में ->, ||=, ++=, <=, _._, ::, और :+=


4
और सीढ़ी के प्रथम संस्करण का सूचकांक, >> artima.com/pins1ed/book-index.html#indexanchor पर
जीन टी

2
संबंधित: ऑपरेटर वर्ण बनाम अल्फ़ान्यूमेरिक वर्ण: stackoverflow.com/questions/7656937/…
लुइगी प्लिंज

1
भी, अगर वहाँ "ऑपरेटरों" (जो ज्यादातर तरीके हैं, कुछ वर्ग के नाम infix के साथ प्रयोग किया जाता है) जिसे आप स्केलक्स या सीढ़ी की किताब में नहीं पा सकते हैं, उदाहरण के लिए "!!", संभावना स्रोत अक्का, स्केलाज़ के लिए स्कैल्पडॉक्स हैं !! और sbt
जीन टी

उदाहरण के लिए इस्तेमाल किए जाने वाले वर्ग के नाम (in German) >> raichoo.blogspot.com/2010/06/spass-mit-scala-infixtypen.html
जीन टी

खोज इंजन द्वारा फ़िल्टरिंग के मुद्दे के बारे में, symbolhound.com भी एक अच्छा विकल्प है
पैट्रिक रिफंडिनी

जवाबों:


526

मैं शिक्षण के उद्देश्य से संचालकों को चार श्रेणियों में बांटता हूं :

  • कीवर्ड / आरक्षित प्रतीक
  • स्वचालित रूप से आयातित तरीके
  • सामान्य विधियाँ
  • सिंथेटिक शक्कर / रचना

यह सौभाग्य की बात है, कि अधिकांश श्रेणियों को प्रश्न में दर्शाया गया है:

->    // Automatically imported method
||=   // Syntactic sugar
++=   // Syntactic sugar/composition or common method
<=    // Common method
_._   // Typo, though it's probably based on Keyword/composition
::    // Common method
:+=   // Common method

इन विधियों में से अधिकांश का सटीक अर्थ उस वर्ग पर निर्भर करता है जो उन्हें परिभाषित कर रहा है। उदाहरण के लिए, <=पर Intसाधन "से कम या बराबर" । पहले वाला, ->मैं नीचे उदाहरण के रूप में दूंगा। ::संभवतः इस विधि को परिभाषित किया गया है List(हालांकि यह उसी नाम की वस्तु हो सकती है), और:+= संभवतः विभिन्न Bufferवर्गों पर परिभाषित विधि है ।

तो, चलो उन्हें देखते हैं।

कीवर्ड / आरक्षित प्रतीक

स्काला में कुछ प्रतीक हैं जो विशेष हैं। उनमें से दो को उचित कीवर्ड माना जाता है, जबकि अन्य केवल "आरक्षित" हैं। वो हैं:

// Keywords
<-  // Used on for-comprehensions, to separate pattern from generator
=>  // Used for function types, function literals and import renaming

// Reserved
( )        // Delimit expressions and parameters
[ ]        // Delimit type parameters
{ }        // Delimit blocks
.          // Method call and path separator
// /* */   // Comments
#          // Used in type notations
:          // Type ascription or context bounds
<: >: <%   // Upper, lower and view bounds
<? <!      // Start token for various XML elements
" """      // Strings
'          // Indicate symbols and characters
@          // Annotations and variable binding on pattern matching
`          // Denote constant or enable arbitrary identifiers
,          // Parameter separator
;          // Statement separator
_*         // vararg expansion
_          // Many different meanings

ये सभी भाषा का हिस्सा हैं , और, जैसे, किसी भी पाठ में पाया जा सकता है जो भाषा का ठीक से वर्णन करता है, जैसे कि Scala Specification (पीडीएफ)।

पिछले एक, अंडरस्कोर, एक विशेष विवरण के लायक है, क्योंकि यह बहुत व्यापक रूप से उपयोग किया जाता है, और इतने सारे अलग-अलग अर्थ हैं। यहाँ एक नमूना है:

import scala._    // Wild card -- all of Scala is imported
import scala.{ Predef => _, _ } // Exception, everything except Predef
def f[M[_]]       // Higher kinded type parameter
def f(m: M[_])    // Existential type
_ + _             // Anonymous function placeholder parameter
m _               // Eta expansion of method into method value
m(_)              // Partial function application
_ => 5            // Discarded parameter
case _ =>         // Wild card pattern -- matches anything
f(xs: _*)         // Sequence xs is passed as multiple parameters to f(ys: T*)
case Seq(xs @ _*) // Identifier xs is bound to the whole matched sequence

मैं शायद कुछ अन्य अर्थ भूल गया, हालांकि।

स्वचालित रूप से आयातित तरीके

इसलिए, यदि आपको वह प्रतीक नहीं मिला, जिसे आप ऊपर की सूची में देख रहे हैं, तो यह एक विधि या एक का हिस्सा होना चाहिए। लेकिन, अक्सर, आपको कुछ प्रतीक दिखाई देंगे और वर्ग के लिए प्रलेखन में वह विधि नहीं होगी। जब ऐसा होता है, या तो आप एक या एक से अधिक विधियों की संरचना को किसी अन्य चीज़ के साथ देख रहे हैं, या विधि को दायरे में आयात किया गया है, या एक आयातित निहित रूपांतरण के माध्यम से उपलब्ध है।

ये अभी भी स्कालाडॉक पर पाए जा सकते हैं : आपको बस यह जानना होगा कि उन्हें कहां देखना है। या, इसे विफल करते हुए, सूचकांक को देखें (वर्तमान में 2.9.1 पर टूट गया, लेकिन रात में उपलब्ध है)।

प्रत्येक स्काला कोड में तीन स्वचालित आयात हैं:

// Not necessarily in this order
import _root_.java.lang._      // _root_ denotes an absolute path
import _root_.scala._
import _root_.scala.Predef._

पहले दो केवल कक्षाओं और एकल वस्तुओं को उपलब्ध कराते हैं। तीसरे में सभी निहित रूपांतरण और आयातित विधियां शामिल हैं, क्योंकि Predefएक वस्तु ही है।

Predefजल्दी से अंदर देखते हुए कुछ प्रतीक दिखाए:

class <:<
class =:=
object <%<
object =:=

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

"a" -> 1  // Look for an implicit from String, AnyRef, Any or type parameter

उपरोक्त मामले ->में, क्लास में ArrowAssocउस विधि के माध्यम से परिभाषित किया any2ArrowAssocजाता है जो ऑब्जेक्ट का प्रकार लेता है A, जहांA एक ही विधि के लिए एक अनबाउंड प्रकार पैरामीटर है।

सामान्य विधियाँ

तो, कई प्रतीकों एक वर्ग पर बस तरीके हैं। उदाहरण के लिए, यदि आप ऐसा करते हैं

List(1, 2) ++ List(3, 4)

आप सूची के++ लिए ScalaDoc पर सही विधि पाएंगे । हालाँकि, एक कन्वेंशन है जिसे आपको तरीकों की खोज करते समय पता होना चाहिए। बृहदान्त्र में समाप्त होने के तरीके ( ) बाएं के बजाय दाईं ओर बांधते हैं । दूसरे शब्दों में, जबकि उपरोक्त विधि कॉल के बराबर है::

List(1, 2).++(List(3, 4))

अगर मेरे पास, इसके बजाय, इसके 1 :: List(2, 3)बराबर होगा:

List(2, 3).::(1)

इसलिए आपको बृहदान्त्र में समाप्त होने वाले तरीकों की तलाश करते हुए दाईं ओर पाए जाने वाले प्रकार को देखना होगा । उदाहरण के लिए विचार करें:

1 +: List(2, 3) :+ 4

पहली विधि ( +:) दाईं ओर बांधती है, और पाई जाती है List। दूसरी विधि ( :+) सिर्फ एक सामान्य विधि है, और बाईं ओर - फिर से, पर बांधती हैList

सिंथेटिक शक्कर / रचना

तो, यहाँ कुछ सिन्थेटिक शक्कर है जो एक विधि को छिपा सकती है:

class Example(arr: Array[Int] = Array.fill(5)(0)) {
  def apply(n: Int) = arr(n)
  def update(n: Int, v: Int) = arr(n) = v
  def a = arr(0); def a_=(v: Int) = arr(0) = v
  def b = arr(1); def b_=(v: Int) = arr(1) = v
  def c = arr(2); def c_=(v: Int) = arr(2) = v
  def d = arr(3); def d_=(v: Int) = arr(3) = v
  def e = arr(4); def e_=(v: Int) = arr(4) = v
  def +(v: Int) = new Example(arr map (_ + v))
  def unapply(n: Int) = if (arr.indices contains n) Some(arr(n)) else None
}

val Ex = new Example // or var for the last example
println(Ex(0))  // calls apply(0)
Ex(0) = 2       // calls update(0, 2)
Ex.b = 3        // calls b_=(3)
// This requires Ex to be a "val"
val Ex(c) = 2   // calls unapply(2) and assigns result to c
// This requires Ex to be a "var"
Ex += 1         // substituted for Ex = Ex + 1

अंतिम एक दिलचस्प है, क्योंकि किसी भी प्रतीकात्मक विधि को एक असाइनमेंट जैसी विधि बनाने के लिए जोड़ा जा सकता है।

और, ज़ाहिर है, विभिन्न संयोजन हैं जो कोड में दिखाई दे सकते हैं:

(_+_) // An expression, or parameter, that is an anonymous function with
      // two parameters, used exactly where the underscores appear, and
      // which calls the "+" method on the first parameter passing the
      // second parameter as argument.

1
क्या val c = ex(2)इसके बजाय आपका मतलब था val ex(c) = 2?
माइक स्टे

3
@ मायकेस्टे नो, मेरा मतलब था val ex(c) = 2
डैनियल सी। सोबरल

ओह, यह पैटर्न मिलान वाक्यविन्यास का उपयोग कर रहा है। धन्यवाद।
माइक स्टे

=> बीच में उपयोग किए जाने पर 'नाम से कॉल' की स्थिति भी बताता है: और y में टाइप करें: => Int '
स्टीफन डब्ल्यू। राइट

1
शायद एक को भी उल्लेख करना चाहिए: / / और: \ सचमुच अनजाने ऑपरेटरों। तो map.foldLeft (initialVal) वही है जो (initialVal: / map) -: \ के बजाय foldRal है।
मिस्टर एमटी

24

स्काला और अन्य भाषाओं के बीच एक (अच्छा, IMO) अंतर यह है कि यह आपको लगभग किसी भी चरित्र के साथ अपने तरीकों का नाम देता है।

आप जो गणना करते हैं वह "विराम चिह्न" नहीं है, बल्कि सादे और सरल तरीके हैं, और जैसे कि उनका व्यवहार एक वस्तु से दूसरी वस्तु में भिन्न होता है (हालांकि कुछ सम्मेलन होते हैं)।

उदाहरण के लिए, सूची के लिए स्केलडॉक प्रलेखन की जाँच करें , और आप यहां बताए गए कुछ तरीकों को देखेंगे।

ध्यान रखने योग्य कुछ बातें:

  • ज्यादातर बार A operator+equal Bसंयोजन A = A operator Bमें ||=या ++=उदाहरणों की तरह अनुवाद होता है ।

  • समाप्त होने वाले तरीके :सही सहयोगी हैं, इसका मतलब यह है कि A :: Bवास्तव में है B.::(A)

आपको Scala प्रलेखन ब्राउज़ करके अधिकांश उत्तर मिलेंगे। यहाँ एक संदर्भ रखने से प्रयासों की नकल होगी, और यह जल्दी से पीछे गिर जाएगा :)


21

आप कुछ मानदंडों के अनुसार पहले समूह कर सकते हैं। इस पोस्ट में मैं सिर्फ अंडरस्कोर कैरेक्टर और राइट-एरो समझाऊंगा।

_._एक अवधि शामिल है। स्काला में एक अवधि हमेशा एक विधि कॉल को इंगित करती है । तो आपके पास रिसीवर की अवधि शेष है, और इसके दाईं ओर संदेश (विधि नाम) है। अब स्काला में _एक विशेष प्रतीक है। इसके बारे में कई पोस्ट हैं, उदाहरण के लिए यह ब्लॉग प्रविष्टि सभी मामलों का उपयोग करती है। यहां यह एक गुमनाम फ़ंक्शन शॉर्ट कट है , यह एक फ़ंक्शन के लिए एक शॉर्टकट है जो एक तर्क लेता है और उस _पर विधि को लागू करता है। अब _एक मान्य विधि नहीं है, इसलिए निश्चित रूप से आप देख रहे थे _._1या कुछ समान है, जो कि _._1फ़ंक्शन तर्क पर विधि लागू कर रहा है। _1करने के लिए _22tuples जो एक टपल की एक विशेष तत्व निकालने के तरीके हैं। उदाहरण:

val tup = ("Hallo", 33)
tup._1 // extracts "Hallo"
tup._2 // extracts 33

अब फ़ंक्शन एप्लिकेशन शॉर्टकट के लिए एक उपयोग के मामले को मान लेते हैं। ऐसे नक्शे को देखते हुए, जो पूर्णांक को स्ट्रिंग में मैप करता है:

val coll = Map(1 -> "Eins", 2 -> "Zwei", 3 -> "Drei")

वूप, एक अजीब विराम चिह्न की एक और घटना पहले से ही है। हाइफ़न और अधिक से अधिक अक्षर, जो एक दाहिने हाथ के तीर से मिलता जुलता है , एक ऑपरेटर है जो उत्पादन करता है a Tuple2। इसलिए (1, "Eins")या तो लिखने के परिणाम में कोई अंतर नहीं है 1 -> "Eins", केवल यह है कि उत्तरार्द्ध पढ़ना आसान है, विशेष रूप से मैप उदाहरण की तरह ट्यूपल्स की सूची में। यह ->कोई जादू नहीं है, यह कुछ अन्य ऑपरेटरों की तरह उपलब्ध है, क्योंकि आपके पास गुंजाइश में ऑब्जेक्ट में सभी निहित रूपांतरण हैं scala.Predef। यहां जो रूपांतरण होता है, वह है

implicit def any2ArrowAssoc [A] (x: A): ArrowAssoc[A] 

कहाँ ArrowAssocहै ->विधि है जो बनाता है Tuple2। इस प्रकार 1 -> "Eins"वास्तविक कॉल है Predef.any2ArrowAssoc(1).->("Eins")। ठीक है। अब वापस मूल प्रश्न में अंडरस्कोर वर्ण के साथ:

// lets create a sequence from the map by returning the
// values in reverse.
coll.map(_._2.reverse) // yields List(sniE, iewZ, ierD)

यहाँ अंडरस्कोर निम्न समकक्ष कोड को छोटा करता है:

coll.map(tup => tup._2.reverse)

ध्यान दें कि mapमेप की विधि फ़ंक्शन तर्क में कुंजी और मूल्य के टपल में गुजरती है। चूंकि हम केवल मूल्यों (स्ट्रिंग्स) में रुचि रखते हैं, हम उन्हें _2ट्यूपल पर विधि के साथ निकालते हैं ।


+1 करने में मुझे ->विधि को समझने में परेशानी हो रही थी लेकिन आपके वाक्य "तो लेखन के परिणाम में कोई अंतर नहीं है (1, "Eins")या 1 -> "Eins"" मुझे वाक्य रचना और इसके उपयोग को समझने में मदद मिली।
जेसी वेब

fyi

15

डैनियल और 0__ के शानदार जवाबों के अलावा, मुझे यह कहना होगा कि स्काला कुछ प्रतीकों के लिए यूनिकोड एनालॉग्स को समझता है , इसलिए इसके बजाय

for (n <- 1 to 10) n % 2 match {
  case 0 => println("even")
  case 1 => println("odd")
}

कोई लिख सकता है

for (n ← 1 to 10) n % 2 match {
  case 0 ⇒ println("even")
  case 1 ⇒ println("odd")
}

10

के बारे में ::एक और है Stackoverflow प्रविष्टि जो शामिल किया गया ::मामला। संक्षेप में, इसका उपयोग हेड एलीमेंट और टेल लिस्ट Lists' कंसेंटिंग ' द्वारा किया जाता है । यह एक ऐसा वर्ग है जो किसी कॉन्सड लिस्ट का प्रतिनिधित्व करता है और जिसका उपयोग एक्सट्रैक्टर के रूप में किया जा सकता है, लेकिन आमतौर पर यह एक सूची पर एक विधि है । जैसा कि पाब्लो फर्नांडीज बताते हैं, चूंकि यह एक बृहदान्त्र में समाप्त होता है, यह सही सहयोगी है , जिसका अर्थ है कि विधि कॉल का रिसीवर दाईं ओर है, और ऑपरेटर के बाईं ओर तर्क है। इस तरह आप सुंदर ढंग से consing व्यक्त कर सकते हैं के रूप में prepending एक मौजूदा सूची में एक नया सिर तत्व:

val x = 2 :: 3 :: Nil  // same result as List(2, 3)
val y = 1 :: x         // yields List(1, 2, 3)

इसके बराबर है

val x = Nil.::(3).::(2) // successively prepend 3 and 2 to an empty list
val y = x.::(1)         // then prepend 1

चिमटा वस्तु के रूप में उपयोग इस प्रकार है:

def extract(l: List[Int]) = l match {
   case Nil          => "empty"
   case head :: Nil  => "exactly one element (" + head + ")"
   case head :: tail => "more than one element"
}

extract(Nil)          // yields "empty"
extract(List(1))      // yields "exactly one element (1)"
extract(List(2, 3))   // yields "more than one element"

यह यहां एक ऑपरेटर की तरह दिखता है, लेकिन यह वास्तव में लेखन का एक और (अधिक पठनीय) तरीका है

def extract2(l: List[Int]) = l match {
   case Nil            => "empty"
   case ::(head, Nil)  => "exactly one element (" + head + ")"
   case ::(head, tail) => "more than one element"
}

आप इस पोस्ट में एक्सट्रैक्टर्स के बारे में अधिक पढ़ सकते हैं ।


9

<=ठीक उसी तरह जैसे आप इसे "पढ़ेंगे": 'इससे ​​कम या बराबर'। तो यह एक गणितीय ऑपरेटर है, की सूची में <((से कम है?), >((इससे बड़ा है?), ==(बराबर ); ((बराबर !=नहीं है?); <=(या इससे कम है?)?>= (इससे अधिक है) या बराबर;)।

यह नहीं होना चाहिए उलझन के साथ =>जो एक तरह का है डबल दाएँ हाथ के तीर , एक समारोह के शरीर से तर्क सूची अलग करने के लिए और पैटर्न मिलान (एक में परीक्षण हालत अलग करने के लिए प्रयोग किया जाता है caseशरीर निष्पादित से ब्लॉक) जब मिलान होता है । आप मेरे पिछले दो उत्तरों में इसका उदाहरण देख सकते हैं। सबसे पहले, समारोह का उपयोग करें:

coll.map(tup => tup._2.reverse)

जो पहले से ही संक्षिप्त है क्योंकि प्रकार छोड़े गए हैं। फॉलो फंक्शन होगा

// function arguments         function body
(tup: Tuple2[Int, String]) => tup._2.reverse

और पैटर्न मिलान का उपयोग करें:

def extract2(l: List[Int]) = l match {
   // if l matches Nil    return "empty"
   case Nil            => "empty"
   // etc.
   case ::(head, Nil)  => "exactly one element (" + head + ")"
   // etc.
   case ::(head, tail) => "more than one element"
}

4
इस भ्रम से बचने के लिए मैंने सही डबल एरो (\ U21D2) के लिए यूनिकोड वर्णों का उपयोग शुरू करने का फैसला किया, जो कि एकल "राइट" एरो (\ U2192), और लेफ्ट सिंगल "एरो (\ U2190)" में है। स्काला इसका समर्थन करता है लेकिन मैं तब तक थोड़ा सशंकित था जब तक कि मैंने थोड़ी देर के लिए कोशिश नहीं की। बस अपने सिस्टम पर एक सुविधाजनक कुंजी संयोजन के लिए इन कोड बिंदुओं को कैसे बांधें, यह देखें। OS X पर यह वास्तव में आसान था।
कॉनर डॉयल

5

मैं बड़ी आईडी परियोजनाओं को समझने के लिए एक आधुनिक आईडीई को महत्वपूर्ण मानता हूं। चूंकि ये ऑपरेटर भी विधियां हैं, अंतर्मुखी विचार में मैं सिर्फ परिभाषाओं में नियंत्रण-क्लिक या नियंत्रण-बी करता हूं।

आप एक कंट्रोल ऑपरेटर में दाईं ओर क्लिक को नियंत्रित कर सकते हैं (: :) और स्कैला जवादोक में यह कहते हुए कि "इस सूची की शुरुआत में एक तत्व जोड़ता है।" उपयोगकर्ता-परिभाषित ऑपरेटरों में, यह और भी महत्वपूर्ण हो जाता है, क्योंकि उन्हें हार्ड-टू-फाइंड इम्पीकिट्स में परिभाषित किया जा सकता है ... आपका आईडीई जानता है कि निहित को कहां परिभाषित किया गया था।


4

बस दूसरे उत्कृष्ट उत्तरों में जोड़ रहे हैं। स्काला दो बार आलोचनात्मक प्रतीकात्मक ऑपरेटरों, /:( foldLeft) और :\( foldRight) ऑपरेटरों की पेशकश करता है, जो पहले सही सहयोगी हैं। तो निम्नलिखित तीन कथन समान हैं:

( 1 to 100 ).foldLeft( 0, _+_ )
( 1 to 100 )./:( 0 )( _+_ )
( 0 /: ( 1 to 100 ) )( _+_ )

जैसा कि ये तीन हैं:

( 1 to 100 ).foldRight( 0, _+_ )
( 1 to 100 ).:\( 0 )( _+_ )
( ( 1 to 100 ) :\ 0 )( _+_ )

2

जावा के अंकगणित ऑपरेटरों में स्काला को अधिकांश भाग विरासत में मिला है । इसमें बिटवाइज़-या |(सिंगल पाइप कैरेक्टर), बिटवाइज़-एंड &, बिटवाइज़-एक्सक्लूसिव-या ^, लॉजिकल (बूलियन) या ||(दो पाइप कैरेक्टर) और लॉजिकल-एंड शामिल हैं &&। दिलचस्प है, आप एकल चरित्र ऑपरेटरों का उपयोग कर सकते हैं boolean, इसलिए java'ish तार्किक ऑपरेटर पूरी तरह से बेमानी हैं:

true && true   // valid
true & true    // valid as well

3 & 4          // bitwise-and (011 & 100 yields 000)
3 && 4         // not valid

जैसा कि एक अन्य पोस्ट में बताया गया है, एक पुष्टिकरण चिन्ह में समाप्त होने वाली कॉल =को हल किया जाता है (यदि उस नाम के साथ एक विधि मौजूद नहीं है!) एक पुनर्मूल्यांकन द्वारा:

var x = 3
x += 1         // `+=` is not a method in `int`, Scala makes it `x = x + 1`

यह 'डबल-चेक' संभव बनाता है, आसानी से एक अपरिवर्तनीय संग्रह के लिए एक परिवर्तनशील विनिमय करने के लिए:

val m = collection.mutable.Set("Hallo")   // `m` a val, but holds mutable coll
var i = collection.immutable.Set("Hallo") // `i` is a var, but holds immutable coll

m += "Welt" // destructive call m.+=("Welt")
i += "Welt" // re-assignment i = i + "Welt" (creates a new immutable Set)

4
PS बूलियन्स पर एकल बनाम दोहरे चरित्र ऑपरेटरों का उपयोग करने के बीच एक अंतर है - पूर्व उत्सुक है (सभी शर्तों का मूल्यांकन किया जाता है), बाद वाला जल्दी समाप्त हो जाता है यदि परिणामस्वरूप बूलियन जाना जाता है: true | { println( "Icke" ); true }⇒ प्रिंट! true || { println( "Icke" ); true }⇒ प्रिंट नहीं करता है !
0__
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.