বা আপনি যদি কোনও ভিডিও গেম খেলেন, সেখানে প্রচুর অক্ষরগুলির অবস্থানের সাথে শুরু করে প্রচুর রাষ্ট্রীয় চলক রয়েছে, যারা নিয়মিত ঘুরে বেড়ায়। পরিবর্তিত মূল্যবোধের ট্র্যাক না রেখে আপনি কীভাবে দরকারী কিছু করতে পারেন?
আপনি যদি আগ্রহী হন তবে এখানে একটি ধারাবাহিক নিবন্ধ রয়েছে যা এরলংয়ের সাথে গেম প্রোগ্রামিংয়ের বর্ণনা দেয়।
আপনি সম্ভবত এই উত্তরটি মত নয়, কিন্তু তুমি কি করবে পেতে কার্মিক প্রোগ্রাম যতক্ষণ না আপনি এটি ব্যবহার। আমি কোডের নমুনাগুলি পোস্ট করতে এবং "এখানে, আপনি দেখতে পাচ্ছেন না" বলতে পারি - তবে আপনি যদি বাক্য গঠন এবং অন্তর্নিহিত নীতিগুলি বুঝতে না পারেন তবে আপনার চোখগুলি কেবলই ঝলমলে। আপনার দৃষ্টিকোণ থেকে, দেখে মনে হচ্ছে যে আমি একই কাজটি একটি অত্যাবশ্যকীয় ভাষা হিসাবে করছি, তবে উদ্দেশ্যমূলকভাবে প্রোগ্রামিংকে আরও কঠিন করে তুলতে কেবল সমস্ত ধরণের সীমানা স্থাপন করছি। আমার দৃষ্টিকোণ, আপনি কেবল ব্লব প্যারাডক্সের অভিজ্ঞতা নিচ্ছেন ।
আমি প্রথমে সংশয়ী ছিলাম, তবে কয়েক বছর আগে আমি কার্যকরী প্রোগ্রামিং ট্রেনে ঝাঁপিয়ে পড়েছিলাম এবং এর প্রেমে পড়েছি। ফাংশনাল প্রোগ্রামিংয়ের কৌশলটি নিদর্শনগুলি, নির্দিষ্ট ভেরিয়েবল অ্যাসাইনমেন্টগুলি সনাক্ত করতে এবং প্রয়োজনীয় স্টেটকে স্ট্যাকে স্থানান্তর করতে সক্ষম হচ্ছে। একটি লুপ, উদাহরণস্বরূপ, পুনরাবৃত্তি হয়:
// Imperative
let printTo x =
for a in 1 .. x do
printfn "%i" a
// Recursive
let printTo x =
let rec loop a = if a <= x then printfn "%i" a; loop (a + 1)
loop 1
এটি খুব সুন্দর নয়, তবে কোনও রূপান্তর ছাড়াই আমরা একই প্রভাব পেয়েছি। অবশ্যই, যেখানেই সম্ভব, আমরা সম্পূর্ণরূপে লুপিং এড়ানো পছন্দ করি এবং এটি কেবল বিমূর্তভাবে দূরে সরিয়ে রাখি:
// Preferred
let printTo x = seq { 1 .. x } |> Seq.iter (fun a -> printfn "%i" a)
Seq.iter পদ্ধতি সংগ্রহের মাধ্যমে গণনা করা এবং প্রতিটি আইটেমের জন্য বেনাম ফাংশন প্রার্থনা করবে। খুব সহজ :)
আমি জানি, মুদ্রণ সংখ্যা হুবহু চিত্তাকর্ষক নয়। তবে আমরা গেমসের সাথে একই পন্থাটি ব্যবহার করতে পারি: সমস্ত স্ট্যাকটি স্ট্যাকের মধ্যে ধরে রাখি এবং পুনরাবৃত্তির কলটিতে আমাদের পরিবর্তনগুলি সহ একটি নতুন অবজেক্ট তৈরি করে। এইভাবে, প্রতিটি ফ্রেম গেমের একটি স্টেটলেস স্ন্যাপশট, যেখানে প্রতিটি ফ্রেম কেবল স্টেটলেস অবজেক্টকে আপডেট করার প্রয়োজন অনুযায়ী কাঙ্ক্ষিত পরিবর্তন সহ একদম নতুন অবজেক্ট তৈরি করে। এর জন্য সিউডোকোড হতে পারে:
// imperative version
pacman = new pacman(0, 0)
while true
if key = UP then pacman.y++
elif key = DOWN then pacman.y--
elif key = LEFT then pacman.x--
elif key = UP then pacman.x++
render(pacman)
// functional version
let rec loop pacman =
render(pacman)
let x, y = switch(key)
case LEFT: pacman.x - 1, pacman.y
case RIGHT: pacman.x + 1, pacman.y
case UP: pacman.x, pacman.y - 1
case DOWN: pacman.x, pacman.y + 1
loop(new pacman(x, y))
অপরিহার্য ও কার্যকরী সংস্করণগুলি অভিন্ন, তবে কার্যকরী সংস্করণ স্পষ্টভাবে কোনও পরিবর্তনীয় অবস্থা ব্যবহার করে না। কার্যকরী কোড সমস্ত রাজ্যটিকে স্ট্যাকের মধ্যে রাখে - এই পদ্ধতির সুন্দর জিনিসটি হ'ল, যদি কিছু ভুল হয়ে যায় তবে ডিবাগ করা সহজ, আপনার যা দরকার তা হ'ল স্ট্যাক ট্রেস।
গেমের যেকোন সংখ্যক অবজেক্ট পর্যন্ত এটি স্কেল করে, কারণ সমস্ত বস্তু (বা সম্পর্কিত বস্তুর সংগ্রহ) তাদের নিজস্ব থ্রেডে রেন্ডার করা যেতে পারে।
আমি যে প্রতিটি ব্যবহারকারীর অ্যাপ্লিকেশন সম্পর্কে ভাবতে পারি তার মধ্যে একটি মূল ধারণা হিসাবে রাষ্ট্র জড়িত।
ক্রিয়ামূলক ভাষায়, বস্তুর স্থিতির পরিবর্তনের পরিবর্তে আমরা আমাদের পরিবর্তনের মাধ্যমে একটি নতুন অবজেক্টটি সহজেই ফিরিয়ে আনি। এটি যত বেশি শোনাচ্ছে তত বেশি দক্ষ। উদাহরণস্বরূপ, ডেটা স্ট্রাকচারগুলি অপরিবর্তনীয় ডেটা স্ট্রাকচার হিসাবে উপস্থাপন করা খুব সহজ। স্ট্যাকস, উদাহরণস্বরূপ, কুখ্যাতভাবে কার্যকর করা সহজ:
using System;
namespace ConsoleApplication1
{
static class Stack
{
public static Stack<T> Cons<T>(T hd, Stack<T> tl) { return new Stack<T>(hd, tl); }
public static Stack<T> Append<T>(Stack<T> x, Stack<T> y)
{
return x == null ? y : Cons(x.Head, Append(x.Tail, y));
}
public static void Iter<T>(Stack<T> x, Action<T> f) { if (x != null) { f(x.Head); Iter(x.Tail, f); } }
}
class Stack<T>
{
public readonly T Head;
public readonly Stack<T> Tail;
public Stack(T hd, Stack<T> tl)
{
this.Head = hd;
this.Tail = tl;
}
}
class Program
{
static void Main(string[] args)
{
Stack<int> x = Stack.Cons(1, Stack.Cons(2, Stack.Cons(3, Stack.Cons(4, null))));
Stack<int> y = Stack.Cons(5, Stack.Cons(6, Stack.Cons(7, Stack.Cons(8, null))));
Stack<int> z = Stack.Append(x, y);
Stack.Iter(z, a => Console.WriteLine(a));
Console.ReadKey(true);
}
}
}
উপরের কোডটি দুটি অপরিবর্তনীয় তালিকা তৈরি করে, তাদের একটি নতুন তালিকা তৈরি করতে সংযোজন করে এবং ফলাফল সংযোজন করে। অ্যাপ্লিকেশনটিতে কোথাও কোনও পরিবর্তনীয় অবস্থা ব্যবহার করা হয় না। এটি দেখতে কিছুটা ভারী মনে হচ্ছে, তবে এটি কেবল কারণ সি # একটি ভার্চুজের ভাষা। এফ # তে সমতুল্য প্রোগ্রামটি এখানে:
type 'a stack =
| Cons of 'a * 'a stack
| Nil
let rec append x y =
match x with
| Cons(hd, tl) -> Cons(hd, append tl y)
| Nil -> y
let rec iter f = function
| Cons(hd, tl) -> f(hd); iter f tl
| Nil -> ()
let x = Cons(1, Cons(2, Cons(3, Cons(4, Nil))))
let y = Cons(5, Cons(6, Cons(7, Cons(8, Nil))))
let z = append x y
iter (fun a -> printfn "%i" a) z
তালিকাগুলি তৈরি এবং পরিচালনা করার জন্য কোনও পরিবর্তনীয় প্রয়োজন। প্রায় সমস্ত ডেটা স্ট্রাকচার সহজেই তাদের কার্যকরী সমতুল্যে রূপান্তরিত হতে পারে। আমি এখানে একটি পৃষ্ঠা লিখেছি যা স্ট্যাকস, সারি, বামপন্থী apগল, লাল-কালো গাছ, অলস তালিকাগুলির অপরিবর্তনীয় বাস্তবায়ন সরবরাহ করে। কোডের একক স্নিপেটে কোনও পরিবর্তনীয় স্থিতি নেই। একটি গাছ "রূপান্তর" করতে আমি একটি নতুন নোড তৈরি করতে চাই যা আমি চাই - এটি খুব কার্যকরী কারণ আমার গাছের প্রতিটি নোডের অনুলিপি তৈরি করার দরকার নেই, আমি আমার নতুনতে পুরানোগুলি পুনরায় ব্যবহার করতে পারি গাছ।
আরও তাত্পর্যপূর্ণ উদাহরণ ব্যবহার করে, আমি এই এসকিউএল পার্সারটিও লিখেছিলাম যা সম্পূর্ণ স্টেটলেস (বা অন্তত আমার কোডটি স্টেটলেস, আমি অন্তর্নিহিত লেক্সিং লাইব্রেরি স্টেটলেস কিনা তা জানি না)।
স্টেটলেস প্রোগ্রামিং হ'ল স্টেটফুল প্রোগ্রামিংয়ের মতোই ভাববাদী এবং শক্তিশালী, রাষ্ট্রহীন চিন্তাভাবনা শুরু করার জন্য নিজেকে প্রশিক্ষণ দেওয়ার জন্য একটু অনুশীলন প্রয়োজন। অবশ্যই, "স্টেটলেস প্রোগ্রামিং যখন সম্ভব হয়, স্টেটফুল প্রোগ্রামিং যেখানে প্রয়োজন" বেশিরভাগ অপরিষ্কার কার্যকরী ভাষার মূলমন্ত্র বলে মনে হয়। কার্যকরী পদ্ধতির যতটা পরিষ্কার বা দক্ষ না হয় তখন পরিবর্তনীয়দের পিছনে ফিরে যাওয়ার কোনও ক্ষতি নেই।