সি # তে সরল রাষ্ট্রের যন্ত্র উদাহরণ?


257

হালনাগাদ:

উদাহরণগুলির জন্য আবারও ধন্যবাদ, তারা খুব সহায়ক হয়েছে এবং নিম্নলিখিতগুলি সহ আমি এগুলি থেকে কোনও কিছুই সরিয়ে নেওয়ার অর্থ নেই।

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

বা আমার কাছে কী রাষ্ট্রীয় মেশিন এবং তাদের সাধারণ ব্যবহার সম্পর্কে ভুল ধারণা রয়েছে?

শুভেচ্ছান্তে


মূল প্রশ্ন:

আমি স্টেট মেশিনগুলি এবং রাষ্ট্রীয় মেশিনগুলি তৈরি করার সরঞ্জামগুলিতে স্টেট মেশিন এবং পুনরাবৃত্তকারী ব্লকগুলি এবং সি # এর জন্য কী নয় সেগুলি সম্পর্কে এই আলোচনাটি পেয়েছি , তাই আমি প্রচুর বিমূর্ত জিনিস পেয়েছি তবে একটি নুব হিসাবে এটি সমস্ত কিছু বিভ্রান্তিকর।

সুতরাং এটি দুর্দান্ত হবে যদি কেউ একটি সি # উত্স কোড-উদাহরণ সরবরাহ করতে পারে যা সম্ভবত একটি সরল রাষ্ট্রীয় মেশিনকে অনুধাবন করতে পারে 3,4 টি রাজ্য, কেবল এর সংক্ষেপণ পেতে।



আপনি কি রাষ্ট্রীয় মেশিনগুলি সম্পর্কে সাধারণ বা কেবল পুনরাবৃত্তভিত্তিক ভিত্তিকগুলি সম্পর্কে ভাবছেন?
স্কুরমেডেল

2
এখানে নেট কোর স্টেটলেস লিবিউ রয়েছে উদাহরণ সহ, ডিএজিএস ডাইগ্রাম ইত্যাদি - পর্যালোচনা করার মতো: hanselman.com/blog/…
zmische

উত্তর:


416

আসুন এই সাধারণ রাষ্ট্র চিত্রটি দিয়ে শুরু করুন:

সরল রাষ্ট্র মেশিন ডায়াগ্রাম

আমাদের আছে:

  • 4 টি রাষ্ট্র (নিষ্ক্রিয়, সক্রিয়, বিরামপ্রাপ্ত এবং প্রস্থানিত)
  • 5 ধরণের রাষ্ট্রীয় রূপান্তর (বিগিনাল কমান্ড, শেষ কমান্ড, বিরতি আদেশ, পুনরায় সূচনা কমান্ড, প্রস্থান কমান্ড)।

আপনি মুষ্টিমেয় উপায়ে এটি সি # তে রূপান্তর করতে পারেন, যেমন বর্তমান অবস্থা এবং কমান্ডের উপর একটি স্যুইচ স্টেটমেন্ট সম্পাদনা করা, বা কোনও রূপান্তর সারণীতে ট্রানজিশন সন্ধান করা। এই সাধারণ রাষ্ট্র মেশিনের জন্য, আমি একটি রূপান্তর টেবিলটিকে পছন্দ করি, যা একটি ব্যবহার করে প্রতিনিধিত্ব করা খুব সহজ Dictionary:

using System;
using System.Collections.Generic;

namespace Juliet
{
    public enum ProcessState
    {
        Inactive,
        Active,
        Paused,
        Terminated
    }

    public enum Command
    {
        Begin,
        End,
        Pause,
        Resume,
        Exit
    }

    public class Process
    {
        class StateTransition
        {
            readonly ProcessState CurrentState;
            readonly Command Command;

            public StateTransition(ProcessState currentState, Command command)
            {
                CurrentState = currentState;
                Command = command;
            }

            public override int GetHashCode()
            {
                return 17 + 31 * CurrentState.GetHashCode() + 31 * Command.GetHashCode();
            }

            public override bool Equals(object obj)
            {
                StateTransition other = obj as StateTransition;
                return other != null && this.CurrentState == other.CurrentState && this.Command == other.Command;
            }
        }

        Dictionary<StateTransition, ProcessState> transitions;
        public ProcessState CurrentState { get; private set; }

        public Process()
        {
            CurrentState = ProcessState.Inactive;
            transitions = new Dictionary<StateTransition, ProcessState>
            {
                { new StateTransition(ProcessState.Inactive, Command.Exit), ProcessState.Terminated },
                { new StateTransition(ProcessState.Inactive, Command.Begin), ProcessState.Active },
                { new StateTransition(ProcessState.Active, Command.End), ProcessState.Inactive },
                { new StateTransition(ProcessState.Active, Command.Pause), ProcessState.Paused },
                { new StateTransition(ProcessState.Paused, Command.End), ProcessState.Inactive },
                { new StateTransition(ProcessState.Paused, Command.Resume), ProcessState.Active }
            };
        }

        public ProcessState GetNext(Command command)
        {
            StateTransition transition = new StateTransition(CurrentState, command);
            ProcessState nextState;
            if (!transitions.TryGetValue(transition, out nextState))
                throw new Exception("Invalid transition: " + CurrentState + " -> " + command);
            return nextState;
        }

        public ProcessState MoveNext(Command command)
        {
            CurrentState = GetNext(command);
            return CurrentState;
        }
    }


    public class Program
    {
        static void Main(string[] args)
        {
            Process p = new Process();
            Console.WriteLine("Current State = " + p.CurrentState);
            Console.WriteLine("Command.Begin: Current State = " + p.MoveNext(Command.Begin));
            Console.WriteLine("Command.Pause: Current State = " + p.MoveNext(Command.Pause));
            Console.WriteLine("Command.End: Current State = " + p.MoveNext(Command.End));
            Console.WriteLine("Command.Exit: Current State = " + p.MoveNext(Command.Exit));
            Console.ReadLine();
        }
    }
}

ব্যক্তিগত পছন্দের বিষয় হিসাবে, আমি আমার রাজ্য মেশিনগুলি GetNextপরের রাজ্যকে নির্বিচারে ফিরিয়ে আনার জন্য একটি MoveNextফাংশন এবং রাষ্ট্রীয় মেশিনকে পরিবর্তিত করার জন্য একটি ফাংশন দিয়ে ডিজাইন করতে চাই ।


65
GetHashCode()প্রাইমগুলি ব্যবহারের সঠিক প্রয়োগের জন্য +1 ।
j72 72

13
আপনি দয়া করে আমাকে গেটহ্যাশকোডের উদ্দেশ্য ব্যাখ্যা করতে পারেন ()?
সিদ্ধার্থ

14
@ সিদ্ধার্থ: StateTransitionক্লাসটি অভিধানে কী হিসাবে ব্যবহৃত হয় এবং কীগুলির সমতা গুরুত্বপূর্ণ। দুটি পৃথক দৃষ্টান্তকে StateTransitionসমান হিসাবে বিবেচনা করা উচিত যতক্ষণ না তারা একই রূপান্তর উপস্থাপন করে (যেমন CurrentStateএবং Commandএকই)। সাম্যতা প্রয়োগ করতে আপনাকে Equalsপাশাপাশি ওভাররাইড করতে হবে GetHashCode। বিশেষত অভিধানে হ্যাশ কোডটি ব্যবহার করা হবে এবং দুটি সমান বস্তুকে অবশ্যই একই হ্যাশ কোডটি ফেরত দিতে হবে। আপনি খুব ভাল পারফরম্যান্স পাবেন যদি না অনেকগুলি অ-সমতুল্য অবজেক্ট একই হ্যাশ কোডটি ভাগ করে নেয় যার কারণে GetHashCodeপ্রদর্শিত হিসাবে প্রয়োগ করা হয়।
মার্টিন লিভারেজ

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

2
কারও যদি এটির প্রয়োজন হয়: আমি এই টেট মেশিনটি সামঞ্জস্য করেছি এবং এটি আমার unityক্য গেমটিতে ব্যবহার করেছি। এটি গিট হাবটিতে
Max_Power89

73

আপনি বিদ্যমান ওপেন সোর্স ফিনিট স্টেট মেশিনগুলির মধ্যে একটি ব্যবহার করতে পারেন। উদাহরণস্বরূপ bbv.Common.StateMachine http://code.google.com/p/bbvcommon/wiki/StateMachine এ পাওয়া গেছে । এটির একটি স্বজ্ঞাত সাবলীল সিনট্যাক্স এবং প্রচুর বৈশিষ্ট্য রয়েছে যেমন, প্রবেশ / প্রস্থান ক্রিয়া, স্থানান্তর ক্রিয়া, প্রহরী, শ্রেণিবদ্ধ, প্যাসিভ বাস্তবায়ন (কলারের থ্রেডে চালিত) এবং সক্রিয় বাস্তবায়ন (নিজস্ব থ্রেড যার উপর fsm চলে, ইভেন্টগুলি একটি কাতারে যুক্ত করা হয়)।

