জাভাস্ক্রিপ্ট: একটি ফাংশন ক্লোন করুন


115

জাভাস্ক্রিপ্টে কোনও ফাংশন ক্লোন করার দ্রুততম উপায় কী (এর বৈশিষ্ট্যগুলি সহ বা ছাড়াই)?

দুটি বিকল্প মাথায় আসবে eval(func.toString())এবং function() { return func.apply(..) }। তবে আমি উদ্বিগ্ন এবং মোড়কের পারফরম্যান্স সম্পর্কে চিন্তিত হয়েছি স্ট্যাকটি আরও খারাপ করে দেবে এবং সম্ভবত অনেক কিছু প্রয়োগ করা হয়েছে বা ইতিমধ্যে মোড়কের জন্য প্রয়োগ করা থাকলে সম্ভবত পারফরম্যান্স হ্রাস পাবে।

new Function(args, body) দেখতে দুর্দান্ত লাগছে, তবে জেএসে কোনও জেএস পার্সার ছাড়াই আমি কীভাবে নির্ভরযোগ্য বিদ্যমান ফাংশনটি আরগস এবং বডিতে বিভক্ত করতে পারি?

আগাম ধন্যবাদ.

আপডেট: আমি যা বলতে চাইছি তা করতে সক্ষম

var funcB = funcA.clone(); // where clone() is my extension
funcB.newField = {...};    // without affecting funcA

আপনি কী বোঝাতে চেয়েছেন আপনি একটি উদাহরণ দিতে পারেন।
জোশবার্ক

অবশ্যই, যোগ করা হয়েছে। (15charsrequided)
Andrey Shchekin

আমি নিশ্চিত নই, তবে = new your_function () অনুলিপি করতে পারলাম; কাজ করে?
সেভেজম্যান

1
আমার মনে হয় না, এটি কনস্ট্রাক্টর হিসাবে ফাংশন ব্যবহার করে একটি উদাহরণ তৈরি করবে
আন্দ্রে শেকकिन

উত্তর:


54

এটা চেষ্টা কর:

var x = function() {
    return 1;
};

var t = function(a,b,c) {
    return a+b+c;
};


Function.prototype.clone = function() {
    var that = this;
    var temp = function temporary() { return that.apply(this, arguments); };
    for(var key in this) {
        if (this.hasOwnProperty(key)) {
            temp[key] = this[key];
        }
    }
    return temp;
};

alert(x === x.clone());
alert(x() === x.clone()());

alert(t === t.clone());
alert(t(1,1,1) === t.clone()(1,1,1));
alert(t.clone()(1,1,1));

ঠিক আছে, তাহলে কি একমাত্র উপায়? আমি এটির উপর কিছুটা উন্নতি করব যাতে এটি দু'বার কল করার সময় দু'বার মুড়ে না যায় তবে অন্যথায়, ঠিক আছে।
অ্যান্ড্রে শেকিন 19

আর্গুমেন্ট সহজেই পাস করার জন্য প্রয়োগ করা হয়। এছাড়াও, এটি এমন উদাহরণগুলির জন্য কাজ করবে যেখানে আপনি কোনও নির্মাণকারীর ক্লোন করতে চান।
জেরেডে

6
হ্যাঁ, আমি মূল পোস্টে প্রয়োগ সম্পর্কে লিখেছিলাম। সমস্যাটি হ'ল এর মতো মোড়ক ফাংশন এর নামটি নষ্ট করে দেয় এবং অনেক ক্লোন পরে ধীর হয়ে যাবে।
আন্দ্রে শেকकिन

কমপক্ষে এইভাবে .name সম্পত্তিকে প্রভাবিত করার একটি উপায় রয়েছে বলে মনে হচ্ছে: ফাংশন ফা () f} var fb = ফাংশন () {ফা.এপ্লি (এটি, যুক্তি); }; অবজেক্ট.ডিফাইনপ্রোপার্টি (fb, {নাম: {মান: 'fb'}});
কিলরোয়

109

এখানে একটি আপডেট উত্তর

var newFunc = oldFunc.bind({}); //clones the function with '{}' acting as it's new 'this' parameter

তবে .bindজাভাস্ক্রিপ্টের একটি আধুনিক (> = iE9) বৈশিষ্ট্য ( এমডিএন এর সাথে সামঞ্জস্যপূর্ণ কাজের সাথে )

