খুব সহজভাবে, টেল-কল অপ্টিমাইজেশন কী?
আরও সুনির্দিষ্টভাবে বলা যায় যে কয়েকটি ছোট কোড স্নিপেটগুলি কোথায় এটি প্রয়োগ করা যেতে পারে, এবং কেন নয়, কেন তার ব্যাখ্যা দিয়ে?
খুব সহজভাবে, টেল-কল অপ্টিমাইজেশন কী?
আরও সুনির্দিষ্টভাবে বলা যায় যে কয়েকটি ছোট কোড স্নিপেটগুলি কোথায় এটি প্রয়োগ করা যেতে পারে, এবং কেন নয়, কেন তার ব্যাখ্যা দিয়ে?
উত্তর:
টেইল-কল অপ্টিমাইজেশন যেখানে আপনি কোনও ফাংশনের জন্য একটি নতুন স্ট্যাক ফ্রেম বরাদ্দ এড়াতে সক্ষম হবেন কারণ কলিং ফাংশনটি কেবল কলটি ফাংশন থেকে পাওয়া মানটি ফিরিয়ে দেবে। সর্বাধিক সাধারণ ব্যবহার হল লেজ-পুনরাবৃত্তি, যেখানে টেল-কল অপ্টিমাইজেশানের সুবিধা নিতে লেখা একটি পুনরাবৃত্ত ফাংশন ধ্রুব স্ট্যাক স্পেস ব্যবহার করতে পারে।
স্কিম হ'ল কয়েকটি প্রোগ্রামিং ল্যাঙ্গুয়েজের একটি যা এই অনুমানের গ্যারান্টি দেয় যে কোনও বাস্তবায়ন অবশ্যই এই অপ্টিমাইজেশন সরবরাহ করতে পারে (জাভাস্ক্রিপ্ট এছাড়াও করে, ইএস 6 দিয়ে শুরু করে) , সুতরাং এখানে স্কিমের ফ্যাক্টরিয়াল ফাংশনের দুটি উদাহরণ রয়েছে:
(define (fact x)
(if (= x 0) 1
(* x (fact (- x 1)))))
(define (fact x)
(define (fact-tail x accum)
(if (= x 0) accum
(fact-tail (- x 1) (* x accum))))
(fact-tail x 1))
প্রথম ফাংশনটি টেল রিকার্সিভ হয় না কারণ যখন পুনরাবৃত্তি কল করা হয় তখন ফাংশনটির কল কলটি ফিরে আসার পরে ফলাফলটির সাথে করা গুণটির ট্র্যাক রাখা প্রয়োজন। এর মতো, স্ট্যাকটি নীচে দেখায়:
(fact 3)
(* 3 (fact 2))
(* 3 (* 2 (fact 1)))
(* 3 (* 2 (* 1 (fact 0))))
(* 3 (* 2 (* 1 1)))
(* 3 (* 2 1))
(* 3 2)
6
বিপরীতে, লেজ পুনরাবৃত্ত ফ্যাক্টরিয়াল জন্য স্ট্যাক ট্রেস নিম্নলিখিত হিসাবে দেখায়:
(fact 3)
(fact-tail 3 1)
(fact-tail 2 3)
(fact-tail 1 6)
(fact-tail 0 6)
6
আপনি দেখতে পাচ্ছেন, আমাদের কেবল প্রতিটি লেকের ফ্যাক্ট লেজ-তে একই পরিমাণের ডেটা রাখতে হবে কারণ আমরা কেবল যে মানটি উপরে পৌঁছেছি তা কেবল ফিরে আসছি। এর অর্থ হ'ল আমি কল করতে চাইলেও (সত্য 1000000), আমার কাছে একই পরিমাণের পরিমাণ (সত্য 3) প্রয়োজন। এটি অ-পুচ্ছ-পুনরাবৃত্তিযোগ্য সত্যের ক্ষেত্রে নয় এবং যেমন বড় মানগুলি স্ট্যাকের ওভারফ্লো হতে পারে cause
আসুন একটি সাধারণ উদাহরণ দিয়ে চলুন: সি তে বাস্তবকৃত ফাংশনাল ফাংশন
আমরা সুস্পষ্ট পুনরাবৃত্ত সংজ্ঞা দিয়ে শুরু করি
unsigned fac(unsigned n)
{
if (n < 2) return 1;
return n * fac(n - 1);
}
ফাংশনটি টেল কল দিয়ে শেষ হয় যদি ফাংশনটি ফেরার আগে শেষ অপারেশনটি অন্য ফাংশন কল হয়। যদি এই কল একই ক্রিয়াকলাপটি আহ্বান করে, এটি লেজ-পুনরাবৃত্ত হয়।
যদিও fac()
প্রথম নজরে পুচ্ছ-পুনরাবৃত্তি মনে হচ্ছে, বাস্তবে যা ঘটে তা তা নয়
unsigned fac(unsigned n)
{
if (n < 2) return 1;
unsigned acc = fac(n - 1);
return n * acc;
}
অর্থাৎ শেষ অপারেশনটি হ'ল গুণ এবং না ফাংশন কল।
যাইহোক, fac()
অতিরিক্ত যুক্তি হিসাবে কল চেইনে জমা হওয়া মানটি প্রেরণ করে এবং ফেরতের মান হিসাবে কেবলমাত্র চূড়ান্ত ফলাফলটি পাস করে পুনরায় লিখতে পারা যায়:
unsigned fac(unsigned n)
{
return fac_tailrec(1, n);
}
unsigned fac_tailrec(unsigned acc, unsigned n)
{
if (n < 2) return acc;
return fac_tailrec(n * acc, n - 1);
}
এখন, কেন এটি দরকারী? যেহেতু আমরা তাত্ক্ষণিকভাবে লেজ কলের পরে ফিরে আসছি, লেজ অবস্থানে ফাংশনটি শুরু করার আগে আমরা পূর্ববর্তী স্ট্যাকফ্রেমটি বাতিল করতে পারি বা পুনরাবৃত্ত ফাংশনগুলির ক্ষেত্রে স্ট্যাকফ্রেমটিকে যেমন রয়েছে তেমনভাবে পুনরায় ব্যবহার করতে পারি।
টেল-কল অপ্টিমাইজেশন আমাদের পুনরাবৃত্ত কোডকে রূপান্তর করে
unsigned fac_tailrec(unsigned acc, unsigned n)
{
TOP:
if (n < 2) return acc;
acc = n * acc;
n = n - 1;
goto TOP;
}
এটি ইনলাইন করা যেতে পারে fac()
এবং আমরা পৌঁছাতে
unsigned fac(unsigned n)
{
unsigned acc = 1;
TOP:
if (n < 2) return acc;
acc = n * acc;
n = n - 1;
goto TOP;
}
যা সমান
unsigned fac(unsigned n)
{
unsigned acc = 1;
for (; n > 1; --n)
acc *= n;
return acc;
}
আমরা এখানে দেখতে পাচ্ছি, পর্যাপ্ত উন্নত অপ্টিমাইজার পুনরাবৃত্তির সাথে লেজ-পুনরাবৃত্তি প্রতিস্থাপন করতে পারে, যা আপনি ফাংশন কল ওভারহেড এড়ানো এবং কেবল স্থির পরিমাণের স্ট্যাক ব্যবহার করার কারণে এটি আরও কার্যকর।
টিসিও (টেল কল অপ্টিমাইজেশন) হ'ল প্রক্রিয়া যার মাধ্যমে একটি স্মার্ট সংকলক একটি ফাংশনে কল করতে পারে এবং কোনও অতিরিক্ত স্ট্যাকের জায়গা নিতে পারে না। শুধুমাত্র অবস্থা যা যদি সেটা হয় গত নির্দেশ একটি ফাংশন মধ্যে মৃত্যুদন্ড কার্যকর হয় চ একটি ফাংশন ছ একটি কল (: নোট ছ হতে পারে চ )। এখানে মূল কীটি হ'ল চ এর আর স্ট্যাক স্পেসের প্রয়োজন নেই - এটি কেবল জি কল করে এবং তারপরে জি যা ফিরে আসবে তা ফিরিয়ে দেয়। এই ক্ষেত্রে অপ্টিমাইজেশান তৈরি করা যেতে পারে যে ছ কেবল রান করে এবং যেকোন মানকে যে এফ বলে returns
এই অপ্টিমাইজেশানটি পুনরাবৃত্ত কলগুলি বিস্ফোরণের পরিবর্তে ধ্রুব স্ট্যাক স্থান নিতে পারে।
উদাহরণ: এই ফ্যাক্টরিয়াল ফাংশনটি TCOptimizable নয়:
def fact(n):
if n == 0:
return 1
return n * fact(n-1)
এই ফাংশনটি তার ফিরতি বিবৃতিতে অন্য ফাংশন কল করার পাশাপাশি কিছু করে।
এই নীচের ফাংশনটি TCOptimizable:
def fact_h(n, acc):
if n == 0:
return acc
return fact_h(n-1, acc*n)
def fact(n):
return fact_h(n, 1)
এর কারণ এই ফাংশনগুলির মধ্যে যে কোনওটি ঘটতে শেষ কাজটি হ'ল অন্য ফাংশনটি কল করা।
টেল কল, পুনরাবৃত্ত টেল কল এবং লেজ কল অপ্টিমাইজেশনের জন্য সম্ভবত সবচেয়ে ভাল উচ্চ স্তরের বিবরণ আমি পেয়েছি এটি ব্লগ পোস্ট
লিখেছেন ড্যান সুগালস্কি। লেজ কল অপ্টিমাইজেশনে তিনি লিখেছেন:
এক মুহুর্তের জন্য, এই সাধারণ ক্রিয়াটি বিবেচনা করুন:
sub foo (int a) { a += 15; return bar(a); }
সুতরাং, আপনি বা আপনার ভাষা সংকলক, কী করতে পারেন? ভাল, এটি কী করতে পারে তা হ'ল ফর্মের
return somefunc();
কোডগুলি নিম্ন-স্তরের ক্রমে পরিণত করাpop stack frame; goto somefunc();
। আমাদের উদাহরণে, মানে আগে আমরা কলbar
,foo
নিজেই আপ সাফ করে এবং তারপর, বরং কলিং চেয়েbar
একটি সাবরুটিন হিসাবে, আমরা একটি নিম্ন স্তরের নাgoto
অপারেশন শুরুbar
।Foo
'ইতিমধ্যে নিজেই স্ট্যাকের বাইরে পরিষ্কার র, তাই যখনbar
দেখে মনে হচ্ছে শুরু মত যে কেহ নামকfoo
সত্যিই ডেকেছেনbar
, এবং যখনbar
তার মান, এটা সরাসরি যে কেহ নামে ফেরৎfoo
, বরং তা ফিরে চেয়েfoo
যা পরে তার কলারের কাছে এটা ফিরে আসবে।
এবং লেজ পুনরাবৃত্তি:
লেজ পুনরাবৃত্তি ঘটে যদি কোনও ফাংশন, এর শেষ অপারেশন হিসাবে, নিজে কল করার ফলাফলটি দেয় । লেজ পুনরাবৃত্তি মোকাবেলা করা সহজ কারণ কোথাও কিছু এলোমেলো ফাংশন শুরুর দিকে ঝাঁপিয়ে পড়ার পরিবর্তে, আপনি নিজের গোড়ার দিকে ফিরে যান, যা করা খুব সাহসী কাজ।
যাতে এটি:
sub foo (int a, int b) { if (b == 1) { return a; } else { return foo(a*a + a, b - 1); }
নিঃশব্দে পরিণত হয়:
sub foo (int a, int b) { label: if (b == 1) { return a; } else { a = a*a + a; b = b - 1; goto label; }
এই বিবরণটি সম্পর্কে আমি যা পছন্দ করি তা হ'ল একটি অত্যাবশ্যকীয় ভাষার পটভূমি (সি, সি ++, জাভা) থেকে আসা ব্যক্তিদের জন্য উপলব্ধি করা কতটা সংক্ষিপ্ত এবং সহজ (
foo
ফাংশন টেল কলটি অনুকূলিত হয়নি? এটি কেবল কোনও ফাংশনকে তার শেষ পদক্ষেপ হিসাবে ডেকে আনে এবং এটি কেবল সেই মানটি ফিরিয়ে দেয়, তাই না?
সবার আগে নোট করুন যে সমস্ত ভাষা এটি সমর্থন করে না।
টিসিও পুনরাবৃত্তির একটি বিশেষ ক্ষেত্রে প্রযোজ্য। এর সংক্ষিপ্তসারটি হ'ল, কোনও ফাংশনে আপনি শেষ কাজটি নিজে কল করুন (যেমন এটি "লেজ" অবস্থান থেকে নিজেকে কল করে), এটি সংকলক দ্বারা মানক পুনরাবৃত্তির পরিবর্তে পুনরাবৃত্তির মতো কাজ করতে অনুকূলিত করা যেতে পারে।
আপনি দেখতে পাচ্ছেন, সাধারণত পুনরাবৃত্তি চলাকালীন, রানটাইমের জন্য সমস্ত পুনরাবৃত্ত কলগুলি ট্র্যাক করে রাখা দরকার, যাতে কোনও ব্যক্তি যখন ফিরে আসে তখন আগের কলটিতে আবার শুরু হতে পারে can (এটি কীভাবে কাজ করে তার একটি ভিজ্যুয়াল ধারণা পেতে পুনরাবৃত্ত কলের ফলাফলটি ম্যানুয়ালি লিখে দেওয়ার চেষ্টা করুন all) সমস্ত কলের খোঁজ রাখা জায়গাগুলি গ্রহণ করে, যা ফাংশনটি নিজেকে অনেক কল করলে তা তাৎপর্যপূর্ণ হয়। তবে টিসিওর সাহায্যে এটি কেবল "শুরুতে ফিরে যান, কেবলমাত্র এই সময়ে প্যারামিটারের মানগুলিকে এই নতুনগুলিতে পরিবর্তন করুন" বলতে পারেন। এটি এটি করতে পারে কারণ পুনরাবৃত্তির কলের পরে কিছুই সেই মানগুলিকে বোঝায় না।
foo
পদ্ধতির টেল কলটি অনুকূলিত নয়?
X86 বিযুক্তির বিশ্লেষণ সহ জিসিসির ন্যূনতম চলমান উদাহরণ
আসুন দেখুন জিসিসি কীভাবে উত্পন্ন সমাবেশটি দেখে আমাদের জন্য টেল কল অপ্টিমাইজেশন স্বয়ংক্রিয়ভাবে করতে পারে।
এটি https://stackoverflow.com/a/9814654/895245 এর মতো অন্যান্য উত্তরে যা উল্লেখ করা হয়েছিল তার চূড়ান্ত উদাহরণ হিসাবে কাজ করবে যা অপ্টিমাইজেশন পুনরাবৃত্ত ফাংশন কলগুলিকে একটি লুপে রূপান্তর করতে পারে।
এর ফলে স্মৃতিশক্তি সাশ্রয় হয় এবং কর্মক্ষমতা উন্নত হয়, যেহেতু আজকাল মেমরির অ্যাক্সেসগুলি মূলত যা প্রোগ্রামগুলি ধীর করে দেয় ।
একটি ইনপুট হিসাবে, আমরা জিসিসি একটি অপ্টিমাইটিজড নিষ্পাপ স্ট্যাক ভিত্তিক ফ্যাকটোরিয়াল দিচ্ছি:
tail_call.c
#include <stdio.h>
#include <stdlib.h>
unsigned factorial(unsigned n) {
if (n == 1) {
return 1;
}
return n * factorial(n - 1);
}
int main(int argc, char **argv) {
int input;
if (argc > 1) {
input = strtoul(argv[1], NULL, 0);
} else {
input = 5;
}
printf("%u\n", factorial(input));
return EXIT_SUCCESS;
}
সংকলন এবং বিচ্ছিন্ন:
gcc -O1 -foptimize-sibling-calls -ggdb3 -std=c99 -Wall -Wextra -Wpedantic \
-o tail_call.out tail_call.c
objdump -d tail_call.out
-foptimize-sibling-calls
অনুসারে লেজ কলগুলির সাধারণীকরণের নাম কোথায় man gcc
:
-foptimize-sibling-calls
Optimize sibling and tail recursive calls.
Enabled at levels -O2, -O3, -Os.
যেমনটি উল্লেখ করা হয়েছে: জিসিসি টেল-পুনরাবৃত্তি অপ্টিমাইজেশন সম্পাদন করছে কিনা তা আমি কীভাবে পরীক্ষা করব?
আমি নির্বাচন করি -O1
কারণ:
-O0
। আমার সন্দেহ হয় যে এটি প্রয়োজনীয় কারণগুলির মধ্যে অন্তর্বর্তী ট্রান্সফর্মেশনগুলি অনুপস্থিত।-O3
অধার্মিক দক্ষ কোড উত্পাদন করে যা খুব শিক্ষামূলক নয়, যদিও এটি লেজ কলটিও অনুকূলিত।এর সাথে বিচ্ছিন্ন -fno-optimize-sibling-calls
:
0000000000001145 <factorial>:
1145: 89 f8 mov %edi,%eax
1147: 83 ff 01 cmp $0x1,%edi
114a: 74 10 je 115c <factorial+0x17>
114c: 53 push %rbx
114d: 89 fb mov %edi,%ebx
114f: 8d 7f ff lea -0x1(%rdi),%edi
1152: e8 ee ff ff ff callq 1145 <factorial>
1157: 0f af c3 imul %ebx,%eax
115a: 5b pop %rbx
115b: c3 retq
115c: c3 retq
সহ -foptimize-sibling-calls
:
0000000000001145 <factorial>:
1145: b8 01 00 00 00 mov $0x1,%eax
114a: 83 ff 01 cmp $0x1,%edi
114d: 74 0e je 115d <factorial+0x18>
114f: 8d 57 ff lea -0x1(%rdi),%edx
1152: 0f af c7 imul %edi,%eax
1155: 89 d7 mov %edx,%edi
1157: 83 fa 01 cmp $0x1,%edx
115a: 75 f3 jne 114f <factorial+0xa>
115c: c3 retq
115d: 89 f8 mov %edi,%eax
115f: c3 retq
দুজনের মধ্যে মূল পার্থক্য হ'ল:
-fno-optimize-sibling-calls
ব্যবহারসমূহ callq
, যা সাধারণত অ অপ্টিমাইজ ফাংশন কল।
এই নির্দেশটি স্ট্যাকের দিকে ফেরতের ঠিকানাটিকে ধাক্কা দেয়, তাই এটি বৃদ্ধি করে।
তদতিরিক্ত, এই সংস্করণটিও করে push %rbx
, যা স্ট্যাকের দিকে ধাক্কা দেয়%rbx
।
জিসিসি এটি করে কারণ এটি সঞ্চয় করে edi
, যা প্রথম ফাংশন আর্গুমেন্ট ( n
) এর মধ্যে ebx
, তারপরে কল factorial
।
জিসিসির এটি করা দরকার কারণ এটি অন্য একটি কল করার জন্য প্রস্তুতি নিচ্ছে factorial
, যা নতুনটি ব্যবহার করবে edi == n-1
।
এটি চয়ন করে ebx
কারণ এই রেজিস্টারটি ক্যালি-সেভড: লিনাক্স x86-64 ফাংশন কলের মাধ্যমে কী রেজিস্টারগুলি সংরক্ষণ করা হয় যাতে সাবকেলটি factorial
এটি পরিবর্তন না করে এবং হারাতে পারে না n
।
-foptimize-sibling-calls
কোনো নির্দেশাবলী স্ট্যাকে ধাক্কা ব্যবহার করে না: এটি শুধুমাত্র করেন goto
মধ্যে জাম্প factorial
নির্দেশাবলী সহ je
এবং jne
।
অতএব, এই সংস্করণটি কোনও ফাংশন কল ছাড়াই কিছুক্ষণ লুপের সমান। স্ট্যাক ব্যবহার ধ্রুবক।
উবুন্টু 18.10, জিসিসি 8.2 তে পরীক্ষিত।
এখানে দেখুন:
http://tratt.net/laurie/tech_articles/articles/tail_call_optimization
আপনি সম্ভবত জানেন যে, পুনরাবৃত্তি ফাংশন কল একটি স্ট্যাকের সর্বনাশ করতে পারে; স্ট্যাক স্পেস থেকে দ্রুত রান আউট করা সহজ। টেইল কল অপ্টিমাইজেশন হল এমন এক উপায় যার মাধ্যমে আপনি একটি পুনরাবৃত্ত শৈলীর অ্যালগোরিদম তৈরি করতে পারেন যা ধ্রুব স্ট্যাক স্পেস ব্যবহার করে, তাই এটি বৃদ্ধি পায় না এবং বৃদ্ধি পায় না এবং আপনি স্ট্যাক ত্রুটি পান।
আমাদের নিশ্চিত করা উচিত যে ফাংশনে নিজেই কোনও গোটো স্টেটমেন্ট নেই .. ফাংশন কল দ্বারা যত্ন নেওয়া কলি ফাংশনের শেষ জিনিস।
বৃহত্তর স্কেল পুনরাবৃত্তিগুলি এটি অপ্টিমাইজেশনের জন্য ব্যবহার করতে পারে তবে ছোট স্কেলে ফাংশন কলটি টেল কল করার জন্য নির্দেশের ওভারহেডটি আসল উদ্দেশ্যকে হ্রাস করে।
টিসিও চিরকালের জন্য চলমান কার্যকারিতার কারণ হতে পারে:
void eternity()
{
eternity();
}
পুনরাবৃত্ত ফাংশন পদ্ধতির একটি সমস্যা আছে। এটি আকার ও (এন) এর একটি কল স্ট্যাক তৈরি করে যা আমাদের মোট মেমরির জন্য ও (এন) খরচ করে। এটি এটিকে স্ট্যাক ওভারফ্লো ত্রুটির কাছে ঝুঁকিপূর্ণ করে তোলে, যেখানে কল স্ট্যাকটি খুব বড় হয়ে যায় এবং স্থানের বাইরে চলে যায়।
টেইল কল অপ্টিমাইজেশন (টিসিও) স্কিম। লম্বা কল স্ট্যাক তৈরি না করা এবং এটি মেমরির ব্যয় সাশ্রয় করতে যেখানে এটি পুনরাবৃত্ত ফাংশনগুলি অনুকূল করতে পারে।
এমন অনেক ভাষা রয়েছে যারা টিসিও করছেন (জাভাস্ক্রিপ্ট, রুবি এবং কয়েকটি সি) যেখানে পাইথন এবং জাভা টিসিও করেন না।
জাভাস্ক্রিপ্ট ভাষা :) :) ব্যবহারের বিষয়টি নিশ্চিত করেছে http:// http://2ality.com/2015/06/tail-call-optimization.html
একটি কার্যকরী ভাষায়, টেল কল অপ্টিমাইজেশন যেমন ফাংশন কল ফলাফল হিসাবে একটি আংশিক মূল্যায়ন বাক্স ফিরে আসতে পারে, যার পরে কলার দ্বারা মূল্যায়ন করা হবে।
f x = g x
f 6 হ্রাস করে জি 6 এ। সুতরাং যদি ফলস্বরূপ বাস্তবায়ন জি 6 ফিরে আসতে পারে এবং তারপরে এই অভিব্যক্তিটিকে কল করে এটি একটি স্ট্যাক ফ্রেম সংরক্ষণ করবে।
এছাড়াও
f x = if c x then g x else h x.
জি 6 বা এইচ 6 এফ 6 এ হ্রাস পায় তাই বাস্তবায়ন যদি সি 6 এর মূল্যায়ন করে এবং এটি সত্য বলে মনে হয় তবে এটি হ্রাস করতে পারে,
if true then g x else h x ---> g x
f x ---> h x
একটি সাধারণ নন টেল কল অপ্টিমাইজেশন দোভাষী এইরকম দেখতে পারে,
class simple_expresion
{
...
public:
virtual ximple_value *DoEvaluate() const = 0;
};
class simple_value
{
...
};
class simple_function : public simple_expresion
{
...
private:
simple_expresion *m_Function;
simple_expresion *m_Parameter;
public:
virtual simple_value *DoEvaluate() const
{
vector<simple_expresion *> parameterList;
parameterList->push_back(m_Parameter);
return m_Function->Call(parameterList);
}
};
class simple_if : public simple_function
{
private:
simple_expresion *m_Condition;
simple_expresion *m_Positive;
simple_expresion *m_Negative;
public:
simple_value *DoEvaluate() const
{
if (m_Condition.DoEvaluate()->IsTrue())
{
return m_Positive.DoEvaluate();
}
else
{
return m_Negative.DoEvaluate();
}
}
}
একটি লেজ কল অপ্টিমাইজেশন দোভাষী এর মত দেখতে হতে পারে,
class tco_expresion
{
...
public:
virtual tco_expresion *DoEvaluate() const = 0;
virtual bool IsValue()
{
return false;
}
};
class tco_value
{
...
public:
virtual bool IsValue()
{
return true;
}
};
class tco_function : public tco_expresion
{
...
private:
tco_expresion *m_Function;
tco_expresion *m_Parameter;
public:
virtual tco_expression *DoEvaluate() const
{
vector< tco_expression *> parameterList;
tco_expression *function = const_cast<SNI_Function *>(this);
while (!function->IsValue())
{
function = function->DoCall(parameterList);
}
return function;
}
tco_expresion *DoCall(vector<tco_expresion *> &p_ParameterList)
{
p_ParameterList.push_back(m_Parameter);
return m_Function;
}
};
class tco_if : public tco_function
{
private:
tco_expresion *m_Condition;
tco_expresion *m_Positive;
tco_expresion *m_Negative;
tco_expresion *DoEvaluate() const
{
if (m_Condition.DoEvaluate()->IsTrue())
{
return m_Positive;
}
else
{
return m_Negative;
}
}
}