জুলিয়েটদের উদাহরণ গ্রহণ করা রাষ্ট্রের যন্ত্রটির সংজ্ঞাটি খুব সহজ হয়ে যায়:

var fsm = new PassiveStateMachine<ProcessState, Command>();
fsm.In(ProcessState.Inactive)
   .On(Command.Exit).Goto(ProcessState.Terminated).Execute(SomeTransitionAction)
   .On(Command.Begin).Goto(ProcessState.Active);
fsm.In(ProcessState.Active)
   .ExecuteOnEntry(SomeEntryAction)
   .ExecuteOnExit(SomeExitAction)
   .On(Command.End).Goto(ProcessState.Inactive)
   .On(Command.Pause).Goto(ProcessState.Paused);
fsm.In(ProcessState.Paused)
   .On(Command.End).Goto(ProcessState.Inactive).OnlyIf(SomeGuard)
   .On(Command.Resume).Goto(ProcessState.Active);
fsm.Initialize(ProcessState.Inactive);
fsm.Start();

fsm.Fire(Command.Begin);

আপডেট : প্রকল্পের অবস্থানটিতে স্থানান্তরিত হয়েছে: https://github.com/appccelerate/statemachine


4
এই দুর্দান্ত ওপেন সোর্স স্টেট মেশিনটি উল্লেখ করার জন্য আপনাকে ধন্যবাদ। আমি কী জিজ্ঞাসা করতে পারি আমি কীভাবে বর্তমান অবস্থা পেতে পারি?
রমজান পোলাট

2
আপনি পারবেন না এবং আপনার করা উচিত নয়। রাজ্য হ'ল অস্থির কিছু। আপনি যখন রাষ্ট্রটিকে অনুরোধ করবেন তখন সম্ভব হয় যে আপনি কোনও উত্তরণের মাঝখানে রয়েছেন। সমস্ত ক্রিয়াগুলি রূপান্তর, রাজ্যের প্রবেশ এবং রাষ্ট্রের প্রস্থানের মধ্যে করা উচিত। আপনি যদি সত্যই এই রাজ্যটি পেতে চান তবে আপনি একটি স্থানীয় ক্ষেত্র যুক্ত করতে এবং একটি প্রবেশের ক্রিয়ায় রাজ্যকে নির্ধারিত করতে পারেন।
রেমো গ্লোর

4
প্রশ্নটি আপনার "দরকার" কীসের জন্য এবং আপনার যদি সত্যিই এসএম রাষ্ট্র বা অন্য কোনও ধরণের রাজ্যের প্রয়োজন হয়। উদাহরণস্বরূপ, যদি আপনার কিছু ডিসপ্লে পাঠ্যের প্রয়োজন হয় তবে কয়েকটি বর্ণিতগুলির একই ডিসপ্লের পাঠ্য থাকতে পারে উদাহরণস্বরূপ যদি প্রেরণের জন্য প্রস্তুত করার জন্য একাধিক সাব স্টেট থাকে। এই ক্ষেত্রে আপনি যা করতে চান তা ঠিক আপনার করা উচিত। সঠিক জায়গায় কিছু প্রদর্শন পাঠ্য আপডেট করুন। উদাহরণস্বরূপ এক্সিকিউটঅনট্রি-র মধ্যে। আপনার যদি আরও তথ্যের প্রয়োজন হয় তবে একটি নতুন প্রশ্ন জিজ্ঞাসা করুন এবং ঠিক এখানে আপনার সমস্যাটি প্রকাশিত হওয়ায় আপনার সমস্যাটি ঠিক করুন।
রেমো গ্লোর

ঠিক আছে আমি একটি নতুন প্রশ্ন জিজ্ঞাসা করছি এবং আপনার উত্তর দেওয়ার জন্য অপেক্ষা করছি। কারণ আমি মনে করি না যে আপনার কাছে সেরা উত্তর থাকলেও অন্য কেউ এই সমস্যার সমাধান করছেন তবে এখনও প্রশ্নকর্তা গ্রহণ করেন নি। আমি এখানে প্রশ্ন url পোস্ট করব। ধন্যবাদ।
রমজান পোলাট

4
সাবলীল এবং ঘোষিত এপিআই এর জন্য +1। এটা অসাধারণ. বিটিডাব্লু, গুগল কোডটি পুরানো বলে মনে হচ্ছে। তাদের নতুন প্রকল্পের সাইটটি এখানে
কেএফএল

51

এখানে একটি খুব ক্লাসিক সসীম রাষ্ট্র মেশিনের উদাহরণ রয়েছে, খুব সরলীকৃত বৈদ্যুতিন ডিভাইসের মডেলিং (একটি টিভির মতো)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace fsm
{
class Program
{
    static void Main(string[] args)
    {
        var fsm = new FiniteStateMachine();
        Console.WriteLine(fsm.State);
        fsm.ProcessEvent(FiniteStateMachine.Events.PlugIn);
        Console.WriteLine(fsm.State);
        fsm.ProcessEvent(FiniteStateMachine.Events.TurnOn);
        Console.WriteLine(fsm.State);
        fsm.ProcessEvent(FiniteStateMachine.Events.TurnOff);
        Console.WriteLine(fsm.State);
        fsm.ProcessEvent(FiniteStateMachine.Events.TurnOn);
        Console.WriteLine(fsm.State);
        fsm.ProcessEvent(FiniteStateMachine.Events.RemovePower);
        Console.WriteLine(fsm.State);
        Console.ReadKey();
    }

    class FiniteStateMachine
    {
        public enum States { Start, Standby, On };
        public States State { get; set; }

        public enum Events { PlugIn, TurnOn, TurnOff, RemovePower };

        private Action[,] fsm;

        public FiniteStateMachine()
        {
            this.fsm = new Action[3, 4] { 
                //PlugIn,       TurnOn,                 TurnOff,            RemovePower
                {this.PowerOn,  null,                   null,               null},              //start
                {null,          this.StandbyWhenOff,    null,               this.PowerOff},     //standby
                {null,          null,                   this.StandbyWhenOn, this.PowerOff} };   //on
        }
        public void ProcessEvent(Events theEvent)
        {
            this.fsm[(int)this.State, (int)theEvent].Invoke();
        }

        private void PowerOn() { this.State = States.Standby; }
        private void PowerOff() { this.State = States.Start; }
        private void StandbyWhenOn() { this.State = States.Standby; }
        private void StandbyWhenOff() { this.State = States.On; }
    }
}
}

6
রাষ্ট্রীয় মেশিনে নতুন যে কারও জন্য, প্রথমে পা ভিজানোর জন্য এটি একটি দুর্দান্ত প্রথম উদাহরণ।
PositiveGuy

2
আমি রাষ্ট্রের মেশিনে নতুন এবং গুরুত্ব সহকারে, এটি আমার কাছে আলো এনেছে - ধন্যবাদ!
এমসি 5

1
আমি এই বাস্তবায়ন পছন্দ। এতে যে কেউ হোঁচট খেতে পারে তার জন্য, কিছুটা "উন্নতি"। এফএসএম ক্লাসে, আমি নালীর private void DoNothing() {return;}সমস্ত দৃষ্টান্ত যুক্ত এবং প্রতিস্থাপন করেছি this.DoNothing। বর্তমান অবস্থায় ফিরে আসার মনোরম পার্শ্ব প্রতিক্রিয়া রয়েছে।
শেঠমো 1111

1
আমি ভাবছি যে এইগুলির কিছুটির পিছনে কোনও যুক্তি আছে কিনা। আমি যখন এটি দেখি, আমার প্রথম স্বজ্ঞাত হ'ল এর উপাদানগুলির নাম পরিবর্তন Statesকরা Unpowered, Standby, On। আমার যুক্তিটি হ'ল যদি কেউ আমাকে জিজ্ঞাসা করে আমার টেলিভিশনটি কোন অবস্থানে রয়েছে, আমি "অফ" বলব এবং "স্টার্ট" না। আমিও পরিবর্তিত StandbyWhenOnএবং StandbyWhenOffকরতে TurnOnএবং TurnOff। এটি কোডটি আরও স্বজ্ঞাতভাবে পড়তে বাধ্য করে, তবে আমি আশ্চর্য হয়েছি যে কোনও সম্মেলন বা অন্যান্য কারণ রয়েছে যা আমার পরিভাষাটিকে কম উপযুক্ত করে তোলে।
জেসন হামজে

যুক্তিসঙ্গত বলে মনে হচ্ছে, আমি আসলে কোনও রাষ্ট্রীয় নামকরণের সম্মেলন অনুসরণ করছিলাম না; নাম হিসাবে আপনি মডেল যাই হোক না কেন এটি বোঝায়।
পিট স্টেনসনেস

20