মন্তব্য

  1. এটা তোলে ক্লোন নয় ফাংশন বস্তুর অতিরিক্ত সংযুক্ত বৈশিষ্ট্য , সহ প্রোটোটাইপ সম্পত্তি। ক্রেডিট @jchook

  2. এই চলকটি নতুন ফাংশনটি বাইন্ড (), এমনকি নতুন ফাংশন প্রয়োগ () কলগুলিতে দেওয়া যুক্তির সাথে আটকে রয়েছে। কৃতিত্ব @ কেভিনকে

function oldFunc() {
  console.log(this.msg);
}
var newFunc = oldFunc.bind({ msg: "You shall not pass!" }); // this object is binded
newFunc.apply({ msg: "hello world" }); //logs "You shall not pass!" instead
  1. বাউন্ড ফাংশন অবজেক্ট, উদাহরণস্বরূপ newFunc / oldFunc কে একই হিসাবে বিবেচনা করে। ক্রেডিট @Christopher
(new newFunc()) instanceof oldFunc; //gives true
(new oldFunc()) instanceof newFunc; //gives true as well
newFunc == oldFunc; //gives false however

2
নোট করুন যে উদাহরণগুলির newFuncজন্য নিজস্ব প্রোটোটাইপ থাকবে না new newFunc, যখন oldFuncহবে।
jchook

1
প্রাকটিক্যাল ডাউনসাইড: উদাহরণস্বরূপ নতুনফাঙ্ক এবং ওল্ডফ্যাঙ্কের মধ্যে পার্থক্য করতে সক্ষম হবে না
ক্রিস্টোফার

1
@ ক্রিস্টোফারস্বাসী: কার্যকারিতা বাড়ানোর সময় এটি আসলে একটি উত্সাহও হতে পারে। তবে হায়, ভালভাবে না বুঝলে এটি বিভ্রান্তিকর হবে (উত্তরে যুক্ত করা হয়েছে)
পিকোক্রিটর

এই উত্তরের একটি বড় সমস্যা হ'ল একবার বেঁধে ফেললে, আপনি দ্বিতীয় বার বাঁধতে পারবেন না। পরবর্তীগুলিতে প্রয়োগ করা কলগুলি 'এই' অবজেক্টটি পাসও উপেক্ষা করে। উদাহরণ: var f = function() { console.log('hello ' + this.name) }যখন {name: 'Bob'}'হ্যালো বব' প্রিন্ট করতে বাধ্য । f.apply({name: 'Sam'})'এই' বস্তুটি উপেক্ষা করে 'হ্যালো বব' মুদ্রণও করবে।
কেভিন মুনি

1
অন্য একটি প্রান্তের কেসটি খেয়াল করুন: কমপক্ষে ভি 8 (এবং সম্ভবত অন্যান্য ইঞ্জিন) এ, এটি Function.prototype.toString () এর আচরণ পরিবর্তন করে changes আবদ্ধ ফাংশনে .toString () কল করা আপনাকে function () { [native code] }পুরো ফাংশনের সামগ্রীর পরিবর্তে স্ট্রিং দেয়।
গ্ল্যাডস্টোনকিপ

19

জারেডের উত্তরের কিছুটা উন্নত সংস্করণ এখানে। আপনি যত বেশি ক্লোন করেছেন এটি গভীরভাবে নেস্টেড ফাংশনগুলির সাথে শেষ হবে না। এটি সর্বদা আসল কল করে।

Function.prototype.clone = function() {
    var cloneObj = this;
    if(this.__isClone) {
      cloneObj = this.__clonedFrom;
    }

    var temp = function() { return cloneObj.apply(this, arguments); };
    for(var key in this) {
        temp[key] = this[key];
    }

    temp.__isClone = true;
    temp.__clonedFrom = cloneObj;

    return temp;
};

এছাড়াও, পিকোক্রিটর দ্বারা প্রদত্ত আপডেট হওয়া জবাবের প্রতিক্রিয়া হিসাবে, এটি লক্ষণীয় যে bind()জাভাস্ক্রিপ্ট ১.৮.৫ এ যুক্ত ফাংশনটিতে জ্যারেডের উত্তরটির সমান সমস্যা রয়েছে - এটি প্রতিবার ব্যবহৃত হওয়ার সাথে সাথে এটি নীড় বাঁধা রাখবে।


