সিপিএস, বা ধারাবাহিকতা-পাসিং শৈলী, প্রোগ্রামগুলির জন্য একটি মধ্যবর্তী উপস্থাপনা, বিশেষ করে কার্যকরী প্রোগ্রাম। এটি এসএমএল এবং স্কিমের মতো ভাষার জন্য কম্পাইলারগুলিতে ব্যবহৃত হয়।
CPS-এ, দুটি নিয়ম রয়েছে: প্রথমত, সেই ফাংশন/অপারেটর আর্গুমেন্টগুলি সর্বদা হতে হবে তুচ্ছ; দ্বিতীয়ত, যে ফাংশন কল ফিরে না. এ থেকে অনেক কিছু বেরিয়ে আসে।
এই পোস্টে, আমরা একটি সাধারণ (প্লটকিন) সিপিএস একটি ছোট স্কিমের মতো ভাষা থেকে রূপান্তরিত হয়। আমরা আইআর-এ কিছু অপ্টিমাইজেশন স্কেচ করব। তারপরে আমরা কার্য সম্পাদনের জন্য IR কম্পাইল করার কয়েকটি সাধারণ উপায় দেখব।
মিনি-স্কিম
আমাদের পূর্ণসংখ্যা আছে: 5
আমাদের পূর্ণসংখ্যার উপর কিছু অপারেশন আছে: (+ 1 2)
, (< 3 4)
(1 বা 0 ফেরত)
আমরা ভেরিয়েবল আবদ্ধ করতে পারি: (let ((x 1)) x)
/ (letrec ...)
?
আমরা একক-প্যারামিটার ফাংশন তৈরি করতে পারি: (lambda (x) (+ x 1))
এবং তারা ভেরিয়েবলের উপর বন্ধ করতে পারে
আমরা ফাংশন কল করতে পারি: (f x)
আমরা শাখা করতে পারি: (if (< x y) x y)
(যেখানে আমরা বুলিয়ান হিসাবে 0 এবং 1 ব্যবহার করার সিদ্ধান্ত নিয়েছি)
আমি কিভাবে…?
আমরা একটি পুনরাবৃত্ত ফাংশন বাস্তবায়ন করতে যাচ্ছি নামক cps
ক্রমবর্ধমানভাবে, ভাষার সহজ ফর্মগুলি দিয়ে শুরু করে এবং সেখান থেকে কাজ করা। অনেক লোক স্কিম এবং স্কিম উভয় ক্ষেত্রেই কম্পাইলার প্রয়োগ করতে পছন্দ করে কিন্তু আমি দেখতে পাই যে সমস্ত কোয়াসিকোটিং সবকিছুকে যতটা হওয়া উচিত তার চেয়ে বেশি চঞ্চল করে তোলে এবং পাঠটিকে অস্পষ্ট করে, তাই আমরা পাইথনে এটি করছি।
এর মানে আমাদের কাছে কোড এবং ডেটার একটি সুন্দর স্পষ্ট বিচ্ছেদ রয়েছে। আমাদের পাইথন কোড হল কম্পাইলার এবং আমরা এস-এক্সপ্রেশনের জন্য পাইথন তালিকার উপর নির্ভর করব। পাইথন তালিকার মতো কিছু নমুনা স্কিম প্রোগ্রাম দেখতে কেমন হতে পারে তা এখানে:
5
("+", 1, 2)
("let", (("x", 1)), "x")
("lambda", ("x"), ("+", "x", 1))
আমাদের cps
ফাংশন দুটি আর্গুমেন্ট নেবে। প্রথম যুক্তি, exp
কম্পাইল করার অভিব্যক্তি। দ্বিতীয় যুক্তি, k
একটি ধারাবাহিকতা. আমাদের করতে হবে কিছু আমাদের মানগুলির সাথে, কিন্তু সিপিএসের প্রয়োজন যে ফাংশনগুলি কখনই ফিরে আসে না। তাহলে আমরা কি করব? অন্য ফাংশন কল, অবশ্যই.
এর মানে হল টপ লেভেলের আহ্বান cps
পাস করা হবে কিছু দরকারী শীর্ষ স্তরের ধারাবাহিকতা মত print-to-screen
বা write-to-file
. সব শিশুর আহ্বান cps
হয় সেই ধারাবাহিকতা, একটি উৎপাদিত ধারাবাহিকতা, বা একটি ধারাবাহিক পরিবর্তনশীল পাস করা হবে।
cps(("+", 1, 2), "$print-to-screen")
# ...or...
cps(("+", 1, 2), ("cont", ("v"), ...))
তাই একটি ধারাবাহিকতা শুধু অন্য ফাংশন. ধরনের.
যদিও আপনি সম্পূর্ণরূপে ধারাবাহিকতা হিসাবে ব্যবহারের জন্য বাস্তব প্রথম-শ্রেণীর ফাংশন তৈরি করতে পারেন, এটি প্রায়শই দরকারী হতে পারে বিভাজন তাদের আলাদা করে আপনার CPS IR. সমস্ত বাস্তব (ব্যবহারকারী) ফাংশন একটি শেষ পরামিতি হিসাবে একটি ধারাবাহিকতা গ্রহণ করবে – তাদের রিটার্ন মানগুলি হস্তান্তরের জন্য – এবং নির্বিচারে পালাতে পারে, যেখানে সমস্ত ধারাবাহিকতা তৈরি করা হয় এবং স্ট্যাকের মতো পদ্ধতিতে বরাদ্দ/মুক্ত করা হয়। (এমনকি আমরা চাইলে একটি নেটিভ স্ট্যাক ব্যবহার করেও সেগুলো বাস্তবায়ন করতে পারতাম। দেখুন “পার্টিশনড সিপিএস” এবং “স্ট্যাক পুনরুদ্ধার করা” হয়ত এর পাতা.)
এই কারণে আমরা আইআর ফাংশন ফর্মগুলিকে সিনট্যাক্টিক্যালি আলাদা করি ("fun", ("x",
IR ধারাবাহিকতা ফর্ম থেকে
"k"), ...)("cont", ("x"), ...)
. একইভাবে, আমরা ফাংশন কলগুলিকে আলাদা করি ("f", "x")
ধারাবাহিক কল থেকে ("$call-cont",
(কোথায়
"k", "x")$call-cont
কম্পাইলারের কাছে পরিচিত একটি বিশেষ ফর্ম)।
আসুন দেখি কিভাবে আমরা সিপিএসে পূর্ণসংখ্যা সংকলন করি:
def cps(exp, k):
match exp:
case int(_):
return ("$call-cont", k, exp)
raise NotImplementedError(type(exp)) # TODO
cps(5, "k") # ("$call-cont", "k", 5)
পূর্ণসংখ্যা সন্তুষ্ট তুচ্ছ প্রয়োজন তাই সমস্ত ধ্রুবক ডেটা (যদি আমাদের স্ট্রিং, ফ্লোট ইত্যাদি থাকে), পরিবর্তনশীল রেফারেন্স এবং এমনকি ল্যাম্বডা এক্সপ্রেশনও থাকে। এগুলোর কোনোটিরই পুনরাবৃত্ত মূল্যায়নের প্রয়োজন নেই, যা তুচ্ছ প্রয়োজনীয়তার মূল। এই সবের জন্য আমাদের নেস্টেড AST ছোট অপারেশনের একটি ক্রমানুসারে লিনিয়ারাইজ করা প্রয়োজন।
ভেরিয়েবল একইভাবে কম্পাইল সহজবোধ্য হয়. আমরা পরিবর্তনশীল নামগুলিকে আমাদের IR-এ আপাতত-এর মতো রেখে দিই, তাই আমাদের চারপাশে পরিবেশের প্যারামিটার রাখার দরকার নেই।
def cps(exp, k):
match exp:
case int(_) | str(_):
return ("$call-cont", k, exp)
raise NotImplementedError(type(exp)) # TODO
cps("x", "k") # ("$call-cont", "k", "x")
এখন ফাংশন কল তাকান. ফাংশন কল হল প্রথম ধরনের এক্সপ্রেশন যার জন্য সাব এক্সপ্রেশনের পুনরাবৃত্ত মূল্যায়ন করা প্রয়োজন। মূল্যায়ন করতে (f
উদাহরণস্বরূপ, আমরা মূল্যায়ন করি
x)f
তারপর x
তারপর একটি ফাংশন কল করুন। মূল্যায়নের ক্রম এই পোস্টের জন্য গুরুত্বপূর্ণ নয়; এটি সংকলিত ভাষার একটি শব্দার্থিক সম্পত্তি।
CPS-তে রূপান্তর করতে, আমাদের উভয়কেই আর্গুমেন্টের পুনরাবৃত্ত সংকলন করতে হবে এবং আমাদের প্রথম ধারাবাহিকতা সংশ্লেষণ করতে হবে!
একটি সাব এক্সপ্রেশন মূল্যায়ন করতে, যা ইচ্ছাকৃতভাবে জটিল হতে পারে, আমাদেরকে একটি পুনরাবৃত্ত কল করতে হবে cps
. সাধারণ কম্পাইলার থেকে ভিন্ন, এটি একটি মান ফেরত দেয় না। পরিবর্তে, আপনি এটি একটি ধারাবাহিকতা পাস করেন (এখানে “কলব্যাক” শব্দটি সাহায্য করে?) যখন সেই মানটির একটি নাম থাকে তখন ভবিষ্যতে কাজ করতে। কম্পাইলার-অভ্যন্তরীণ নাম তৈরি করতে, আমাদের আছে একটি gensym
ফাংশন যা আকর্ষণীয় নয় এবং অনন্য স্ট্রিং প্রদান করে।
শুধুমাত্র একটি জিনিস যা এটিকে আলাদা করে, উদাহরণস্বরূপ, একটি জাভাস্ক্রিপ্ট কলব্যাক, এটি একটি পাইথন ফাংশন নয় বরং জেনারেট করা কোডের একটি ফাংশন।
def cps(exp, k):
match exp:
case (func, arg):
vfunc = gensym()
varg = gensym()
return cps(func, ("cont", (vfunc),
cps(arg, ("cont", (varg),
(vfunc, varg, k)))))
# ...
cps(("f", 1), "k")
# ("$call-cont", ("cont", ("v0"),
# ("$call-cont", ("cont", ("v1"),
# ("v0", "v1", "k")),
# 1)),
# "f")
উল্লেখ্য যে আমাদের উৎপন্ন ফাংশন কল থেকে (f x)
এখন একটি ধারাবাহিক যুক্তি রয়েছে যা আগে ছিল না। এই কারণে (f x)
না ফিরে
কিছু, কিন্তু পরিবর্তে প্রদত্ত ধারাবাহিকতা মান পাস.
লাইক আদিম অপারেটর কল +
আমাদের অন্যান্য আকর্ষণীয় ক্ষেত্রে. ফাংশন কলের মতো, আমরা অপারেন্ডগুলিকে পুনরাবৃত্তভাবে মূল্যায়ন করি এবং একটি অতিরিক্ত ধারাবাহিক যুক্তিতে পাস করি। মনে রাখবেন যে সমস্ত CPS বাস্তবায়ন সাধারণ গণিত অপারেটরদের জন্য এটি করে না; কেউ কেউ সরল পাটিগণিতকে প্রকৃতপক্ষে “রিটার্ন” মানগুলিকে অনুমতি দিতে বেছে নেয়।
def gensym(): ...
def cps(exp, k):
match exp:
case (op, x, y) if op in ("+", "-"):
vx = gensym()
vy = gensym()
return cps(x, ("cont", (vx),
cps(y, ("cont", (vy),
(f"$op", vx, vy, k)))))
# ...
cps(("+", 1, 2), "k")
# ("$call-cont", ("cont", ("v0"),
# ("$call-cont", ("cont", ("v1"),
# ("$+", "v0", "v1", "k")),
# 2)),
# 1)
এছাড়াও আমরা আমাদের CPS IR-এ অপারেটরের জন্য একটি বিশেষ ফর্ম তৈরি করি যা দিয়ে শুরু হয়
$
. তাই +
পরিণত হয় $+
এবং তাই এটি ফাংশন কল থেকে অপারেটর আহ্বানকে আলাদা করতে সাহায্য করে।
এখন ফাংশন তৈরির দিকে নজর দেওয়া যাক। ল্যাম্বডা এক্সপ্রেশন যেমন (lambda (x)
রান-টাইমে একটি ফাংশন তৈরি করতে হবে এবং সেই ফাংশন বডিতে কিছু কোড রয়েছে। “ফিরতে”, আমরা ব্যবহার করি
(+ x 1))$call-cont
যথারীতি, কিন্তু আমাদের একটি নতুন তৈরি করতেও মনে রাখতে হবে fun
একটি ধারাবাহিকতা পরামিতি সহ ফর্ম (এবং তারপর ফাংশন বডি মাধ্যমে থ্রেড)।
def cps(exp, k):
match exp:
case ("lambda", (arg), body):
vk = gensym("k")
return ("$call-cont", k,
("fun", (arg, vk), cps(body, vk)))
# ...
cps(("lambda", ("x"), "x"), "k")
# ("$call-cont", "k",
# ("fun", ("x", "k0"),
# ("$call-cont", "k0", "x")))
ঠিক আছে, শেষ এই মিনি ভাষা আমাদের if
অভিব্যক্তি: (if cond iftrue
যেখানে সব
iffalse)cond
, iftrue
এবং iffalse
নির্বিচারে নেস্টেড এক্সপ্রেশন হতে পারে। এই শুধু আমরা কল মানে cps
পুনরাবৃত্তিমূলকভাবে তিনবার।
আমরা এই নতুন কম্পাইলার বিল্টইন নামক যোগ ($if cond iftrue iffalse)
এটি একটি তুচ্ছ অভিব্যক্তি নেয় – শর্তটি – এবং সিদ্ধান্ত নেয় কোন শাখাটি কার্যকর করতে হবে। এটি প্রায় একটি মেশিন কোড শর্তসাপেক্ষ লাফের সমতুল্য।
সহজবোধ্য বাস্তবায়ন ঠিক কাজ করে, কিন্তু আপনি কি ভুল হতে পারে দেখতে পারেন?
def cps(exp, k):
match exp:
case ("if", cond, iftrue, iffalse):
vcond = gensym()
return cps(cond, ("cont", (vcond),
("$if", vcond,
cps(iftrue, k),
cps(iffalse, k))))
# ...
cps(("if", 1, 2, 3), "k")
# ("$call-cont", ("cont", ("v0"),
# ("$if", "v0",
# ("$call-cont", "k", 2),
# ("$call-cont", "k", 3))),
# 1)
সমস্যা হল আমাদের ধারাবাহিকতা, k
একটি ধারাবাহিক পরিবর্তনশীল হতে হবে না – এটি একটি নির্বিচারে জটিল অভিব্যক্তি হতে পারে। আমাদের বাস্তবায়ন এটি সংকলিত কোডে অনুলিপি করে দুইবারযা সবচেয়ে খারাপ ক্ষেত্রে সূচকীয় প্রোগ্রাম বৃদ্ধির দিকে নিয়ে যেতে পারে। পরিবর্তে, এর একটি নামের সাথে এটি আবদ্ধ করা যাক এবং তারপর নামটি দুইবার ব্যবহার করুন।
def cps(exp, k):
match exp:
case ("if", cond, iftrue, iffalse):
vcond = gensym()
vk = gensym("k")
return ("$call-cont", ("cont", (vk),
cps(cond, ("cont", (vcond),
("$if", vcond,
cps(iftrue, vk),
cps(iffalse, vk))))),
k)
# ...
cps(("if", 1, 2, 3), "k")
# ("$call-cont", ("cont", ("k1"),
# ("$call-cont", ("cont", ("v0"),
# ("$if", "v0",
# ("$call-cont", "k1", 2),
# ("$call-cont", "k1", 3))),
# 1)),
# "k")
শেষ, let
একটি ধারাবাহিকতা ব্যবহার করে পরিচালনা করা যেতে পারে, যেমন আমরা পূর্ববর্তী উদাহরণগুলিতে অস্থায়ী ভেরিয়েবলগুলিকে আবদ্ধ করেছি। আপনি এটি ডিসুগার করে এটি পরিচালনা করতে পারেন
((lambda (x) body) value)
কিন্তু এটি পরবর্তীতে পরিত্রাণ পেতে আপনার অপ্টিমাইজারের জন্য অনেক বেশি প্রশাসনিক ওভারহেড তৈরি করবে।
def cps(exp, k):
match exp:
case ("let", (name, value), body):
return cps(value, ("cont", (name),
cps(body, k)))
# ...
cps(("let", ("x", 1), ("+", "x", 2)), "k")
# ('$call-cont', ('cont', ('x'),
# ('$call-cont', ('cont', ('v0'),
# ('$call-cont', ('cont', ('v1'),
# ('$+', 'v0', 'v1', 'k')),
# 2)),
# 'x')),
# 1)
সেখানে আপনি এটি আছে. সিপিএস কনভার্টার থেকে একটি কার্যকরী মিনি-স্কিম। আমার বাস্তবায়ন হল পাইথন কোডের 30 লাইন। এটি সংক্ষিপ্ত এবং মিষ্টি কিন্তু আপনি হয়তো কিছু ত্রুটি লক্ষ্য করেছেন…
এখন, আপনি হয়তো লক্ষ্য করেছেন যে আমরা অনেক তুচ্ছ অভিব্যক্তির নাম দিচ্ছি – অপ্রয়োজনীয় cont
ফর্ম মত ব্যবহার করা হয় let
বাঁধাই পূর্ণসংখ্যার নাম কেন 3
যদি এটা তুচ্ছ হয়?
এটি এড়াতে লোকেরা একটি পদ্ধতি গ্রহণ করে মেটা ধারাবাহিকতাযাকে আমি মনে করি অনেকে “হায়ার-অর্ডার ট্রান্সফর্ম” বলে। পরিবর্তে সবসময় উৎপন্ন
cont
s, আমরা কখনও কখনও পরিবর্তে একটি কম্পাইলার-লেভেল (এই ক্ষেত্রে, পাইথন) ফাংশনে পাস করতে পারি।
দেখুন ম্যাট Might এর নিবন্ধ এবং আমি কি মনে করি একটি কাজ পাইথন বাস্তবায়ন.
এই পদ্ধতিটি, যদিও মাঝে মাঝে যুক্তি করা কঠিন এবং আরও জটিল, এটি নির্গত হওয়ার আগে একটি উল্লেখযোগ্য পরিমাণ কোড হ্রাস করে। কয়েকটি-পাস কম্পাইলারদের জন্য, সংস্থান-সংকল্পিত পরিবেশের জন্য, বিশাল প্রোগ্রামগুলির জন্য, … এটি অনেক অর্থবহ করে তোলে।
আরেকটি পদ্ধতি, সম্ভাব্যভাবে যুক্তি করা সহজ, হল আপনার অপ্টিমাইজারের উপর নির্ভর করা। আপনি সম্ভবত যাইহোক একটি অপ্টিমাইজার চাইবেন, তাই আপনি আপনার মধ্যবর্তী কোডটিও কাটতে এটি ব্যবহার করতে পারেন।
অপ্টিমাইজেশন
যেকোন IR এর মতই, পুনরাবৃত্ত পুনর্লিখন করে অপ্টিমাইজ করা সম্ভব। আমরা এখানে কোনটি বাস্তবায়ন করব না (আপাতত… হয়তো আমি এটিতে ফিরে আসব) তবে কয়েকটি সাধারণ স্কেচ আউট করব।
সবচেয়ে সহজ একটি সম্ভবত ধ্রুবক ভাঁজ, বাঁক মত (+ 3 4)
মধ্যে 7
. CPS সমতুল্য এই ধরনের দেখায়:
("$+", "3", "4", "k") # => ("$call-cont", "k", 7)
("$if", 1, "t", "f") # => t
("$if", 0, "t", "f") # => f
একটি বিশেষ করে গুরুত্বপূর্ণ অপ্টিমাইজেশান, বিশেষ করে যদি আমরা ব্যবহার করছি সাধারণ CPS রূপান্তর ব্যবহার করে, তা হল বিটা হ্রাস। এই মত অভিব্যক্তি বাঁক প্রক্রিয়া ((lambda (x) (+ x 1)) 2)
মধ্যে (+ 2 1)
প্রতিস্থাপন দ্বারা 2
জন্য x
. উদাহরণস্বরূপ, CPS-এ:
("$call-cont", ("cont", ("k1"),
("$call-cont", ("cont", ("v0"),
("$if", "v0",
("$call-cont", "k1", 2),
("$call-cont", "k1", 3))),
1)),
"k")
# into
("$call-cont", ("cont", ("v0"),
("$if", "v0",
("$call-cont", "k", 2),
("$call-cont", "k", 3))),
1)
# into
("$if", 1,
("$call-cont", "k", 2),
("$call-cont", "k", 3))
# into (via constant folding)
("$call-cont", "k", 2)
প্রতিস্থাপনকে সুযোগ-সচেতন হতে হবে, এবং তাই আপনার অপ্টিমাইজারের মাধ্যমে একটি পরিবেশের প্যারামিটার থ্রেড করা প্রয়োজন।
একটি বাদ দিয়ে: এমনকি আপনি যদি আপনার অভিব্যক্তিগুলিকে অনন্য পরিবর্তনশীল বাইন্ডিং এবং নাম তৈরি করার জন্য “আলফাটিস” করেন তবে প্রতিস্থাপন করার সময় আপনি দুর্ঘটনাক্রমে একই নামের দ্বিতীয় বাইন্ডিং তৈরি করতে পারেন। যেমন:
# substitute(haystack, name, replacement) substitute(("+", "x", "x"), "x", ("let", ("x0", 1), "x0"))
এই দুটি বাইন্ডিং তৈরি করবে
x0
যা বিশ্বব্যাপী স্বতন্ত্রতা সম্পত্তি লঙ্ঘন করে।
আপনি সবসময় এই পুনর্লিখন করতে চান না. কোড ব্লোআপ এড়াতে, আপনি শুধুমাত্র তখনই প্রতিস্থাপন করতে চাইতে পারেন যদি ফাংশন বা ধারাবাহিকতার প্যারামিটারের নামটি শূন্য বা এক বার বডিতে প্রদর্শিত হয়। অথবা, যদি এটি একাধিকবার হয়, শুধুমাত্র যদি প্রতিস্থাপিত অভিব্যক্তিটি একটি পূর্ণসংখ্যা/ভেরিয়েবল হয়। এটি একটি সাধারণ হিউরিস্টিক যা কিছু খারাপ-কেস পরিস্থিতি এড়াতে পারে কিন্তু সর্বাধিক উপকারী নাও হতে পারে – এটি একটি স্থানীয় সর্বোত্তম।
আরেকটি বিষয় সচেতন হতে হবে যে প্রতিস্থাপন মূল্যায়নের ক্রম পরিবর্তন করতে পারে। তাই আপনার কাছে শুধুমাত্র একটি প্যারামিটার রেফারেন্স থাকলেও, আপনি বিকল্প করতে নাও পারেন:
((lambda (f) (begin (g) f))
(do-a-side-effect))
যেহেতু প্রোগ্রামটি এখন চলছে, do-a-side-effect
আগে ডাকা হবে g
এবং ফলাফল হয়ে যাবে f
. যদি আপনি বিকল্প do-a-side-effect
জন্য f
আপনার অপ্টিমাইজারে, g
আগে ডাকা হবে do-a-side-effect
. আপনি আরও আক্রমণাত্মক হতে পারেন যদি আপনার বিশ্লেষক আপনাকে বলে যে কোন ফাংশনগুলি পার্শ্ব-প্রতিক্রিয়া মুক্ত, কিন্তু অন্যথায়… ফাংশন কলগুলির বিষয়ে সতর্ক থাকুন৷
এছাড়াও আরও উন্নত অপ্টিমাইজেশান রয়েছে তবে সেগুলি সিপিএসের পরিচিতির বাইরে যায় এবং আমি সেগুলিকে স্কেচ করার জন্য যথেষ্ট আত্মবিশ্বাসী বোধ করি না।
ঠিক আছে, আমরা একগুচ্ছ CPS→CPS রূপান্তর করেছি। এখন আমরা অপ্টিমাইজ করা কোড চালাতে চাই। এটি করার জন্য, আমাদেরকে সিপিএস থেকে পরিকল্পিত কিছুতে রূপান্তরিত করতে হবে।
সি থেকে, স্বপ্ন দেখার সম্ভাবনা
এই বিভাগে আমরা সিপিএস থেকে এক্সিকিউটেবল কোড তৈরি করার জন্য কয়েকটি পন্থা তালিকাভুক্ত করব কিন্তু আমরা কোনোটি বাস্তবায়ন করব না।
আপনি সিপিএস থেকে সরাসরি সি কোড তৈরি করতে পারেন। দ fun
এবং cont
ফর্মগুলি শীর্ষ-স্তরের সি ফাংশন হয়ে ওঠে। বন্ধ সমর্থন করার জন্য, আপনাকে বিনামূল্যে পরিবর্তনশীল বিশ্লেষণ করতে হবে এবং বন্ধ কাঠামো বরাদ্দ প্রত্যেকের জন্য (এছাড়াও দেখুন স্ক্র্যাপস্ক্রিপ্ট মধ্যে পদ্ধতি “ফাংশন” নামক বিভাগে।) তারপর আপনি একটি খুব সাধারণ কলিং কনভেনশন করতে পারেন যেখানে আপনি চারপাশে ক্লোজার পাস করেন। দুর্ভাগ্যবশত, এটি খুব কার্যকর নয় এবং টেল-কল নির্মূলের গ্যারান্টি দেয় না।
টেইল-কল নির্মূল সমর্থন করতে, আপনি ব্যবহার করতে পারেন trampolines. এটি বেশিরভাগই প্রতিটি টেইল-কলের সাথে স্তূপে একটি ফ্রেমের মতো বন্ধ বরাদ্দ করা জড়িত। আপনার যদি আবর্জনা সংগ্রহকারী থাকে তবে এটি খুব খারাপ নয়; ফ্রেম খুব বেশি দিন বাঁচে না। আসলে, আপনি যদি ফ্যাক্টরিয়াল উদাহরণ যন্ত্র এলির ব্লগ পোস্টে, আপনি দেখতে পাচ্ছেন যে ট্রামপোলিন ফ্রেমগুলি কেবল পরেরটি বরাদ্দ না হওয়া পর্যন্ত বেঁচে থাকে।
এই পর্যবেক্ষণ উন্নয়নের নেতৃত্বে এমটিএ-তে চেনি
অ্যালগরিদম, যা আবর্জনা সংগ্রহকারীর জন্য একটি তরুণ প্রজন্ম হিসাবে C কল স্ট্যাক ব্যবহার করে। এটি ব্যবহার করে setjmp
এবং longjmp
স্ট্যাক unwind. এই পদ্ধতি ব্যবহার করা হয় চিকেন স্কিম এবং ঘূর্ণিঝড় পরিকল্পনা. দেখে নিন
বেকারের 1994 বাস্তবায়ন.
আপনি যদি এই ট্রামপোলিন স্টাফগুলির কোনওটি করতে না চান তবে আপনি ওয়ান বিগ সুইচ পদ্ধতিও করতে পারেন যা প্রতিটি fun
s এবং cont
s মধ্যে a case
একটি বিশাল মধ্যে switch
বিবৃতি কল হয়ে যায় goto
s আপনি একটি সংলগ্ন অ্যারেতে খুব সহজেই আপনার স্ট্যাক রুটগুলি পরিচালনা করতে পারেন। যাইহোক, আপনি কল্পনা করতে পারেন, বড় স্কিম প্রোগ্রামগুলি কিছু C কম্পাইলারের সাথে সমস্যা সৃষ্টি করতে পারে।
সবশেষে, আপনাকে সি জেনারেট করতে হবে না। আপনি CPS থেকে একটি নিম্ন-স্তরের IR এবং তারপরে কোনো ধরনের অ্যাসেম্বলি ল্যাঙ্গুয়েজে আপনার নিজের লোয়ারিংও করতে পারেন।
মোড়ানো
আপনি দেখেছেন কীভাবে সিপিএস তৈরি করতে হয়, কীভাবে এটি অপ্টিমাইজ করা যায় এবং কীভাবে এটি নির্মূল করা যায়। আরো অনেক কিছু শেখার আছে, যদি আপনি আগ্রহী হন। আপনি যদি এটি দরকারী খুঁজে আমাকে উপাদান পাঠান দয়া করে.
আমি মূলত স্ক্র্যাপস্ক্রিপ্টের জন্য একটি CPS-ভিত্তিক অপ্টিমাইজার এবং কোড জেনারেটর লেখার পরিকল্পনা করেছিলাম কিন্তু আমি CPS-এর সাথে মিলিত প্যাটার্ন কম্পাইল করার সূক্ষ্ম বিবরণে আটকে গিয়েছিলাম। হয়তো আমি ভবিষ্যতে এটিকে নেস্টে ডিসুগার করে ফিরে আসব
if
s বা কিছু।
চেক আউট কোড.
স্বীকৃতি
ধন্যবাদ বৈভব সাগর এবং কার্তিক আগরাম এই পোস্টে প্রতিক্রিয়া দেওয়ার জন্য। ধন্যবাদ
অলিন শিভার্স কার্যকরী প্রোগ্রামিং ভাষা কম্পাইল করার একটি চমৎকার কোর্সের জন্য।