কিছু নির্লজ্জ স্ব-প্রচার এখানে, তবে কিছুক্ষণ আগে আমি ইয়েল্ডম্যাচাইন নামে একটি লাইব্রেরি তৈরি করেছি যা একটি সীমাবদ্ধ-জটিলতার রাষ্ট্রীয় মেশিনকে খুব পরিষ্কার এবং সাধারণ উপায়ে বর্ণনা করতে দেয়। উদাহরণস্বরূপ, একটি বাতি বিবেচনা করুন:

একটি প্রদীপ রাষ্ট্র মেশিন

লক্ষ্য করুন যে এই রাষ্ট্রের মেশিনটিতে 2 টি ট্রিগার এবং 3 টি রাজ্য রয়েছে। ইয়েলডম্যাচাইন কোডে, আমরা রাষ্ট্র সম্পর্কিত সমস্ত আচরণের জন্য একটি একক পদ্ধতি লিখি, যার মধ্যে আমরা gotoপ্রতিটি রাজ্যের জন্য ব্যবহার করার ভয়াবহ নৃশংসতা সম্পাদন করি । একটি ট্রিগার একটি সম্পত্তি বা প্রকারের ক্ষেত্র হয়ে ওঠে Action, নামক একটি অ্যাট্রিবিউট দিয়ে সজ্জিত Trigger। আমি প্রথম রাষ্ট্রের কোড এবং নীচে এর রূপান্তরগুলি মন্তব্য করেছি; পরবর্তী রাজ্যগুলি একই প্যাটার্ন অনুসরণ করে।

public class Lamp : StateMachine
{
    // Triggers (or events, or actions, whatever) that our
    // state machine understands.
    [Trigger]
    public readonly Action PressSwitch;

    [Trigger]
    public readonly Action GotError;

    // Actual state machine logic
    protected override IEnumerable WalkStates()
    {
    off:                                       
        Console.WriteLine("off.");
        yield return null;

        if (Trigger == PressSwitch) goto on;
        InvalidTrigger();

    on:
        Console.WriteLine("*shiiine!*");
        yield return null;

        if (Trigger == GotError) goto error;
        if (Trigger == PressSwitch) goto off;
        InvalidTrigger();

    error:
        Console.WriteLine("-err-");
        yield return null;

        if (Trigger == PressSwitch) goto off;
        InvalidTrigger();
    }
}

সংক্ষিপ্ত এবং সুন্দর, আহ!

এই রাষ্ট্রীয় মেশিনটি কেবল এতে ট্রিগার প্রেরণ দ্বারা নিয়ন্ত্রণ করা হয়:

var sm = new Lamp();
sm.PressSwitch(); //go on
sm.PressSwitch(); //go off

sm.PressSwitch(); //go on
sm.GotError();    //get error
sm.PressSwitch(); //go off

কেবল স্পষ্ট করে বলতে, আমি কীভাবে এটি ব্যবহার করতে হয় তা বুঝতে সাহায্য করার জন্য আমি প্রথম রাজ্যে কিছু মন্তব্য যুক্ত করেছি।

    protected override IEnumerable WalkStates()
    {
    off:                                       // Each goto label is a state

        Console.WriteLine("off.");             // State entry actions

        yield return null;                     // This means "Wait until a 
                                               // trigger is called"

                                               // Ah, we got triggered! 
                                               //   perform state exit actions 
                                               //   (none, in this case)

        if (Trigger == PressSwitch) goto on;   // Transitions go here: 
                                               // depending on the trigger 
                                               // that was called, go to
                                               // the right state

        InvalidTrigger();                      // Throw exception on 
                                               // invalid trigger

        ...

এটি কাজ করে কারণ সি # সংকলক প্রকৃতপক্ষে প্রতিটি পদ্ধতির ব্যবহারের জন্য অভ্যন্তরীণভাবে একটি স্টেট মেশিন তৈরি করেছে yield return। এই কনস্ট্রাক্টটি অলসভাবে উপাত্তের সিক্যুয়েন্স তৈরি করতে ব্যবহৃত হয়, তবে এই ক্ষেত্রে আমরা আসলে ফিরে আসা সিকোয়েন্সগুলিতে আগ্রহী নই (যা যাইহোক সমস্ত নাল হয়), তবে রাষ্ট্রের আচরণে যা হুডের নীচে তৈরি হয়।

StateMachineবেস বর্গ প্রত্যেকটিতে নির্ধারণ কোডে নির্মাণ উপর কিছু প্রতিফলন করে [Trigger]কর্ম, যা সেট করে Triggerসদস্য ও রাষ্ট্র মেশিন এগিয়ে আসে।

এটি ব্যবহার করতে সক্ষম হওয়ার জন্য আপনার প্রকৃতপক্ষে সত্যিকারের বোঝার দরকার নেই।


2
"গোটো" কেবলমাত্র অত্যাচারী যদি এটি পদ্ধতির মধ্যে লাফ দেয়। ভাগ্যক্রমে, সি # তে অনুমোদিত নয়।
ব্র্যানন

ভাল যুক্তি! প্রকৃতপক্ষে, কোনও স্ট্যাটিকালি টাইপ করা ভাষা যদি gotoপদ্ধতিগুলির মধ্যে মঞ্জুরি দেয় তবে আমি খুব মুগ্ধ হব ।
স্ক্রাববেল

3
@ ব্রানন: কোন ভাষা gotoপদ্ধতির মধ্যে ঝাঁপিয়ে পড়তে দেয় ? কীভাবে এটি সম্ভবত কাজ করবে তা আমি দেখছি না। না,goto সমস্যাযুক্ত কারণ এটি পদ্ধতিগত প্রোগ্রামিংয়ের ফলাফল দেয় (এটি নিজেই ইউনিট টেস্টিংয়ের মতো দুর্দান্ত জিনিসগুলিকে জটিল InvalidTriggerকরে তোলে) কোড পুনরাবৃত্তি প্রচার করে (লক্ষ্য করা যায় কীভাবে প্রতিটি রাজ্যের জন্য সন্নিবেশ করা প্রয়োজন?) এবং পরিশেষে প্রোগ্রামটি অনুসরণ করা আরও শক্ত করে তোলে। এটি এই থ্রেডের (সর্বাধিক) অন্যান্য সমাধানের সাথে তুলনা করুন এবং আপনি দেখতে পাবেন যে এটি কেবলমাত্র একমাত্র যেখানে সম্পূর্ণ পদ্ধতিতে এফএসএম ঘটে। এটি উদ্বেগ উত্থাপন করার জন্য যথেষ্ট।
গ্রো

1
উদাহরণস্বরূপ @ গ্রু, জিডাব্লু-বেসিক। এটি সহায়তা করে যে এটির পদ্ধতিগুলি বা এমনকি ফাংশন নেই। তদ্ব্যতীত, এই উদাহরণটিতে আপনি "প্রোগ্রাম প্রবাহকে আরও কঠোরভাবে অনুসরণ করতে" কেন খুঁজে পান তা বোঝার জন্য আমার খুব কষ্ট হয়েছে। এটি একটি রাষ্ট্রীয় মেশিন, অন্য একটির থেকে একটি রাজ্যে "যাওয়া" আপনার একমাত্র কাজ। এই মানচিত্র gotoবেশ ভাল।
স্ক্রেবল

3
জিডব্লু-বেসিক gotoফাংশনগুলির মধ্যে ঝাঁপ দেওয়ার অনুমতি দেয় , তবে এটি কার্যকারিতা সমর্থন করে না? :) আপনি ঠিক বলেছেন, "অনুসরণ করা শক্ত" মন্তব্যটি একটি সাধারণ gotoসমস্যা, প্রকৃতপক্ষে এই ক্ষেত্রে তেমন সমস্যা হয় না।
গ্রো

13

আপনি একটি পুনরাবৃত্তকারী ব্লক কোড করতে পারেন যা আপনাকে অর্কেস্ট্রেটেড ফ্যাশনে একটি কোড ব্লক কার্যকর করতে দেয়। কোড ব্লকটি কীভাবে ভেঙে গেছে তার কোনও কিছুর সাথে মিল নেই, আপনি এটি কোড করতে চান ঠিক তেমন। উদাহরণ স্বরূপ:

IEnumerable<int> CountToTen()
{
    System.Console.WriteLine("1");
    yield return 0;
    System.Console.WriteLine("2");
    System.Console.WriteLine("3");
    System.Console.WriteLine("4");
    yield return 0;
    System.Console.WriteLine("5");
    System.Console.WriteLine("6");
    System.Console.WriteLine("7");
    yield return 0;
    System.Console.WriteLine("8");
    yield return 0;
    System.Console.WriteLine("9");
    System.Console.WriteLine("10");
}