2019+ এ, __properties এর পরিবর্তে প্রতীক () ব্যবহার করা ভাল।
আলেকজান্ডার মিলস

10

অদ্ভুত কিন্তু এখনও উপরে প্রশ্ন কর্মক্ষমতা বিষয় উত্তর খুঁজে পেতে অসমর্থ হচ্ছে, আমি এই লিখেছিলেন সারকথা পরীক্ষা nodejs উভয় কর্মক্ষমতা এবং সব উপস্থাপন (এবং রান) সমাধান নির্ভরযোগ্যতা জন্য।

আমি একটি ক্লোন ফাংশন তৈরির ও প্রাচীরের সময়কে একটি ক্লোন নির্বাহের সাথে তুলনা করেছি। দৃ errors় মন্তব্যগুলির সাথে দৃser়তা ত্রুটির সাথে ফলাফলগুলি অন্তর্ভুক্ত।

আমার দুটি সেন্ট প্লাস (লেখকের পরামর্শের ভিত্তিতে):

ক্লোন0 সেন্ট (দ্রুত তবে কৃপণ):

Function.prototype.clone = function() {
  var newfun;
  eval('newfun=' + this.toString());
  for (var key in this)
    newfun[key] = this[key];
  return newfun;
};

ক্লোন ৪৪ শতাংশ (ধীর কিন্তু তবে যারা ওয়াল অপছন্দ করেন না তাদের উদ্দেশ্যে) কেবল তাদের এবং তাদের পূর্বপুরুষদের জন্য পরিচিত):

Function.prototype.clone = function() {
  var newfun = new Function('return ' + this.toString())();
  for (var key in this)
    newfun[key] = this[key];
  return newfun;
};

পারফরম্যান্সের ক্ষেত্রে, যদি ওপল / নতুন ফাংশনটি মোড়কের সমাধানের চেয়ে ধীর হয় (এবং এটি সত্যিকারের ফাংশনের দেহের আকারের উপর নির্ভর করে), এটি আপনাকে খালি ফাংশন ক্লোন দেয় (এবং আমি সম্পত্তিগুলির সাথে সত্যের অগভীর ক্লোনটি কিন্তু অংশীদারিহীন রাষ্ট্রের অর্থ) অপ্রয়োজনীয় ঝাপসা ছাড়াই লুকানো বৈশিষ্ট্য, মোড়ক ফাংশন এবং স্ট্যাক সহ সমস্যা সহ

এছাড়াও আপনাকে সর্বদা একটি গুরুত্বপূর্ণ বিষয় বিবেচনা করা উচিত: কম কোড, ভুলগুলির জন্য কম স্থান।

ইওল / নতুন ফাংশনটি ব্যবহারের ক্ষতিটি হ'ল ক্লোন এবং মূল ফাংশনটি বিভিন্ন স্কোপে কাজ করবে। স্কোপড ভেরিয়েবলগুলি ব্যবহার করে এমন ফাংশনগুলির সাথে এটি ভাল কাজ করবে না। বাইন্ড-জাতীয় মোড়ক ব্যবহার করে সমাধানগুলি স্কোপটি স্বাধীন।


সাবধান থাকুন যে eval এবং নতুন ফাংশন সমতুল্য নয়। স্থানীয় স্কোপে ওপালগুলি প্রকাশ করুন, তবে ফাংশনটি তা করে না। এটি ফাংশন কোডের ভিতরে থেকে অন্যান্য ভেরিয়েবল অ্যাক্সেস করতে চেষ্টা করতে সমস্যা তৈরি করতে পারে। বিস্তৃত ব্যাখ্যার জন্য পারফেকশন কিলস / গ্লোবাল-আভাল- কি-are-the-options দেখুন ।
পিয়ের

ডান এবং না হয় eval বা নতুন ফাংশন ব্যবহার করে আপনি ফাংশনটির আসল সুযোগের সাথে একত্রে ক্লোন করতে পারবেন না।
রয়্যালটিএম

