जावास्क्रिप्ट इटरेटर प्रोटोकॉल समजून घेण्यासाठी आणि अंमलात आणण्यासाठी एक सर्वसमावेशक मार्गदर्शक, जे तुम्हाला उत्तम डेटा हँडलिंगसाठी कस्टम इटरेटर्स तयार करण्यास सक्षम करते.
जावास्क्रिप्ट इटरेटर प्रोटोकॉल आणि कस्टम इटरेटर्सचे रहस्य उलगडणे
जावास्क्रिप्टचा इटरेटर प्रोटोकॉल डेटा स्ट्रक्चर्समधून जाण्यासाठी (traverse) एक प्रमाणित मार्ग प्रदान करतो. हा प्रोटोकॉल समजून घेतल्याने डेव्हलपर्सना अॅरेज आणि स्ट्रिंग्स सारख्या अंगभूत इटरेबल्ससोबत कार्यक्षमतेने काम करण्यास आणि विशिष्ट डेटा स्ट्रक्चर्स व ऍप्लिकेशनच्या आवश्यकतांनुसार स्वतःचे कस्टम इटरेबल्स तयार करण्यास सक्षम करते. हा मार्गदर्शक इटरेटर प्रोटोकॉल आणि कस्टम इटरेटर्स कसे लागू करावेत याचे सर्वसमावेशक अन्वेषण प्रदान करतो.
इटरेटर प्रोटोकॉल म्हणजे काय?
इटरेटर प्रोटोकॉल हे परिभाषित करतो की एखाद्या ऑब्जेक्टवर कसे पुनरावृत्ती (iterate) करता येईल, म्हणजेच, त्याचे घटक क्रमाने कसे ऍक्सेस केले जाऊ शकतात. यात दोन भाग आहेत: इटरेबल प्रोटोकॉल आणि इटरेटर प्रोटोकॉल.
इटरेबल प्रोटोकॉल
एखाद्या ऑब्जेक्टला इटरेबल मानले जाते जर त्यात Symbol.iterator
की असलेली मेथड असेल. या मेथडने इटरेटर प्रोटोकॉलचे पालन करणारा ऑब्जेक्ट परत करणे आवश्यक आहे.
थोडक्यात, इटरेबल ऑब्जेक्टला स्वतःसाठी इटरेटर कसा तयार करायचा हे माहित असते.
इटरेटर प्रोटोकॉल
इटरेटर प्रोटोकॉल एका क्रमातून (sequence) व्हॅल्यूज कशा मिळवायच्या हे परिभाषित करतो. एखाद्या ऑब्जेक्टला इटरेटर मानले जाते जर त्यात next()
मेथड असेल जी दोन प्रॉपर्टीज असलेला ऑब्जेक्ट परत करते:
value
: क्रमातील पुढील व्हॅल्यू.done
: एक बुलियन व्हॅल्यू जी दर्शवते की इटरेटर क्रमाच्या शेवटी पोहोचला आहे की नाही. जरdone
हेtrue
असेल, तरvalue
प्रॉपर्टी वगळली जाऊ शकते.
next()
मेथड ही इटरेटर प्रोटोकॉलचा मुख्य भाग आहे. next()
ला प्रत्येक कॉल केल्यावर इटरेटर पुढे जातो आणि क्रमातील पुढील व्हॅल्यू परत करतो. जेव्हा सर्व व्हॅल्यूज परत केल्या जातात, तेव्हा next()
हे done
ला true
सेट करून ऑब्जेक्ट परत करते.
बिल्ट-इन इटरेबल्स
जावास्क्रिप्टमध्ये अनेक बिल्ट-इन डेटा स्ट्रक्चर्स आहेत जे मूळतः इटरेबल आहेत. यामध्ये यांचा समावेश आहे:
- अॅरेज (Arrays)
- स्ट्रिंग्स (Strings)
- मॅप्स (Maps)
- सेट्स (Sets)
- फंक्शनचा Arguments ऑब्जेक्ट
- टाइप्डअॅरेज (TypedArrays)
हे इटरेबल्स for...of
लूप, स्प्रेड सिंटॅक्स (...
), आणि इटरेटर प्रोटोकॉलवर अवलंबून असलेल्या इतर रचनांसोबत थेट वापरले जाऊ शकतात.
अॅरेजचे उदाहरण:
const myArray = ["apple", "banana", "cherry"];
for (const item of myArray) {
console.log(item); // Output: apple, banana, cherry
}
स्ट्रिंग्सचे उदाहरण:
const myString = "Hello";
for (const char of myString) {
console.log(char); // Output: H, e, l, l, o
}
for...of
लूप
for...of
लूप इटरेबल ऑब्जेक्ट्सवर पुनरावृत्ती करण्यासाठी एक शक्तिशाली रचना आहे. हे इटरेटर प्रोटोकॉलची गुंतागुंत आपोआप हाताळते, ज्यामुळे क्रमातील व्हॅल्यूज ऍक्सेस करणे सोपे होते.
for...of
लूपचा सिंटॅक्स आहे:
for (const element of iterable) {
// प्रत्येक घटकासाठी कार्यान्वित केला जाणारा कोड
}
for...of
लूप इटरेबल ऑब्जेक्टमधून इटरेटर मिळवतो (Symbol.iterator
वापरून), आणि इटरेटरच्या next()
मेथडला done
हे true
होईपर्यंत वारंवार कॉल करतो. प्रत्येक पुनरावृत्तीसाठी, element
व्हेरिएबलला next()
ने परत केलेल्या value
प्रॉपर्टीचे मूल्य दिले जाते.
कस्टम इटरेटर्स तयार करणे
जावास्क्रिप्टमध्ये बिल्ट-इन इटरेबल्स असले तरी, इटरेटर प्रोटोकॉलची खरी शक्ती तुमच्या स्वतःच्या डेटा स्ट्रक्चर्ससाठी कस्टम इटरेटर्स परिभाषित करण्याच्या क्षमतेमध्ये आहे. यामुळे तुमचा डेटा कसा ट्रॅव्हर्स आणि ऍक्सेस केला जाईल यावर तुम्ही नियंत्रण ठेवू शकता.
कस्टम इटरेटर कसे तयार करावे हे येथे दिले आहे:
- तुमच्या कस्टम डेटा स्ट्रक्चरचे प्रतिनिधित्व करणारा क्लास किंवा ऑब्जेक्ट परिभाषित करा.
- तुमच्या क्लास किंवा ऑब्जेक्टवर
Symbol.iterator
मेथड लागू करा. या मेथडने एक इटरेटर ऑब्जेक्ट परत केला पाहिजे. - इटरेटर ऑब्जेक्टमध्ये
next()
मेथड असणे आवश्यक आहे जीvalue
आणिdone
प्रॉपर्टीज असलेला ऑब्जेक्ट परत करते.
उदाहरण: एका साध्या रेंजसाठी इटरेटर तयार करणे
चला Range
नावाचा एक क्लास तयार करूया जो संख्यांची श्रेणी दर्शवतो. आपण रेंजमधील संख्यांवर पुनरावृत्ती करण्यासाठी इटरेटर प्रोटोकॉल लागू करू.
class Range {
constructor(start, end) {
this.start = start;
this.end = end;
}
[Symbol.iterator]() {
let currentValue = this.start;
const that = this; // इटरेटर ऑब्जेक्टमध्ये 'this' वापरण्यासाठी 'this' कॅप्चर करा
return {
next() {
if (currentValue <= that.end) {
return {
value: currentValue++,
done: false,
};
} else {
return {
value: undefined,
done: true,
};
}
},
};
}
}
const myRange = new Range(1, 5);
for (const number of myRange) {
console.log(number); // Output: 1, 2, 3, 4, 5
}
स्पष्टीकरण:
Range
क्लास त्याच्या कंस्ट्रक्टरमध्येstart
आणिend
व्हॅल्यूज घेतो.Symbol.iterator
मेथड एक इटरेटर ऑब्जेक्ट परत करते. या इटरेटर ऑब्जेक्टची स्वतःची स्थिती (currentValue
) आणि एकnext()
मेथड असते.next()
मेथड तपासते कीcurrentValue
रेंजमध्ये आहे की नाही. जर असेल, तर ती सध्याची व्हॅल्यू आणिdone
लाfalse
सेट करून ऑब्जेक्ट परत करते. तसेच पुढील पुनरावृत्तीसाठीcurrentValue
वाढवते.- जेव्हा
currentValue
हेend
व्हॅल्यूपेक्षा जास्त होते, तेव्हाnext()
मेथडdone
लाtrue
सेट करून ऑब्जेक्ट परत करते. that = this
च्या वापराकडे लक्ष द्या. कारण `next()` मेथड वेगळ्या स्कोपमध्ये (for...of
लूपद्वारे) कॉल केली जाते, `next()` मधील `this` हे `Range` इन्स्टन्सला संदर्भित करणार नाही. हे सोडवण्यासाठी, आपण `this` व्हॅल्यू (Range
इन्स्टन्स) `next()` च्या स्कोपच्या बाहेर `that` मध्ये कॅप्चर करतो आणि नंतर `next()` च्या आत `that` वापरतो.
उदाहरण: लिंक्ड लिस्टसाठी इटरेटर तयार करणे
चला आणखी एक उदाहरण विचारात घेऊया: लिंक्ड लिस्ट डेटा स्ट्रक्चरसाठी इटरेटर तयार करणे. लिंक्ड लिस्ट हा नोड्सचा एक क्रम आहे, जिथे प्रत्येक नोडमध्ये एक व्हॅल्यू आणि लिस्टमधील पुढील नोडचा संदर्भ (पॉइंटर) असतो. लिस्टमधील शेवटच्या नोडचा संदर्भ null (किंवा undefined) असतो.
class LinkedListNode {
constructor(value, next = null) {
this.value = value;
this.next = next;
}
}
class LinkedList {
constructor() {
this.head = null;
}
append(value) {
const newNode = new LinkedListNode(value);
if (!this.head) {
this.head = newNode;
return;
}
let current = this.head;
while (current.next) {
current = current.next;
}
current.next = newNode;
}
[Symbol.iterator]() {
let current = this.head;
return {
next() {
if (current) {
const value = current.value;
current = current.next;
return {
value: value,
done: false
};
} else {
return {
value: undefined,
done: true
};
}
}
};
}
}
// वापराचे उदाहरण:
const myList = new LinkedList();
myList.append("London");
myList.append("Paris");
myList.append("Tokyo");
for (const city of myList) {
console.log(city); // Output: London, Paris, Tokyo
}
स्पष्टीकरण:
LinkedListNode
क्लास लिंक्ड लिस्टमधील एका नोडचे प्रतिनिधित्व करतो, ज्यात एकvalue
आणि पुढील नोडचा संदर्भ (next
) असतो.LinkedList
क्लास स्वतः लिंक्ड लिस्टचे प्रतिनिधित्व करतो. त्यात एकhead
प्रॉपर्टी असते, जी लिस्टमधील पहिल्या नोडकडे निर्देश करते.append()
मेथड लिस्टच्या शेवटी नवीन नोड्स जोडते.Symbol.iterator
मेथड एक इटरेटर ऑब्जेक्ट तयार करते आणि परत करते. हा इटरेटर सध्या भेट दिलेल्या नोडचा (current
) मागोवा ठेवतो.next()
मेथड तपासते की सध्याचा नोड आहे की नाही (current
null नाही). जर असेल, तर ते सध्याच्या नोडमधून व्हॅल्यू मिळवते,current
पॉइंटरला पुढील नोडवर नेते, आणि व्हॅल्यूसहdone: false
असलेला ऑब्जेक्ट परत करते.- जेव्हा
current
null होतो (म्हणजे आपण लिस्टच्या शेवटी पोहोचलो आहोत), तेव्हाnext()
मेथडdone: true
असलेला ऑब्जेक्ट परत करते.
जनरेटर फंक्शन्स
जनरेटर फंक्शन्स इटरेटर्स तयार करण्याचा अधिक संक्षिप्त आणि सुंदर मार्ग प्रदान करतात. ते मागणीनुसार व्हॅल्यूज तयार करण्यासाठी yield
कीवर्ड वापरतात.
जनरेटर फंक्शन function*
सिंटॅक्स वापरून परिभाषित केले जाते.
उदाहरण: जनरेटर फंक्शन वापरून इटरेटर तयार करणे
चला Range
इटरेटरला जनरेटर फंक्शन वापरून पुन्हा लिहूया:
class Range {
constructor(start, end) {
this.start = start;
this.end = end;
}
*[Symbol.iterator]() {
for (let i = this.start; i <= this.end; i++) {
yield i;
}
}
}
const myRange = new Range(1, 5);
for (const number of myRange) {
console.log(number); // Output: 1, 2, 3, 4, 5
}
स्पष्टीकरण:
Symbol.iterator
मेथड आता एक जनरेटर फंक्शन आहे (*
कडे लक्ष द्या).- जनरेटर फंक्शनच्या आत, आपण संख्यांच्या श्रेणीवर पुनरावृत्ती करण्यासाठी
for
लूप वापरतो. yield
कीवर्ड जनरेटर फंक्शनची अंमलबजावणी थांबवतो आणि सध्याची व्हॅल्यू (i
) परत करतो. पुढच्या वेळी जेव्हा इटरेटरचीnext()
मेथड कॉल केली जाते, तेव्हा अंमलबजावणी जिथे थांबली होती (yield
स्टेटमेंटनंतर) तिथून पुन्हा सुरू होते.- जेव्हा लूप पूर्ण होतो, तेव्हा जनरेटर फंक्शन अप्रत्यक्षपणे
{ value: undefined, done: true }
परत करते, जे पुनरावृत्तीच्या समाप्तीचा संकेत देते.
जनरेटर फंक्शन्स next()
मेथड आणि done
फ्लॅग आपोआप हाताळून इटरेटर निर्मिती सुलभ करतात.
उदाहरण: फिबोनाची क्रम जनरेटर
जनरेटर फंक्शन्स वापरण्याचे आणखी एक उत्तम उदाहरण म्हणजे फिबोनाची क्रम तयार करणे:
function* fibonacciSequence() {
let a = 0;
let b = 1;
while (true) {
yield a;
[a, b] = [b, a + b]; // एकाच वेळी अपडेट करण्यासाठी डीस्ट्रक्चरिंग असाइनमेंट
}
}
const fibonacci = fibonacciSequence();
for (let i = 0; i < 10; i++) {
console.log(fibonacci.next().value); // Output: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34
}
स्पष्टीकरण:
fibonacciSequence
फंक्शन एक जनरेटर फंक्शन आहे.- ते
a
आणिb
या दोन व्हेरिएबल्सना फिबोनाची क्रमातील पहिल्या दोन संख्यांनी (0 आणि 1) सुरू करते. while (true)
लूप एक अनंत क्रम तयार करतो.yield a
स्टेटमेंटa
ची सध्याची व्हॅल्यू तयार करते.[a, b] = [b, a + b]
स्टेटमेंट डीस्ट्रक्चरिंग असाइनमेंट वापरूनa
आणिb
ला क्रमातील पुढील दोन संख्यांवर एकाच वेळी अपडेट करते.fibonacci.next().value
एक्सप्रेशन जनरेटरमधून पुढील व्हॅल्यू मिळवते. कारण जनरेटर अनंत आहे, तुम्हाला त्यातून किती व्हॅल्यूज काढायच्या आहेत हे नियंत्रित करणे आवश्यक आहे. या उदाहरणात, आपण पहिल्या 10 व्हॅल्यूज काढतो.
इटरेटर प्रोटोकॉल वापरण्याचे फायदे
- प्रमाणीकरण (Standardization): इटरेटर प्रोटोकॉल विविध डेटा स्ट्रक्चर्सवर पुनरावृत्ती करण्याचा एक सुसंगत मार्ग प्रदान करतो.
- लवचिकता (Flexibility): तुम्ही तुमच्या विशिष्ट गरजांनुसार कस्टम इटरेटर्स परिभाषित करू शकता.
- वाचनीयता (Readability):
for...of
लूप पुनरावृत्ती कोडला अधिक वाचनीय आणि संक्षिप्त बनवतो. - कार्यक्षमता (Efficiency): इटरेटर्स आळशी (lazy) असू शकतात, म्हणजे ते फक्त आवश्यकतेनुसार व्हॅल्यूज तयार करतात, ज्यामुळे मोठ्या डेटा सेटसाठी कार्यप्रदर्शन सुधारू शकते. उदाहरणार्थ, वरील फिबोनाची क्रम जनरेटर फक्त तेव्हाच पुढील व्हॅल्यूची गणना करतो जेव्हा `next()` कॉल केले जाते.
- सुसंगतता (Compatibility): इटरेटर्स स्प्रेड सिंटॅक्स आणि डीस्ट्रक्चरिंगसारख्या इतर जावास्क्रिप्ट वैशिष्ट्यांसह अखंडपणे काम करतात.
प्रगत इटरेटर तंत्रे
इटरेटर्स एकत्र करणे
तुम्ही अनेक इटरेटर्सना एकाच इटरेटरमध्ये एकत्र करू शकता. जेव्हा तुम्हाला अनेक स्रोतांमधून डेटावर एकत्रितपणे प्रक्रिया करण्याची आवश्यकता असते तेव्हा हे उपयुक्त ठरते.
function* combineIterators(...iterables) {
for (const iterable of iterables) {
for (const item of iterable) {
yield item;
}
}
}
const array1 = [1, 2, 3];
const array2 = ["a", "b", "c"];
const string1 = "XYZ";
const combined = combineIterators(array1, array2, string1);
for (const value of combined) {
console.log(value); // Output: 1, 2, 3, a, b, c, X, Y, Z
}
या उदाहरणात, `combineIterators` फंक्शन युक्तिवाद म्हणून कितीही इटरेबल्स घेते. ते प्रत्येक इटरेबलवर पुनरावृत्ती करते आणि प्रत्येक आयटम yield करते. परिणामी एकच इटरेटर मिळतो जो सर्व इनपुट इटरेबल्समधील सर्व व्हॅल्यूज तयार करतो.
इटरेटर्सना फिल्टर आणि ट्रान्सफॉर्म करणे
तुम्ही असे इटरेटर्स देखील तयार करू शकता जे दुसऱ्या इटरेटरद्वारे तयार केलेल्या व्हॅल्यूजला फिल्टर किंवा ट्रान्सफॉर्म करतात. यामुळे तुम्हाला डेटावर पाइपलाइनमध्ये प्रक्रिया करता येते, प्रत्येक व्हॅल्यू तयार होताना त्यावर विविध ऑपरेशन्स लागू करता येतात.
function* filterIterator(iterable, predicate) {
for (const item of iterable) {
if (predicate(item)) {
yield item;
}
}
}
function* mapIterator(iterable, transform) {
for (const item of iterable) {
yield transform(item);
}
}
const numbers = [1, 2, 3, 4, 5, 6];
const evenNumbers = filterIterator(numbers, (x) => x % 2 === 0);
const squaredEvenNumbers = mapIterator(evenNumbers, (x) => x * x);
for (const value of squaredEvenNumbers) {
console.log(value); // Output: 4, 16, 36
}
येथे, `filterIterator` एक इटरेबल आणि एक प्रेडिकेट फंक्शन घेते. ते फक्त त्या आयटम्सना yield करते ज्यासाठी प्रेडिकेट `true` परत करते. `mapIterator` एक इटरेबल आणि एक ट्रान्सफॉर्म फंक्शन घेते. ते प्रत्येक आयटमवर ट्रान्सफॉर्म फंक्शन लागू केल्याचा परिणाम yield करते.
वास्तविक-जगातील अनुप्रयोग
इटरेटर प्रोटोकॉल जावास्क्रिप्ट लायब्ररीज आणि फ्रेमवर्कमध्ये मोठ्या प्रमाणावर वापरला जातो, आणि तो विविध वास्तविक-जगातील अनुप्रयोगांमध्ये मौल्यवान आहे, विशेषतः मोठ्या डेटासेट किंवा असिंक्रोनस ऑपरेशन्स हाताळताना.
- डेटा प्रोसेसिंग (Data Processing): इटरेटर्स मोठ्या डेटासेटवर कार्यक्षमतेने प्रक्रिया करण्यासाठी उपयुक्त आहेत, कारण ते तुम्हाला संपूर्ण डेटासेट मेमरीमध्ये लोड न करता डेटाच्या तुकड्यांसह काम करण्याची परवानगी देतात. कल्पना करा की तुम्ही ग्राहक डेटा असलेली एक मोठी CSV फाईल पार्स करत आहात. एक इटरेटर तुम्हाला संपूर्ण फाईल एकाच वेळी मेमरीमध्ये लोड न करता प्रत्येक पंक्तीवर प्रक्रिया करण्याची परवानगी देतो.
- असिंक्रोनस ऑपरेशन्स (Asynchronous Operations): इटरेटर्सचा वापर असिंक्रोनस ऑपरेशन्स हाताळण्यासाठी केला जाऊ शकतो, जसे की API मधून डेटा आणणे. तुम्ही डेटा उपलब्ध होईपर्यंत अंमलबजावणी थांबवण्यासाठी जनरेटर फंक्शन्स वापरू शकता आणि नंतर पुढील व्हॅल्यूसह पुन्हा सुरू करू शकता.
- कस्टम डेटा स्ट्रक्चर्स (Custom Data Structures): विशिष्ट ट्रॅव्हर्सल आवश्यकतांसह कस्टम डेटा स्ट्रक्चर्स तयार करण्यासाठी इटरेटर्स आवश्यक आहेत. एक ट्री डेटा स्ट्रक्चर विचारात घ्या. तुम्ही ट्रीला विशिष्ट क्रमाने (उदा. डेप्थ-फर्स्ट किंवा ब्रेथ-फर्स्ट) ट्रॅव्हर्स करण्यासाठी कस्टम इटरेटर लागू करू शकता.
- गेम डेव्हलपमेंट (Game Development): गेम डेव्हलपमेंटमध्ये, इटरेटर्सचा वापर गेम ऑब्जेक्ट्स, पार्टिकल इफेक्ट्स आणि इतर डायनॅमिक घटकांचे व्यवस्थापन करण्यासाठी केला जाऊ शकतो.
- यूजर इंटरफेस लायब्ररीज (User Interface Libraries): अनेक UI लायब्ररीज अंतर्निहित डेटा बदलांवर आधारित घटकांना कार्यक्षमतेने अपडेट आणि रेंडर करण्यासाठी इटरेटर्सचा वापर करतात.
सर्वोत्तम पद्धती
Symbol.iterator
योग्यरित्या लागू करा: तुमचीSymbol.iterator
मेथड इटरेटर प्रोटोकॉलचे पालन करणारा इटरेटर ऑब्जेक्ट परत करत असल्याची खात्री करा.done
फ्लॅग अचूकपणे हाताळा:done
फ्लॅग पुनरावृत्तीच्या समाप्तीचा संकेत देण्यासाठी महत्त्वपूर्ण आहे. तुमच्याnext()
मेथडमध्ये ते योग्यरित्या सेट केल्याची खात्री करा.- जनरेटर फंक्शन्स वापरण्याचा विचार करा: जनरेटर फंक्शन्स इटरेटर्स तयार करण्याचा अधिक संक्षिप्त आणि वाचनीय मार्ग प्रदान करतात.
next()
मध्ये साइड इफेक्ट्स टाळा:next()
मेथडने प्रामुख्याने पुढील व्हॅल्यू मिळवणे आणि इटरेटरची स्थिती अपडेट करण्यावर लक्ष केंद्रित केले पाहिजे.next()
मध्ये गुंतागुंतीच्या ऑपरेशन्स किंवा साइड इफेक्ट्स करणे टाळा.- तुमच्या इटरेटर्सची कसून चाचणी करा: तुमचे कस्टम इटरेटर्स योग्यरित्या वागत आहेत याची खात्री करण्यासाठी त्यांची विविध डेटा सेट्स आणि परिस्थितींसह चाचणी करा.
निष्कर्ष
जावास्क्रिप्ट इटरेटर प्रोटोकॉल डेटा स्ट्रक्चर्समधून जाण्यासाठी एक शक्तिशाली आणि लवचिक मार्ग प्रदान करतो. इटरेबल आणि इटरेटर प्रोटोकॉल समजून घेऊन, आणि जनरेटर फंक्शन्सचा फायदा घेऊन, तुम्ही तुमच्या विशिष्ट गरजांनुसार कस्टम इटरेटर्स तयार करू शकता. यामुळे तुम्हाला डेटासोबत कार्यक्षमतेने काम करता येते, कोडची वाचनीयता सुधारते, आणि तुमच्या ऍप्लिकेशन्सचे कार्यप्रदर्शन वाढवते. इटरेटर्सवर प्रभुत्व मिळवल्याने जावास्क्रिप्टच्या क्षमतांची अधिक सखोल समज मिळते आणि तुम्हाला अधिक सुंदर आणि कार्यक्षम कोड लिहिण्यास सक्षम करते.