এই ক্ষেত্রে, আপনি যখন কাউন্টটোটেন কল করবেন তখন কিছুই আসলে কার্যকর হয় না। আপনি যা পান তা কার্যকরভাবে একটি স্টেট মেশিন জেনারেটর, যার জন্য আপনি রাষ্ট্র মেশিনের একটি নতুন উদাহরণ তৈরি করতে পারেন। আপনি getEnumerator () এ কল করে এটি করুন। ফলস্বরূপ আইনিমরেটর কার্যকরভাবে একটি স্টেট মেশিন যা আপনি মুভনেেক্সট (...) কল করে ড্রাইভ করতে পারেন।

সুতরাং, এই উদাহরণে, প্রথমবার আপনি মুভনেক্সটকে কল করুন (...) আপনি কনসোলে "1" লিখিত দেখতে পাবেন এবং পরের বার আপনি মুভনেক্সটকে কল করবেন (...) আপনি দেখতে পাবেন 2, 3, 4, এবং তারপরে ৫,,, and এবং তারপরে ৮, এবং তারপরে ৯, ১০. আপনি দেখতে পাচ্ছেন, কীভাবে ঘটনাগুলি ঘটতে হবে তা বাছাই করার জন্য এটি একটি কার্যকর প্রক্রিয়া।


6
বাধ্যতামূলক লিংক ন্যায্য সতর্কতা
sehe

8

আমি এখানে অন্য উত্তর পোস্ট করছি কারণ এটি ভিন্ন দৃষ্টিকোণ থেকে রাষ্ট্রীয় মেশিন; খুব চাক্ষুষ।

আমার মূল উত্তরটি ক্লাসিক আবশ্যক কোড। আমি মনে করি কোড হিসাবে এটি বেশ ভিজ্যুয়াল কারণ অ্যারে যা রাষ্ট্র মেশিনকে ভিজ্যুয়ালাইজিং সহজ করে। খারাপ দিকটি আপনাকে এই সমস্ত লিখতে হবে। রিমসের উত্তর বয়লার-প্লেট কোড লেখার প্রয়াসকে হ্রাস করে তবে এটি খুব কম দৃশ্যমান। তৃতীয় বিকল্প আছে; সত্যিই রাষ্ট্রীয় মেশিন অঙ্কন।

যদি আপনি .NET ব্যবহার করে থাকেন এবং রান সময়ের 4 য় সংস্করণ লক্ষ্য করতে পারেন তবে আপনার কাছে কর্মপ্রবাহের স্টেট মেশিন ক্রিয়াকলাপগুলি ব্যবহার করার বিকল্প রয়েছে । এগুলি সংক্ষেপে আপনাকে স্টেট মেশিনটি আঁকতে দেয় (অনেকটা জুলিয়েটের ডায়াগ্রামের মতো) এবং ডাব্লুএফ-র রান-টাইম এটি আপনার জন্য কার্যকর করতে দেয়।

আরও বিশদের জন্য উইন্ডোজ ওয়ার্কফ্লো ফাউন্ডেশন এবং এই কোডপ্লেক্স সাইটটির সাথে এমএসডিএন নিবন্ধ বিল্ডিং স্টেট মেশিনগুলি দেখুন সর্বশেষতম সংস্করণের জন্য ।

নেটটি টার্গেট করার সময় আমি সেই বিকল্পটি সবসময় পছন্দ করতাম কারণ নন প্রোগ্রামারগুলিকে এটি দেখতে, পরিবর্তন এবং ব্যাখ্যা করা সহজ; ছবিগুলি বলে হাজার হাজার শব্দের মূল্য!


আমি মনে করি, স্টেট মেশিনটি পুরো ওয়ার্কফ্লো ফাউন্ডেশনের অন্যতম সেরা অঙ্গ!
ফেবসনেট

7

এটি মনে রাখা দরকারী যে রাষ্ট্রীয় মেশিনগুলি একটি বিমূর্ততা, এবং একটি তৈরি করতে আপনার বিশেষ সরঞ্জামের প্রয়োজন নেই, তবে সরঞ্জামগুলি কার্যকর হতে পারে।

আপনি উদাহরণস্বরূপ ফাংশন সহ একটি রাষ্ট্রীয় মেশিন উপলব্ধি করতে পারেন:

void Hunt(IList<Gull> gulls)
{
    if (gulls.Empty())
       return;

    var target = gulls.First();
    TargetAcquired(target, gulls);
}

void TargetAcquired(Gull target, IList<Gull> gulls)
{
    var balloon = new WaterBalloon(weightKg: 20);

    this.Cannon.Fire(balloon);

    if (balloon.Hit)
    {
       TargetHit(target, gulls);
    }
    else
       TargetMissed(target, gulls);
}

void TargetHit(Gull target, IList<Gull> gulls)
{
    Console.WriteLine("Suck on it {0}!", target.Name);
    Hunt(gulls);
}

void TargetMissed(Gull target, IList<Gull> gulls)
{
    Console.WriteLine("I'll get ya!");
    TargetAcquired(target, gulls);
}

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

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

একটি সাধারণ উপায় হ'ল রাজ্যের প্রতিনিধিত্ব করতে ক্লাস ব্যবহার করা এবং তারপরে তাদের বিভিন্ন উপায়ে সংযুক্ত করা।


7

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

http://gamedevelopment.tutsplus.com/tutorials/finite-state-machines-theory-and-implementation--gamedev-11867

টিউটোরিয়ালটি ভাষা অজ্ঞেয়গত, তাই এটি সহজেই আপনার সি # প্রয়োজনীয়তার সাথে খাপ খাইয়ে নিতে পারে।

এছাড়াও, ব্যবহৃত উদাহরণ (খাবারের জন্য পিঁপড়ে) বোঝা সহজ।


টিউটোরিয়াল থেকে:

এখানে চিত্র বর্ণনা লিখুন

public class FSM {
    private var activeState :Function; // points to the currently active state function

    public function FSM() {
    }

    public function setState(state :Function) :void {
        activeState = state;
    }

    public function update() :void {
        if (activeState != null) {
            activeState();
        }
    }
}


public class Ant
{
    public var position   :Vector3D;
    public var velocity   :Vector3D;
    public var brain      :FSM;

    public function Ant(posX :Number, posY :Number) {
        position    = new Vector3D(posX, posY);
        velocity    = new Vector3D( -1, -1);
        brain       = new FSM();

        // Tell the brain to start looking for the leaf.
        brain.setState(findLeaf);
    }

    /**
    * The "findLeaf" state.
    * It makes the ant move towards the leaf.
    */
    public function findLeaf() :void {
        // Move the ant towards the leaf.
        velocity = new Vector3D(Game.instance.leaf.x - position.x, Game.instance.leaf.y - position.y);

        if (distance(Game.instance.leaf, this) <= 10) {
            // The ant is extremelly close to the leaf, it's time
            // to go home.
            brain.setState(goHome);
        }

        if (distance(Game.mouse, this) <= MOUSE_THREAT_RADIUS) {
            // Mouse cursor is threatening us. Let's run away!
            // It will make the brain start calling runAway() from
            // now on.
            brain.setState(runAway);
        }
    }

    /**
    * The "goHome" state.
    * It makes the ant move towards its home.
    */
    public function goHome() :void {
        // Move the ant towards home
        velocity = new Vector3D(Game.instance.home.x - position.x, Game.instance.home.y - position.y);

        if (distance(Game.instance.home, this) <= 10) {
            // The ant is home, let's find the leaf again.
            brain.setState(findLeaf);
        }
    }

    /**
    * The "runAway" state.
    * It makes the ant run away from the mouse cursor.
    */
    public function runAway() :void {
        // Move the ant away from the mouse cursor
        velocity = new Vector3D(position.x - Game.mouse.x, position.y - Game.mouse.y);

        // Is the mouse cursor still close?
        if (distance(Game.mouse, this) > MOUSE_THREAT_RADIUS) {
            // No, the mouse cursor has gone away. Let's go back looking for the leaf.
            brain.setState(findLeaf);
        }
    }

    public function update():void {
        // Update the FSM controlling the "brain". It will invoke the currently
        // active state function: findLeaf(), goHome() or runAway().
        brain.update();

        // Apply the velocity vector to the position, making the ant move.
        moveBasedOnVelocity();
    }

    (...)
}

1
যদিও এই লিঙ্কটি প্রশ্নের উত্তর দিতে পারে, উত্তরের প্রয়োজনীয় অংশগুলি এখানে অন্তর্ভুক্ত করা এবং রেফারেন্সের জন্য লিঙ্কটি সরবরাহ করা ভাল। লিঙ্কযুক্ত পৃষ্ঠাগুলি পরিবর্তিত হলে লিঙ্ক-শুধুমাত্র উত্তরগুলি অবৈধ হতে পারে। - পর্যালোচনা থেকে
ড্রিলেল

@ ড্র্নেল আমি টিউটোরিয়াল থেকে বিটগুলি অনুলিপি এবং আটকানোতে পারতাম ... তবে লেখকের কাছ থেকে ক্রেডিট নেওয়া হবে না?
জেট ব্লু