প্রকৃতপক্ষে: একবার আপনি Object.assign(newfun.prototype, this.prototype);রিটার্ন স্টেটমেন্টের (ক্লিন সংস্করণ) আগে যুক্ত করলে, আপনার পদ্ধতিটি সর্বোত্তম উত্তর।
ভিভিক

9

এই পদ্ধতিটি কাজ করা বেশ উত্তেজনাপূর্ণ ছিল, সুতরাং এটি ফাংশন কল ব্যবহার করে কোনও ফাংশনের ক্লোন তৈরি করে।

এমডিএন ফাংশন রেফারেন্সে বর্ণিত ক্লোজারগুলি সম্পর্কে কিছু সীমাবদ্ধতা

function cloneFunc( func ) {
  var reFn = /^function\s*([^\s(]*)\s*\(([^)]*)\)[^{]*\{([^]*)\}$/gi
    , s = func.toString().replace(/^\s|\s$/g, '')
    , m = reFn.exec(s);
  if (!m || !m.length) return; 
  var conf = {
      name : m[1] || '',
      args : m[2].replace(/\s+/g,'').split(','),
      body : m[3] || ''
  }
  var clone = Function.prototype.constructor.apply(this, [].concat(conf.args, conf.body));
  return clone;
}

উপভোগ করুন।


5

সংক্ষিপ্ত এবং সাধারণ:

Function.prototype.clone = function() {
  return new Function('return ' + this.toString())();
};

1
এছাড়াও, এটি হুডের নীচে ইওালের বিভিন্ন রূপ ব্যবহার করে, যা বিভিন্ন কারণে সেরাভাবে এড়ানো হয় (এটি এখানে won'tুকবে না, এটি অন্য জায়গাগুলির অংশে coveredাকা থাকবে)।
অ্যান্ড্রু ফকনার

2
এই সমাধানটির নিজস্ব জায়গা রয়েছে (যখন আপনি কোনও ব্যবহারকারীর ফাংশন ক্লোন করছেন এবং এভালটি ব্যবহার করা হয় না সেদিকে খেয়াল রাখবেন না)
লয়েড

2
এটি কার্যকারিতাও হারায়। নতুন ফাংশনটি বাইরের স্কোপ ওয়ারগুলি বোঝায় যা নতুন স্কোপে আর নেই।
trusktr

4
const oldFunction = params => {
  // do something
};

const clonedFunction = (...args) => oldFunction(...args);

3
const clonedFunction = Object.assign(() => {}, originalFunction);

মনে রাখবেন এটি অসম্পূর্ণ। এটি থেকে বৈশিষ্ট্যগুলি অনুলিপি করবে originalFunction, তবে আপনি যখন চালাবেন তখন তা বাস্তবায়িত হবে না clonedFunction, যা অপ্রত্যাশিত is
ডেভিড ক্যালহাউন

2

এই উত্তরটি এমন লোকদের জন্য যারা কোনও ফাংশন ক্লোনিংকে তাদের পছন্দসই ব্যবহারের উত্তর হিসাবে দেখেন, তবে যাদের অনেকেই আসলে কোনও ফাংশন ক্লোন করার প্রয়োজন হয় না , কারণ তারা যা চান তা কেবল একই ফাংশনে বিভিন্ন বৈশিষ্ট্য সংযুক্ত করতে সক্ষম হতে পারে তবে কেবল এই ফাংশনটি একবার ঘোষণা করুন।

একটি ফাংশন তৈরির ফাংশন তৈরি করে এটি করুন:

function createFunction(param1, param2) {
   function doSomething() {
      console.log('in the function!');
   }
   // Assign properties to `doSomething` if desired, perhaps based
   // on the arguments passed into `param1` and `param2`. Or,
   // even return a different function from among a group of them.
   return doSomething;
};

let a = createFunction();
a.something = 1;
let b = createFunction();
b.something = 2; // does not overwrite a.something
console.log(a.something);
a();
b();

এটি ঠিক যেমনটি আপনি উল্লেখ করেছেন ঠিক তেমন নয়, তবে আপনি কীভাবে ক্লোন করতে চান তা ফাংশনটি কীভাবে ব্যবহার করতে চান তার উপর নির্ভর করে। এটি আরও মেমরি ব্যবহার করে কারণ এটি অনুরোধে একবারে ফাংশনের একাধিক অনুলিপি তৈরি করে। তবে এই কৌশলটি কোনও জটিল cloneক্রিয়াকলাপ ছাড়াই কিছু লোকের ব্যবহারের কেস সমাধান করতে পারে ।


1

শুধু ভাবছি - আপনি যখন প্রোটোটাইপগুলি রেখে কোনও ফাংশন ক্লোন করতে চান এবং আপনার ইচ্ছামত কিছুতে কোনও ফাংশন কলের সুযোগ সেট করতে পারেন কেন?

 var funcA = {};
 funcA.data = 'something';
 funcA.changeData = function(d){ this.data = d; }

 var funcB = {};
 funcB.data = 'else';

 funcA.changeData.call(funcB.data);

 alert(funcA.data + ' ' + funcB.data);

1
যদি স্বয়ংক্রিয়ভাবে ফাংশনের ক্ষেত্রগুলি পরিবর্তন করার কোনও কারণ থাকে (স্ব-অন্তর্ভুক্ত ক্যাশে, 'স্ট্যাটিক' বৈশিষ্ট্য), তবে এমন পরিস্থিতিতে আছে যখন আমি কোনও ফাংশন ক্লোন করতে চাই এবং মূলটিকে প্রভাবিত না করে এটিকে সংশোধন করতে চাই।
আন্দ্রে শেকकिन 15

আমি নিজেই ফাংশনের বৈশিষ্ট্যগুলি বোঝাতে চাইছি।
Andrey Shchekin

1
ফাংশনগুলির কোনও বৈশিষ্ট্যের মতো বৈশিষ্ট্য থাকতে পারে, সে কারণেই
রাদু সিমিনেস্কু

1

আপনি যদি ফাংশন কনস্ট্রাক্টর ব্যবহার করে কোনও ক্লোন তৈরি করতে চান তবে এর মতো কিছু কাজ করা উচিত:

_cloneFunction = function(_function){
    var _arguments, _body, _result;
    var _regexFunction = /^function[\s]+[\w]*\(([\w\s,_\$]*)?\)\{(.*)\}$/;
    var _regexArguments = /((?!=^|,)([\w\$_]))+/g;
    var _matches = _function.toString().match(_regexFunction)
    if(_matches){
        if(_matches[1]){
            _result = _matches[1].match(_regexArguments);
        }else{
            _result = [];
        }
        _result.push(_matches[2]);
    }else{
        _result = [];
    }
    var _clone = Function.apply(Function, _result);
    // if you want to add attached properties
    for(var _key in _function){
        _clone[_key] = _function[_key];
    }
    return _clone;
}

একটি সহজ পরীক্ষা:

(function(){
    var _clone, _functions, _key, _subKey;
    _functions = [
        function(){ return 'anonymous function'; }
        ,function Foo(){ return 'named function'; }
        ,function Bar(){ var a = function(){ return 'function with internal function declaration'; }; return a; }
        ,function Biz(a,boo,c){ return 'function with parameters'; }
    ];
    _functions[0].a = 'a';
    _functions[0].b = 'b';
    _functions[1].b = 'b';
    for(_key in _functions){
        _clone = window._cloneFunction(_functions[_key]);
        console.log(_clone.toString(), _clone);
        console.log('keys:');
        for(_subKey in _clone){
            console.log('\t', _subKey, ': ', _clone[_subKey]);
        }
    }
})()

এই ক্লোনগুলি কোনও বন্ধ ওভার ভেরিয়েবলের জন্য তাদের নাম এবং সুযোগ হারাবে।


1

আমি জ্যারেডের উত্তরটি আমার নিজস্ব পদ্ধতিতে উত্সাহিত করেছি:

    Function.prototype.clone = function() {
        var that = this;
        function newThat() {
            return (new that(
                arguments[0],
                arguments[1],
                arguments[2],
                arguments[3],
                arguments[4],
                arguments[5],
                arguments[6],
                arguments[7],
                arguments[8],
                arguments[9]
            ));
        }
        function __clone__() {
            if (this instanceof __clone__) {
                return newThat.apply(null, arguments);
            }
            return that.apply(this, arguments);
        }
        for(var key in this ) {
            if (this.hasOwnProperty(key)) {
                __clone__[key] = this[key];
            }
        }
        return __clone__;
    };

1) এখন এটি নির্মাণকারীদের ক্লোনিং সমর্থন করে (নতুন সাথে কল করতে পারে); সেক্ষেত্রে মূল কন্সট্রাক্টারে সমস্ত আর্গুমেন্ট পাস করার অসম্ভবতার কারণে - কেবলমাত্র 10 টি আর্গুমেন্ট লাগে (আপনি এটি আলাদা করতে পারেন)

2) সব কিছুই সঠিক সমাপ্তিতে


