আমি বিশ্বাস করি যে ধারাবাহিকতা কলব্যাকের একটি বিশেষ ক্ষেত্রে। একটি ফাংশন যে কোনও সংখ্যক কার্য, যে কোনও সংখ্যক কলব্যাক করতে পারে। উদাহরণ স্বরূপ:
var array = [1, 2, 3];
forEach(array, function (element, array, index) {
array[index] = 2 * element;
});
console.log(array);
function forEach(array, callback) {
var length = array.length;
for (var i = 0; i < length; i++)
callback(array[i], array, i);
}
তবে যদি কোনও ফাংশন অন্য ফাংশনটিকে শেষ কাজ হিসাবে ডেকে তোলে তবে দ্বিতীয় ফাংশনটিকে প্রথমটির ধারাবাহিকতা বলা হয়। উদাহরণ স্বরূপ:
var array = [1, 2, 3];
forEach(array, function (element, array, index) {
array[index] = 2 * element;
});
console.log(array);
function forEach(array, callback) {
var length = array.length;
// This is the last thing forEach does
// cont is a continuation of forEach
cont(0);
function cont(index) {
if (index < length) {
callback(array[index], array, index);
// This is the last thing cont does
// cont is a continuation of itself
cont(++index);
}
}
}
যদি কোনও ফাংশন অন্য ফাংশনটিকে শেষ কাজ হিসাবে কল করে তবে এটি একটি লেজ কল বলে। স্কিমের মতো কিছু ভাষা পুচ্ছ কল অপ্টিমাইজেশান সম্পাদন করে। এর অর্থ এই যে টেল কলটি কোনও ফাংশন কলের পুরো ওভারহেড ব্যয় করে না। পরিবর্তে এটি একটি সরল গোটো হিসাবে প্রয়োগ করা হয়েছে (কলিং ফাংশনের স্ট্যাক ফ্রেমের সাথে লেজ কলটির স্ট্যাক ফ্রেম প্রতিস্থাপন করা হয়েছে)।
বোনাস : ধারাবাহিকতা পাস করার শৈলীতে এগিয়ে চলছে। নিম্নলিখিত প্রোগ্রাম বিবেচনা করুন:
console.log(pythagoras(3, 4));
function pythagoras(x, y) {
return x * x + y * y;
}
এখন যদি প্রতিটি ক্রিয়াকলাপ (যোগ, গুণ, ইত্যাদি সহ) ফাংশন আকারে লিখিত হয় তবে আমাদের তা হবে:
console.log(pythagoras(3, 4));
function pythagoras(x, y) {
return add(square(x), square(y));
}
function square(x) {
return multiply(x, x);
}
function multiply(x, y) {
return x * y;
}
function add(x, y) {
return x + y;
}
তদতিরিক্ত যদি আমাদের কোনও মান ফেরত না দেওয়া হয় তবে আমাদের ক্রমাগত নিম্নোক্ত ব্যবহার করতে হবে:
pythagoras(3, 4, console.log);
function pythagoras(x, y, cont) {
square(x, function (x_squared) {
square(y, function (y_squared) {
add(x_squared, y_squared, cont);
});
});
}
function square(x, cont) {
multiply(x, x, cont);
}
function multiply(x, y, cont) {
cont(x * y);
}
function add(x, y, cont) {
cont(x + y);
}
প্রোগ্রামিংয়ের এই স্টাইলটিতে যাতে আপনাকে মানগুলি ফেরত দেওয়া যায় না (এবং তাই আপনাকে প্রায় পাশের ধারাবাহিকতা অবলম্বন করতে হবে) বলা হয় ধারাবাহিকতা পাসিং স্টাইল।
ধারাবাহিকতা পাস করার শৈলীতে দুটি সমস্যা রয়েছে:
- ধারাবাহিকতা পেরিয়ে যাওয়া কল স্ট্যাকের আকার বাড়িয়ে তোলে। আপনি যদি স্কিমের মতো ভাষা ব্যবহার না করেন যা লেজ কলগুলি সরিয়ে দেয় আপনি স্ট্যাকের জায়গাগুলির বাইরে চলে যাওয়ার ঝুঁকিপূর্ণ হবেন।
- নেস্টেড ফাংশনগুলি লিখতে এটি ব্যথা হয়।
অবিচ্ছিন্নভাবে ধারাবাহিকতা কল করে প্রথম সমস্যাটি জাভাস্ক্রিপ্টে সহজেই সমাধান করা যেতে পারে। অবিচ্ছিন্নভাবে ধারাবাহিকতা ডেকে ফাংশনটি ধারাবাহিকতা বলার আগেই ফিরে আসে। সুতরাং কল স্ট্যাকের আকার বাড়েনি:
Function.prototype.async = async;
pythagoras.async(3, 4, console.log);
function pythagoras(x, y, cont) {
square.async(x, function (x_squared) {
square.async(y, function (y_squared) {
add.async(x_squared, y_squared, cont);
});
});
}
function square(x, cont) {
multiply.async(x, x, cont);
}
function multiply(x, y, cont) {
cont.async(x * y);
}
function add(x, y, cont) {
cont.async(x + y);
}
function async() {
setTimeout.bind(null, this, 0).apply(null, arguments);
}
দ্বিতীয় সমস্যাটি সাধারণত একটি ফাংশন ব্যবহার করে সমাধান করা হয় call-with-current-continuation
যা প্রায়শই সংক্ষেপিত হয় callcc
। দুর্ভাগ্যক্রমে callcc
জাভাস্ক্রিপ্টে পুরোপুরি প্রয়োগ করা যায় না, তবে এর বেশিরভাগ ব্যবহারের ক্ষেত্রে আমরা একটি প্রতিস্থাপন ফাংশন লিখতে পারি:
pythagoras(3, 4, console.log);
function pythagoras(x, y, cont) {
var x_squared = callcc(square.bind(null, x));
var y_squared = callcc(square.bind(null, y));
add(x_squared, y_squared, cont);
}
function square(x, cont) {
multiply(x, x, cont);
}
function multiply(x, y, cont) {
cont(x * y);
}
function add(x, y, cont) {
cont(x + y);
}
function callcc(f) {
var cc = function (x) {
cc = x;
};
f(cc);
return cc;
}
callcc
ফাংশন একটি ফাংশন লাগে f
এবং তা প্রযোজ্য current-continuation
(যেমন সংক্ষিপ্ত cc
)। current-continuation
ধারাবাহিকতায় ফাংশন যা থেকে কল পর ফাংশন বডির বাকি আপ গোপন নেই callcc
।
ফাংশনটির প্রধান অংশটি বিবেচনা করুন pythagoras
:
var x_squared = callcc(square.bind(null, x));
var y_squared = callcc(square.bind(null, y));
add(x_squared, y_squared, cont);
current-continuation
সেকেন্ডের callcc
হল:
function cc(y_squared) {
add(x_squared, y_squared, cont);
}
একইভাবে current-continuation
প্রথমটিরটি callcc
হ'ল:
function cc(x_squared) {
var y_squared = callcc(square.bind(null, y));
add(x_squared, y_squared, cont);
}
যেহেতু current-continuation
প্রথমটির callcc
মধ্যে অন্যটি রয়েছে callcc
এটি অবশ্যই ধারাবাহিকতা পাসিং স্টাইলে রূপান্তর করতে হবে:
function cc(x_squared) {
square(y, function cc(y_squared) {
add(x_squared, y_squared, cont);
});
}
সুতরাং মূলত callcc
যৌক্তিকভাবে পুরো ফাংশন বডিটিকে আমরা যা থেকে শুরু করেছিলাম তা ফিরিয়ে দেয় (এবং সেই নামহীন ফাংশনগুলির নাম দেয় cc
)। কলসিসির এই বাস্তবায়নটি ব্যবহার করে পাইথাগোরাস ফাংশনটি তখন হয়ে যায়:
function pythagoras(x, y, cont) {
callcc(function(cc) {
square(x, function (x_squared) {
square(y, function (y_squared) {
add(x_squared, y_squared, cont);
});
});
});
}
আবার আপনি callcc
জাভাস্ক্রিপ্টে প্রয়োগ করতে পারবেন না , তবে আপনি এটি জাভাস্ক্রিপ্টে ধারাবাহিকতা পাস করার শৈলীটি নিম্নলিখিতভাবে প্রয়োগ করতে পারেন:
Function.prototype.async = async;
pythagoras.async(3, 4, console.log);
function pythagoras(x, y, cont) {
callcc.async(square.bind(null, x), function cc(x_squared) {
callcc.async(square.bind(null, y), function cc(y_squared) {
add.async(x_squared, y_squared, cont);
});
});
}
function square(x, cont) {
multiply.async(x, x, cont);
}
function multiply(x, y, cont) {
cont.async(x * y);
}
function add(x, y, cont) {
cont.async(x + y);
}
function async() {
setTimeout.bind(null, this, 0).apply(null, arguments);
}
function callcc(f, cc) {
f.async(cc);
}
ফাংশনটি callcc
জটিল নিয়ন্ত্রণ প্রবাহ কাঠামো যেমন ট্রাই-ক্যাচ ব্লক, কর্টিনস, জেনারেটর, ফাইবার ইত্যাদি বাস্তবায়নের জন্য ব্যবহার করা যেতে পারে