1
@ জেটব্লিউ: উত্তরের লিঙ্কটি একটি রেফারেন্স হিসাবে রেখে দিন এবং আপনার নিজের কথায় প্রাসঙ্গিক বিটগুলি উত্তর পোস্টে অন্তর্ভুক্ত করুন যাতে কারও কপিরাইট ভঙ্গ না হয়। আমি জানি এটি কঠোর বলে মনে হচ্ছে তবে এই নিয়মের কারণে অনেক উত্তর অনেক বেশি হয়ে গেছে।
ফ্লিম 18

6

আজ আমি স্টেট ডিজাইন প্যাটার্নে গভীর। আমি এবং ThreadState, যা (+/-) C # সমান, থ্রেডিং থেকে ছবি বর্ণনা অনুযায়ী পরীক্ষিত C # থ্রেডিং

এখানে চিত্র বর্ণনা লিখুন

আপনি সহজেই নতুন রাজ্য যুক্ত করতে পারেন, এক রাজ্য থেকে অন্য রাজ্যে মুভগুলি কনফিগার করা খুব সহজ কারণ এটি রাষ্ট্রীয় প্রয়োগের ক্ষেত্রে অন্তর্ভুক্ত

বাস্তবায়ন এবং এটিতে ব্যবহার: স্টেট ডিজাইন প্যাটার্ন দ্বারা কার্যকর .NET থ্রেডস্টেট


1
লিঙ্কটি মারা গেছে। আরেকটা আছে?
রোলগুলি

5

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

আমি বিশ্বাস করি যে পদ্ধতিটি আমি সর্বদা জেনেছি তাকে "আইট্রেটিভ লুপ" এর মতো কিছু বলা হয়। এটিতে আপনার অবশ্যই একটি 'সময়' লুপ থাকে যা পর্যায়ক্রমে ইভেন্টগুলির (ইন্টারঅ্যাপ্টস) উপর ভিত্তি করে প্রস্থান করে, তারপরে আবার মূল লুপটিতে ফিরে আসে।

বাধাপ্রাপ্ত হ্যান্ডলারের মধ্যে আপনি একটি বর্তমান স্টেট পাস করে একটি নেক্সটস্টেট ফিরিয়ে আনতেন, যা মূল লুপে বর্তমানের স্টেট পরিবর্তনশীলটিকে ওভাররাইট করে। প্রোগ্রামটি বন্ধ না হওয়া অবধি আপনি এই বিজ্ঞাপনটি করছেন (বা মাইক্রোকন্ট্রোলার পুনরায় সেট করুন)।

আমি অন্যান্য উত্তরগুলি যা দেখতে পাচ্ছি তা এফএসএম, আমার মনে, বাস্তবায়নের উদ্দেশ্যে কীভাবে হয় তার সাথে তুলনা করে খুব জটিল দেখায়; এর সৌন্দর্য তার সরলতায় রয়েছে এবং এফএসএম অনেকগুলি, অনেকগুলি রাজ্য এবং রূপান্তরগুলির সাথে খুব জটিল হতে পারে তবে তারা জটিল প্রক্রিয়াটি সহজেই ভেঙে যায় এবং হজম হতে দেয়।

আমি বুঝতে পারি আমার প্রতিক্রিয়াতে আর একটি প্রশ্ন থাকা উচিত নয়, তবে আমি জিজ্ঞাসা করতে বাধ্য হই: এই অন্যান্য প্রস্তাবিত সমাধানগুলি কেন এত জটিল বলে মনে হচ্ছে?
তারা দৈত্য স্লেজ হাতুড়ি দিয়ে একটি ছোট পেরেক মারার মতো বলে মনে হচ্ছে।


1
সম্পূর্ণ একমত. একটি স্যুইচ স্টেটমেন্ট সহ একটি লুপ আপনি যতটা সহজ পেতে পারেন তত সহজ।
রোলগুলি

2
আপনার কাছে অনেকগুলি রাজ্য এবং শর্তাদি সহ একটি জটিল রাষ্ট্র মেশিন না থাকলে আপনি একাধিক নেস্টেড সুইচ দিয়ে শেষ করতে পারেন। আপনার লুপ বাস্তবায়নের উপর নির্ভর করে ব্যস্ত-অপেক্ষায় কোনও জরিমানাও থাকতে পারে।
সুন রিভার্স

3

কি স্টেটপ্যাটার্ন। এটি কি আপনার প্রয়োজনের সাথে খাপ খায়?

আমি মনে করি এর প্রসঙ্গটি সম্পর্কিত, তবে এটির জন্য অবশ্যই এটি শট shot

http://en.wikipedia.org/wiki/State_pattern

এটি আপনার রাজ্যগুলিকে কোথায় যাবে এবং সিদ্ধান্ত নিতে দেয় "অবজেক্ট" শ্রেণি নয়।

ব্রুনো


1
রাষ্ট্রীয় প্যাটার্নটি এমন শ্রেণীর সাথে সম্পর্কিত যা এটি যে রাষ্ট্র / মোডে রয়েছে তার ভিত্তিতে ভিন্নভাবে কাজ করতে পারে, এটি রাষ্ট্রগুলির মধ্যে রূপান্তরকে মোকাবেলা করে না।
এলি আলগ্রান্টি

3

আমার মতে একটি রাষ্ট্রীয় যন্ত্রটি কেবলমাত্র রাষ্ট্র পরিবর্তন করার জন্যই নয়, একটি নির্দিষ্ট রাজ্যের মধ্যে ট্রিগার / ইভেন্টগুলি পরিচালনা করার জন্যও (খুব গুরুত্বপূর্ণ)। আপনি যদি রাষ্ট্রের মেশিন ডিজাইনের প্যাটার্ন আরও ভালভাবে বুঝতে চান তবে হেড ফার্স্ট ডিজাইন প্যাটার্নস, পৃষ্ঠা 320 বইয়ের মধ্যে একটি ভাল বিবরণ পাওয়া যাবে

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


3

আমি স্রেফ এটি অবদান রেখেছি:

https://code.google.com/p/ysharp/source/browse/#svn%2Ftrunk%2FStateMachinesPoC