পরিবর্তে arguments[0], arguments[1] /*[...]*/আপনি কেবল ব্যবহার করবেন না কেন ...arguments? 1) আর্গুমেন্টের পরিমাণ সম্পর্কে এখানে কোনও নির্ভরতা নেই (এখানে 10 এর
মধ্যেই

স্প্রেড অপারেটরের ব্যবহারের সাথে, এটি অবশ্যই ফাংশনগুলির জন্য আমার ওজি ক্লোনিং পদ্ধতি হবে, অনেক অনেক ধন্যবাদ।
ভিভিক

0
function cloneFunction(Func, ...args) {
  function newThat(...args2) {
    return new Func(...args2);
  }
  function clone() {
    if (this instanceof clone) {
      return newThat(...args);
    }
    return Func.apply(this, args);
  }
  for (const key in Func) {
    if (Func.hasOwnProperty(key)) {
      clone[key] = Func[key];
    }
  }
  Object.defineProperty(clone, 'name', { value: Func.name, configurable: true })
  return clone
};

function myFunction() {
  console.log('Called Function')
}

myFunction.value = 'something';

const newFunction = cloneFunction(myFunction);

newFunction.another = 'somethingelse';

console.log('Equal? ', newFunction === myFunction);
console.log('Names: ', myFunction.name, newFunction.name);
console.log(myFunction);
console.log(newFunction);
console.log('InstanceOf? ', newFunction instanceof myFunction);

myFunction();
newFunction();

যদিও আমি কখনই এটি ব্যবহারের পরামর্শ দিচ্ছি না, আমি ভেবেছিলাম যে এটি সবচেয়ে ভাল বলে মনে হচ্ছে এমন কিছু অনুশীলন গ্রহণ করে এবং এটি কিছুটা ঠিক করে নিয়ে আরও সুনির্দিষ্ট ক্লোনটি নিয়ে আসা একটি আকর্ষণীয় সামান্য চ্যালেঞ্জ হবে। লগগুলির ফলাফল এখানে:

Equal?  false
Names:  myFunction myFunction
{ [Function: myFunction] value: 'something' }
{ [Function: myFunction] value: 'something', another: 'somethingelse' }
InstanceOf?  false
Called Function
Called Function

0
const clone = (fn, context = this) => {
  // Creates a new function, optionally preserving desired context.
  const newFn = fn.bind(context);

  // Shallow copies over function properties, if any.
  return Object.assign(newFn, fn);
}

// Usage:

// Setup the function to copy from.
const log = (...args) => console.log(...args);
log.testProperty = 1;

// Clone and make sure the function and properties are intact.
const log2 = clone(log);
log2('foo');
// -> 'foo'
log2.testProperty;
// -> 1

// Make sure tweaks to the clone function's properties don't affect the original function properties.
log2.testProperty = 2;
log2.testProperty;
// -> 2
log.testProperty;
// -> 1

এই ক্লোন ফাংশন:

  1. প্রসঙ্গ সংরক্ষণ করে।
  2. একটি মোড়ক, এবং মূল ফাংশন চালায়।
  3. ফাংশন বৈশিষ্ট্যের উপর অনুলিপি।

মনে রাখবেন যে এই সংস্করণটি কেবল অগভীর অনুলিপি সম্পাদন করে। যদি আপনার ফাংশনে বৈশিষ্ট্য হিসাবে অবজেক্ট থাকে তবে আসল অবজেক্টের রেফারেন্স সংরক্ষিত থাকে (অবজেক্ট স্প্রেড বা অবজেক্ট.সেসাইন এর মতো আচরণ)। এর অর্থ ক্লোন করা ফাংশনটিতে গভীর বৈশিষ্ট্যগুলি পরিবর্তন করা মূল ফাংশনে রেফারেন্স করা বস্তুকে প্রভাবিত করবে!

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.