আইওসবারভার (সিগন্যালের) হিসাবে রাজ্য সহ প্রত্যক্ষ ও অপ্রত্যক্ষ আদেশ প্রেরণাকে বঞ্চিত করার উদাহরণগুলির মধ্যে একটি, এইভাবে সংকেত উত্সের প্রতিক্রিয়াশীল, IObservable (সিগন্যালের):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Test
{
    using Machines;

    public static class WatchingTvSampleAdvanced
    {
        // Enum type for the transition triggers (instead of System.String) :
        public enum TvOperation { Plug, SwitchOn, SwitchOff, Unplug, Dispose }

        // The state machine class type is also used as the type for its possible states constants :
        public class Television : NamedState<Television, TvOperation, DateTime>
        {
            // Declare all the possible states constants :
            public static readonly Television Unplugged = new Television("(Unplugged TV)");
            public static readonly Television Off = new Television("(TV Off)");
            public static readonly Television On = new Television("(TV On)");
            public static readonly Television Disposed = new Television("(Disposed TV)");

            // For convenience, enter the default start state when the parameterless constructor executes :
            public Television() : this(Television.Unplugged) { }

            // To create a state machine instance, with a given start state :
            private Television(Television value) : this(null, value) { }

            // To create a possible state constant :
            private Television(string moniker) : this(moniker, null) { }

            private Television(string moniker, Television value)
            {
                if (moniker == null)
                {
                    // Build the state graph programmatically
                    // (instead of declaratively via custom attributes) :
                    Handler<Television, TvOperation, DateTime> stateChangeHandler = StateChange;
                    Build
                    (
                        new[]
                        {
                            new { From = Television.Unplugged, When = TvOperation.Plug, Goto = Television.Off, With = stateChangeHandler },
                            new { From = Television.Unplugged, When = TvOperation.Dispose, Goto = Television.Disposed, With = stateChangeHandler },
                            new { From = Television.Off, When = TvOperation.SwitchOn, Goto = Television.On, With = stateChangeHandler },
                            new { From = Television.Off, When = TvOperation.Unplug, Goto = Television.Unplugged, With = stateChangeHandler },
                            new { From = Television.Off, When = TvOperation.Dispose, Goto = Television.Disposed, With = stateChangeHandler },
                            new { From = Television.On, When = TvOperation.SwitchOff, Goto = Television.Off, With = stateChangeHandler },
                            new { From = Television.On, When = TvOperation.Unplug, Goto = Television.Unplugged, With = stateChangeHandler },
                            new { From = Television.On, When = TvOperation.Dispose, Goto = Television.Disposed, With = stateChangeHandler }
                        },
                        false
                    );
                }
                else
                    // Name the state constant :
                    Moniker = moniker;
                Start(value ?? this);
            }

            // Because the states' value domain is a reference type, disallow the null value for any start state value : 
            protected override void OnStart(Television value)
            {
                if (value == null)
                    throw new ArgumentNullException("value", "cannot be null");
            }

            // When reaching a final state, unsubscribe from all the signal source(s), if any :
            protected override void OnComplete(bool stateComplete)
            {
                // Holds during all transitions into a final state
                // (i.e., stateComplete implies IsFinal) :
                System.Diagnostics.Debug.Assert(!stateComplete || IsFinal);

                if (stateComplete)
                    UnsubscribeFromAll();
            }

            // Executed before and after every state transition :
            private void StateChange(IState<Television> state, ExecutionStep step, Television value, TvOperation info, DateTime args)
            {
                // Holds during all possible transitions defined in the state graph
                // (i.e., (step equals ExecutionStep.LeaveState) implies (not state.IsFinal))
                System.Diagnostics.Debug.Assert((step != ExecutionStep.LeaveState) || !state.IsFinal);

                // Holds in instance (i.e., non-static) transition handlers like this one :
                System.Diagnostics.Debug.Assert(this == state);

                switch (step)
                {
                    case ExecutionStep.LeaveState:
                        var timeStamp = ((args != default(DateTime)) ? String.Format("\t\t(@ {0})", args) : String.Empty);
                        Console.WriteLine();
                        // 'value' is the state value that we are transitioning TO :
                        Console.WriteLine("\tLeave :\t{0} -- {1} -> {2}{3}", this, info, value, timeStamp);
                        break;
                    case ExecutionStep.EnterState:
                        // 'value' is the state value that we have transitioned FROM :
                        Console.WriteLine("\tEnter :\t{0} -- {1} -> {2}", value, info, this);
                        break;
                    default:
                        break;
                }
            }

            public override string ToString() { return (IsConstant ? Moniker : Value.ToString()); }
        }

        public static void Run()
        {
            Console.Clear();

            // Create a signal source instance (here, a.k.a. "remote control") that implements
            // IObservable<TvOperation> and IObservable<KeyValuePair<TvOperation, DateTime>> :
            var remote = new SignalSource<TvOperation, DateTime>();

            // Create a television state machine instance (automatically set in a default start state),
            // and make it subscribe to a compatible signal source, such as the remote control, precisely :
            var tv = new Television().Using(remote);
            bool done;

            // Always holds, assuming the call to Using(...) didn't throw an exception (in case of subscription failure) :
            System.Diagnostics.Debug.Assert(tv != null, "There's a bug somewhere: this message should never be displayed!");

            // As commonly done, we can trigger a transition directly on the state machine :
            tv.MoveNext(TvOperation.Plug, DateTime.Now);

            // Alternatively, we can also trigger transitions by emitting from the signal source / remote control
            // that the state machine subscribed to / is an observer of :
            remote.Emit(TvOperation.SwitchOn, DateTime.Now);
            remote.Emit(TvOperation.SwitchOff);
            remote.Emit(TvOperation.SwitchOn);
            remote.Emit(TvOperation.SwitchOff, DateTime.Now);

            done =
                (
                    tv.
                        MoveNext(TvOperation.Unplug).
                        MoveNext(TvOperation.Dispose) // MoveNext(...) returns null iff tv.IsFinal == true
                    == null
                );

            remote.Emit(TvOperation.Unplug); // Ignored by the state machine thanks to the OnComplete(...) override above

            Console.WriteLine();
            Console.WriteLine("Is the TV's state '{0}' a final state? {1}", tv.Value, done);

            Console.WriteLine();
            Console.WriteLine("Press any key...");
            Console.ReadKey();
        }
    }
}

দ্রষ্টব্য: এই উদাহরণটি বরং কৃত্রিম এবং বেশিরভাগ সংখ্যক অরথোগোনাল বৈশিষ্ট্য ডেমো করার জন্য। সিআরটিপি ব্যবহার করে রাষ্ট্রীয় মান ডোমেনটি একটি পূর্ণ বিকাশমান শ্রেণীর দ্বারা কার্যকরভাবে প্রয়োগ করার খুব কমই দরকার ছিল (দেখুন: http://en.wikedia.org/wiki/ictiously_recurring_template_pattern ) ।

একই রাষ্ট্রীয় মেশিনের জন্য এবং একই পরীক্ষার কেস সহকারে (রাষ্ট্রীয় মূল্য ডোমেন হিসাবে একটি সরল এনাম টাইপ ব্যবহার করে) সম্ভবত আরও সহজ এবং সম্ভবত আরও অনেক সাধারণ বাস্তবায়ন ব্যবহারের ক্ষেত্রে:

https://code.google.com/p/ysharp/source/browse/trunk/StateMachinesPoC/WatchingTVSample.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Test
{
    using Machines;

    public static class WatchingTvSample
    {
        public enum Status { Unplugged, Off, On, Disposed }

        public class DeviceTransitionAttribute : TransitionAttribute
        {
            public Status From { get; set; }
            public string When { get; set; }
            public Status Goto { get; set; }
            public object With { get; set; }
        }

        // State<Status> is a shortcut for / derived from State<Status, string>,
        // which in turn is a shortcut for / derived from State<Status, string, object> :
        public class Device : State<Status>
        {
            // Executed before and after every state transition :
            protected override void OnChange(ExecutionStep step, Status value, string info, object args)
            {
                if (step == ExecutionStep.EnterState)
                {
                    // 'value' is the state value that we have transitioned FROM :
                    Console.WriteLine("\t{0} -- {1} -> {2}", value, info, this);
                }
            }

            public override string ToString() { return Value.ToString(); }
        }

        // Since 'Device' has no state graph of its own, define one for derived 'Television' :
        [DeviceTransition(From = Status.Unplugged, When = "Plug", Goto = Status.Off)]
        [DeviceTransition(From = Status.Unplugged, When = "Dispose", Goto = Status.Disposed)]
        [DeviceTransition(From = Status.Off, When = "Switch On", Goto = Status.On)]
        [DeviceTransition(From = Status.Off, When = "Unplug", Goto = Status.Unplugged)]
        [DeviceTransition(From = Status.Off, When = "Dispose", Goto = Status.Disposed)]
        [DeviceTransition(From = Status.On, When = "Switch Off", Goto = Status.Off)]
        [DeviceTransition(From = Status.On, When = "Unplug", Goto = Status.Unplugged)]
        [DeviceTransition(From = Status.On, When = "Dispose", Goto = Status.Disposed)]
        public class Television : Device { }

        public static void Run()
        {
            Console.Clear();

            // Create a television state machine instance, and return it, set in some start state :
            var tv = new Television().Start(Status.Unplugged);
            bool done;

            // Holds iff the chosen start state isn't a final state :
            System.Diagnostics.Debug.Assert(tv != null, "The chosen start state is a final state!");

            // Trigger some state transitions with no arguments
            // ('args' is ignored by this state machine's OnChange(...), anyway) :
            done =
                (
                    tv.
                        MoveNext("Plug").
                        MoveNext("Switch On").
                        MoveNext("Switch Off").
                        MoveNext("Switch On").
                        MoveNext("Switch Off").
                        MoveNext("Unplug").
                        MoveNext("Dispose") // MoveNext(...) returns null iff tv.IsFinal == true
                    == null
                );

            Console.WriteLine();
            Console.WriteLine("Is the TV's state '{0}' a final state? {1}", tv.Value, done);

            Console.WriteLine();
            Console.WriteLine("Press any key...");
            Console.ReadKey();
        }
    }
}

'আছে HTH


প্রতিটি রাজ্যের উদাহরণগুলির সাথে রাষ্ট্রের গ্রাফের নিজস্ব অনুলিপি থাকা কি একটু আশ্চর্যের নয়?
গ্রো

@ গ্রু: না, তারা দেয় না। শুধুমাত্র প্রাইভেট কনস্ট্রাক্টর ব্যবহার করে মনিকারের নাল স্ট্রিং ব্যবহার করে নির্মিত টেলিভিশনের উদাহরণগুলি (সুতরাং, সুরক্ষিত 'বিল্ড' পদ্ধতিটি কল করে) স্টেট মেশিন হিসাবে স্টেট গ্রাফ থাকবে। অন্যদের (ক অবজ্ঞাসূচক সঙ্গে টেলিভিশনের নামে দৃষ্টান্ত না হতে হবে নিছক "ফিক্স নির্দেশ" রাজ্যের (তাই কথা বলতে) যে প্রচলিত এবং অ্যাড-হক উদ্দেশ্যে নাল), রাষ্ট্র ধ্রুবক হিসেবে কাজ (যে রাষ্ট্র গ্রাফ (গুলি) প্রকৃত রাষ্ট্রের মেশিনগুলি তাদের শীর্ষকে হিসাবে উল্লেখ করবে)। '
এইচটিএইচ

ঠিক আছে, আমি এটি পেয়েছি যাইহোক, আইএমএইচও, আপনি যদি এমন কিছু কোড অন্তর্ভুক্ত করেন যা আসলে এই রূপান্তরগুলি পরিচালনা করে তবে ভাল হত। এইভাবে, এটি কেবল আপনার লাইব্রেরির জন্য (আইএমএইচও) না-তাই-স্পষ্ট ইন্টারফেস ব্যবহারের উদাহরণ হিসাবে কাজ করে। উদাহরণস্বরূপ, কিভাবে StateChangeসমাধান করা হয় ? প্রতিবিম্ব মাধ্যমে? সত্যিই প্রয়োজন?
গ্রো

1
@ গ্রু: ভালো মন্তব্য। হ্যান্ডলারের সেই প্রথম উদাহরণটিতে প্রতিফলিত করার প্রয়োজন নেই কারণ এটি প্রোগ্রামিয়ালি সেখানে সম্পন্ন হয়েছে এবং এটি স্ট্যাটিকালি বাউন্ড / টাইপ চেক করা যেতে পারে (কাস্টম অ্যাট্রিবিউটগুলির মাধ্যমে বিপরীতে)। সুতরাং এটিও প্রত্যাশা অনুযায়ী কাজ: private Television(string moniker, Television value) { Handler<Television, TvOperation, DateTime> myHandler = StateChange; // (code omitted) new { From = Television.Unplugged, When = TvOperation.Plug, Goto = Television.Off, With = myHandler } }
YSharp

1
আপনার প্রচেষ্টার জন্য ধন্যবাদ!
গ্রো

3

আমি এই জেনেরিক স্টেট মেশিন তৈরি করেছি জুলিয়েটের কোড থেকে । এটি আমার জন্য দুর্দান্ত কাজ করছে।

এগুলি হ'ল সুবিধা:

  • আপনি দুটি এনাম TStateএবং কোড সহ নতুন স্টেট মেশিন তৈরি করতে পারেনTCommand ,
  • TransitionResult<TState>এর ফলাফলের উপর আরও নিয়ন্ত্রণ রাখতে স্ট্রাক্ট যুক্ত করা হয়েছে[Try]GetNext()পদ্ধতির
  • নেস্টেড বর্গ প্রকাশক StateTransition শুধুমাত্র মাধ্যমে AddTransition(TState, TCommand, TState)এটা দিয়ে কাজ করতে সহজে উপার্জন

কোড:

public class StateMachine<TState, TCommand>
    where TState : struct, IConvertible, IComparable
    where TCommand : struct, IConvertible, IComparable
{
    protected class StateTransition<TS, TC>
        where TS : struct, IConvertible, IComparable
        where TC : struct, IConvertible, IComparable
    {
        readonly TS CurrentState;
        readonly TC Command;

        public StateTransition(TS currentState, TC command)
        {
            if (!typeof(TS).IsEnum || !typeof(TC).IsEnum)
            {
                throw new ArgumentException("TS,TC must be an enumerated type");
            }

            CurrentState = currentState;
            Command = command;
        }

        public override int GetHashCode()
        {
            return 17 + 31 * CurrentState.GetHashCode() + 31 * Command.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            StateTransition<TS, TC> other = obj as StateTransition<TS, TC>;
            return other != null
                && this.CurrentState.CompareTo(other.CurrentState) == 0
                && this.Command.CompareTo(other.Command) == 0;
        }
    }

    private Dictionary<StateTransition<TState, TCommand>, TState> transitions;
    public TState CurrentState { get; private set; }

    protected StateMachine(TState initialState)
    {
        if (!typeof(TState).IsEnum || !typeof(TCommand).IsEnum)
        {
            throw new ArgumentException("TState,TCommand must be an enumerated type");
        }

        CurrentState = initialState;
        transitions = new Dictionary<StateTransition<TState, TCommand>, TState>();
    }

    /// <summary>
    /// Defines a new transition inside this state machine
    /// </summary>
    /// <param name="start">source state</param>
    /// <param name="command">transition condition</param>
    /// <param name="end">destination state</param>
    protected void AddTransition(TState start, TCommand command, TState end)
    {
        transitions.Add(new StateTransition<TState, TCommand>(start, command), end);
    }

    public TransitionResult<TState> TryGetNext(TCommand command)
    {
        StateTransition<TState, TCommand> transition = new StateTransition<TState, TCommand>(CurrentState, command);
        TState nextState;
        if (transitions.TryGetValue(transition, out nextState))
            return new TransitionResult<TState>(nextState, true);
        else
            return new TransitionResult<TState>(CurrentState, false);
    }

    public TransitionResult<TState> MoveNext(TCommand command)
    {
        var result = TryGetNext(command);
        if(result.IsValid)
        {
            //changes state
            CurrentState = result.NewState;
        }
        return result;
    }
}

এটি ট্রাইগেটেক্সট পদ্ধতিটির রিটার্ন টাইপ:

public struct TransitionResult<TState>
{
    public TransitionResult(TState newState, bool isValid)
    {
        NewState = newState;
        IsValid = isValid;
    }
    public TState NewState;
    public bool IsValid;
}

ব্যবহারবিধি:

এইভাবে আপনি একটি তৈরি করতে পারেন OnlineDiscountStateMachineজেনেরিক ক্লাস থেকে :

একটি enum নির্ধারণ OnlineDiscountStateতার রাজ্য এবং একটি enum জন্যOnlineDiscountCommand জন্য এবং তার আদেশের জন্য ।

একটি ক্লাস সংজ্ঞায়িত করুন OnlineDiscountStateMachineএই দুটি এনাম ব্যবহার করে জেনেরিক ক্লাস থেকে প্রাপ্ত

কনস্ট্রাক্টরটি আবিষ্কার করুন base(OnlineDiscountState.InitialState)যাতে প্রাথমিক অবস্থায় সেট করা থাকেOnlineDiscountState.InitialState

AddTransitionযতবার প্রয়োজন ততবার ব্যবহার করুন

public class OnlineDiscountStateMachine : StateMachine<OnlineDiscountState, OnlineDiscountCommand>
{
    public OnlineDiscountStateMachine() : base(OnlineDiscountState.Disconnected)
    {
        AddTransition(OnlineDiscountState.Disconnected, OnlineDiscountCommand.Connect, OnlineDiscountState.Connected);
        AddTransition(OnlineDiscountState.Disconnected, OnlineDiscountCommand.Connect, OnlineDiscountState.Error_AuthenticationError);
        AddTransition(OnlineDiscountState.Connected, OnlineDiscountCommand.Submit, OnlineDiscountState.WaitingForResponse);
        AddTransition(OnlineDiscountState.WaitingForResponse, OnlineDiscountCommand.DataReceived, OnlineDiscountState.Disconnected);
    }
}

উদ্ভূত রাষ্ট্র মেশিন ব্যবহার করুন

    odsm = new OnlineDiscountStateMachine();
    public void Connect()
    {
        var result = odsm.TryGetNext(OnlineDiscountCommand.Connect);

        //is result valid?
        if (!result.IsValid)
            //if this happens you need to add transitions to the state machine
            //in this case result.NewState is the same as before
            Console.WriteLine("cannot navigate from this state using OnlineDiscountCommand.Connect");

        //the transition was successfull
        //show messages for new states
        else if(result.NewState == OnlineDiscountState.Error_AuthenticationError)
            Console.WriteLine("invalid user/pass");
        else if(result.NewState == OnlineDiscountState.Connected)
            Console.WriteLine("Connected");
        else
            Console.WriteLine("not implemented transition result for " + result.NewState);
    }

1

আমি মনে করি জুলিয়েট দ্বারা প্রস্তাবিত স্টেট মেশিনের একটি ভুল রয়েছে: গেটহ্যাশকোড পদ্ধতিটি দুটি ভিন্ন ট্রানজিশনের জন্য একই হ্যাশ কোডটি ফিরিয়ে দিতে পারে, উদাহরণস্বরূপ:

রাজ্য = সক্রিয় (1), আদেশ = বিরতি (2) => হ্যাশকোড = 17 + 31 + 62 = 110

রাজ্য = বিরতি (2), কমান্ড = শেষ (1) => হ্যাশকোড = 17 + 62 + 31 = 110

এই ত্রুটিটি এড়াতে, পদ্ধতিটি এমন হওয়া উচিত:

public override int GetHashCode()
   {
            return 17 + 23 * CurrentState.GetHashCode() + 31 * Command.GetHashCode();
   }

অ্যালেক্স


1
কোনও সম্ভাব্য সংমিশ্রণের জন্য হ্যাশ কোডের কোনও অনন্য নম্বর ফেরত দেওয়ার প্রয়োজন হয় না, লক্ষ্য ব্যাপ্তি জুড়ে ভাল বিতরণের সাথে একটি স্বতন্ত্র মান (এই ক্ষেত্রে সীমাটি সমস্ত সম্ভাব্য intমান)। এজন্য HashCodeসর্বদা পাশাপাশি প্রয়োগ করা হয় Equals। যদি হ্যাশ কোডগুলি একই হয়, তবে পদ্ধতিটি ব্যবহার করে অবজেক্টগুলি সঠিক eqaulity জন্য পরীক্ষা করা হয় Equals
দিমিত্রি অ্যাভটনোমভ

0

FiniteStateMachine একটি সাধারণ স্টেট মেশিন, সি # লিঙ্কে লিখিত

আমার লাইব্রেরিটি ফিনাইটস্টেটম্যাচাইন ব্যবহারের জন্য সুবিধাগুলি:

  1. বাইরের বিশ্বের কাছে একটি একক ইন্টারফেস উপস্থাপন করতে একটি "প্রসঙ্গ" শ্রেণি সংজ্ঞায়িত করুন।
  2. একটি রাষ্ট্র বিমূর্ত বেস বর্গ নির্ধারণ করুন।
  3. রাজ্য মেশিনের বিভিন্ন "রাজ্য" রাজ্য বেস শ্রেণীর উত্পন্ন শ্রেণি হিসাবে উপস্থাপন করুন।
  4. উপযুক্ত রাজ্য উদ্ভূত শ্রেণিতে রাষ্ট্র-নির্দিষ্ট আচরণের সংজ্ঞা দিন।
  5. "প্রসঙ্গ" শ্রেণিতে বর্তমান "রাষ্ট্র" এর দিকে একটি পয়েন্টার বজায় রাখুন।
  6. রাজ্য মেশিনের স্থিতি পরিবর্তন করতে, বর্তমান "রাষ্ট্র" পয়েন্টারটি পরিবর্তন করুন।

ডিএলএল ডাউনলোড করুন

লিনকিউপ্যাডের উদাহরণ:

void Main()
{
            var machine = new SFM.Machine(new StatePaused());
            var output = machine.Command("Input_Start", Command.Start);
            Console.WriteLine(Command.Start.ToString() + "->  State: " + machine.Current);
            Console.WriteLine(output);

            output = machine.Command("Input_Pause", Command.Pause);
            Console.WriteLine(Command.Pause.ToString() + "->  State: " + machine.Current);
            Console.WriteLine(output);
            Console.WriteLine("-------------------------------------------------");
}
    public enum Command
    {
        Start,
        Pause,
    }

    public class StateActive : SFM.State
    {

        public override void Handle(SFM.IContext context)

        {
            //Gestione parametri
            var input = (String)context.Input;
            context.Output = input;

            //Gestione Navigazione
            if ((Command)context.Command == Command.Pause) context.Next = new StatePaused();
            if ((Command)context.Command == Command.Start) context.Next = this;

        }
    }


public class StatePaused : SFM.State
{

     public override void Handle(SFM.IContext context)

     {

         //Gestione parametri
         var input = (String)context.Input;
         context.Output = input;

         //Gestione Navigazione
         if ((Command)context.Command == Command.Start) context.Next = new  StateActive();
         if ((Command)context.Command == Command.Pause) context.Next = this;


     }

 }

1
এটির জিএনইউ জিপিএল লাইসেন্স রয়েছে।
ডের_মিস্টার

0

আমি state.cs সুপারিশ করব । আমি ব্যক্তিগতভাবে state.js (জাভাস্ক্রিপ্ট সংস্করণ) ব্যবহার করেছি এবং এটিতে খুব খুশি। সেই সি # সংস্করণ একইভাবে কাজ করে।

আপনি তাত্ক্ষণিকভাবে বলেছেন:

        // create the state machine
        var player = new StateMachine<State>( "player" );

        // create some states
        var initial = player.CreatePseudoState( "initial", PseudoStateKind.Initial );
        var operational = player.CreateCompositeState( "operational" );
        ...

আপনি কিছু স্থানান্তরণ ইনস্ট্যান্ট করুন:

        var t0 = player.CreateTransition( initial, operational );
        player.CreateTransition( history, stopped );
        player.CreateTransition<String>( stopped, running, ( state, command ) => command.Equals( "play" ) );
        player.CreateTransition<String>( active, stopped, ( state, command ) => command.Equals( "stop" ) );

আপনি রাষ্ট্র এবং স্থানান্তরের বিষয়ে ক্রিয়া সংজ্ঞায়িত করেন:

    t0.Effect += DisengageHead;
    t0.Effect += StopMotor;

এবং thats প্রায় কাছাকাছি এটি. আরও তথ্যের জন্য ওয়েবসাইটটি দেখুন।


0

নিউগেটে 2 টি জনপ্রিয় রাষ্ট্রীয় মেশিন প্যাকেজ রয়েছে।

অ্যাপ্লিকেশনটি. স্টেটম্যাচাইন ( ১৩..6 কে ডাউনলোড + ৩.৮২ কে উত্তরাধিকার সংস্করণ (বিবিভি.কমন.স্টেটম্যাচাইন))

স্টেটম্যাচাইনটুলকিট ( 1.56 কে ডাউনলোড)

অ্যাপ্লিকেশন লিবের ভাল ডকুমেন্টেশন রয়েছে , তবে এটি। নেট 4 সমর্থন করে না, তাই আমি আমার প্রকল্পের জন্য স্টেটম্যাচিনটুলকিট বেছে নিয়েছি।


0

এই রেপোর অন্যান্য বিকল্প https://github.com/lingkodsoft/StateBliss ফ্লুয়েন্ট সিনট্যাক্স ব্যবহার করেছে, ট্রিগারগুলিকে সমর্থন করে।

    public class BasicTests
    {
        [Fact]
        public void Tests()
        {
            // Arrange
            StateMachineManager.Register(new [] { typeof(BasicTests).Assembly }); //Register at bootstrap of your application, i.e. Startup
            var currentState = AuthenticationState.Unauthenticated;
            var nextState = AuthenticationState.Authenticated;
            var data = new Dictionary<string, object>();

            // Act
            var changeInfo = StateMachineManager.Trigger(currentState, nextState, data);

            // Assert
            Assert.True(changeInfo.StateChangedSucceeded);
            Assert.Equal("ChangingHandler1", changeInfo.Data["key1"]);
            Assert.Equal("ChangingHandler2", changeInfo.Data["key2"]);
        }

        //this class gets regitered automatically by calling StateMachineManager.Register
        public class AuthenticationStateDefinition : StateDefinition<AuthenticationState>
        {
            public override void Define(IStateFromBuilder<AuthenticationState> builder)
            {
                builder.From(AuthenticationState.Unauthenticated).To(AuthenticationState.Authenticated)
                    .Changing(this, a => a.ChangingHandler1)
                    .Changed(this, a => a.ChangedHandler1);

                builder.OnEntering(AuthenticationState.Authenticated, this, a => a.OnEnteringHandler1);
                builder.OnEntered(AuthenticationState.Authenticated, this, a => a.OnEnteredHandler1);

                builder.OnExiting(AuthenticationState.Unauthenticated, this, a => a.OnExitingHandler1);
                builder.OnExited(AuthenticationState.Authenticated, this, a => a.OnExitedHandler1);

                builder.OnEditing(AuthenticationState.Authenticated, this, a => a.OnEditingHandler1);
                builder.OnEdited(AuthenticationState.Authenticated, this, a => a.OnEditedHandler1);

                builder.ThrowExceptionWhenDiscontinued = true;
            }

            private void ChangingHandler1(StateChangeGuardInfo<AuthenticationState> changeinfo)
            {
                var data = changeinfo.DataAs<Dictionary<string, object>>();
                data["key1"] = "ChangingHandler1";
            }

            private void OnEnteringHandler1(StateChangeGuardInfo<AuthenticationState> changeinfo)
            {
                // changeinfo.Continue = false; //this will prevent changing the state
            }

            private void OnEditedHandler1(StateChangeInfo<AuthenticationState> changeinfo)
            {                
            }

            private void OnExitedHandler1(StateChangeInfo<AuthenticationState> changeinfo)
            {                
            }

            private void OnEnteredHandler1(StateChangeInfo<AuthenticationState> changeinfo)
            {                
            }

            private void OnEditingHandler1(StateChangeGuardInfo<AuthenticationState> changeinfo)
            {
            }

            private void OnExitingHandler1(StateChangeGuardInfo<AuthenticationState> changeinfo)
            {
            }

            private void ChangedHandler1(StateChangeInfo<AuthenticationState> changeinfo)
            {
            }
        }

        public class AnotherAuthenticationStateDefinition : StateDefinition<AuthenticationState>
        {
            public override void Define(IStateFromBuilder<AuthenticationState> builder)
            {
                builder.From(AuthenticationState.Unauthenticated).To(AuthenticationState.Authenticated)
                    .Changing(this, a => a.ChangingHandler2);

            }

            private void ChangingHandler2(StateChangeGuardInfo<AuthenticationState> changeinfo)
            {
                var data = changeinfo.DataAs<Dictionary<string, object>>();
                data["key2"] = "ChangingHandler2";
            }
        }
    }

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