Как реализовать интерактивное дерево решений в C # - PullRequest
3 голосов
/ 05 октября 2019

Мне нужно разрешить пользователям выбирать свой собственный путь, выбирая между двумя простыми вариантами, отображаемыми на их экране, чтобы перейти к следующему набору вариантов, пока они не достигнут одного из окончаний, то есть должно быть достигнуто нечто подобное:

enter image description here

Я пробовал следующий код, но каждый раз оценивается только левая сторона. Мне интересно, как я могу достичь таких результатов, как на изображении выше (охватывающий все ветви)? Например, если пользователь выбирает «Нет», приложение не должно задавать никаких дополнительных вопросов от пользователя и просто показывает сообщение «Возможно, вы хотите пиццу». Я сделал это с помощью алгоритма дерева решений, и мне нужно исправить его, чтобы он покрывал как левую, так и правую стороны, как на изображении выше.

namespace ConsoleApp3
{
    class Program
    {
        static void Main(string[] args)
        {
            var decisionTree = MainDecisionTree();

            var client = new Client();

            Console.WriteLine("Do you want a book? true/false");
            client.Answer[0] = bool.Parse(Console.ReadLine());
            Console.WriteLine("Do you like it? true/false");
            client.Answer[1] = bool.Parse(Console.ReadLine());
            Console.WriteLine("Are you sure? true/false");
            client.Answer[2] = bool.Parse(Console.ReadLine());

            decisionTree.Evaluate(client);

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

        private static DecisionQuery MainDecisionTree()
        {
            //Decision 2
            var wantBranch = new DecisionQuery
            {
                Title = "Do you want a book?",
                Test = (client) => client.Answer[0],
                Positive = new DecisionResult { Result = true },
                Negative = new DecisionResult { Result = false }
            };

            //Decision 1
            var deserveBranch = new DecisionQuery
            {
                Title = "Do you like it?",
                Test = (client) => client.Answer[1],
                Positive = wantBranch,
                Negative = new DecisionResult { Result = false }
            };


            //Decision 0
            var sureBranch = new DecisionQuery
            {
                Title = "Are you sure?",
                Test = (client) => client.Answer[2],
                Positive = deserveBranch,
                Negative = new DecisionResult { Result = false }
            };

            return sureBranch;
        }
    }

    public class DecisionResult : Decision
    {
        public bool Result { get; set; }

        public override void Evaluate(Client client)
        {
            Console.WriteLine("\r\nThe result: {0}", Result ? "Buy it" : "You need to wait");
        }
    }

    public class DecisionQuery : Decision
    {
        public string Title { get; set; }
        public Decision Positive { get; set; }
        public Decision Negative { get; set; }
        public Func<Client, bool> Test { get; set; }

        public override void Evaluate(Client client)
        {
            bool result = this.Test(client);
            string resultAsString = result ? "yes" : "no";

            Console.WriteLine($"\t- {this.Title}? {resultAsString}");

            if (result) this.Positive.Evaluate(client);
            else this.Negative.Evaluate(client);
        }
    }

    public abstract class Decision
    {
        public abstract void Evaluate(Client client);
    }

    public class Client
    {
        public bool[] Answer { get; set; } = new bool[3];
    }
}

Ответы [ 2 ]

3 голосов
/ 05 октября 2019

Если я понимаю вашу проблему, вот ваш код исправлен.

Я переименовал некоторые вещи.

Я вызвал MakeDecisionTree метод, который инициализирует экспертную систему с деревом условий ивозвращает корневое условие.

Каждый condition содержит от sentence до evaluate, и это может быть query или result.

Для result,evaluate отображает sentence.

. Для query метод evaluate просит пользователя ответить на вопрос yes или no. И используя этот ответ, он вызывает соответствующий evaluate следующего ребенка condition.

Извините за мой английский здесь, это не мой родной язык, и я не участвую в AI.

static private void DecisionTreeTest()
{
  Console.WriteLine("Please, answer a few questions with yes or no.");
  Console.WriteLine();
  MakeDecisionTree().Evaluate();
}
static private bool GetUserAnswer(string question)
{
  Console.WriteLine(question);
  string userInput;
  while ( true )
  {
    userInput = Console.ReadLine().ToLower();
    if ( userInput == "yes" )
      return true;
    else
    if ( userInput == "no" )
      return false;
    else
      Console.WriteLine("Your answer is not supported, retry please." +
                        Environment.NewLine + Environment.NewLine +
                        question);
  }
}
static private DecisionTreeQuery MakeDecisionTree()
{
  var queryAreYouSure
    = new DecisionTreeQuery("Are you sure?",
                            new DecisionTreeResult("Buy it."),
                            new DecisionTreeResult("You need to wait."),
                            GetUserAnswer);
  var queryIsItAGoodBook
    = new DecisionTreeQuery("Is it a good book?",
                            new DecisionTreeResult("What are you waiting for? Just buy it."),
                            new DecisionTreeResult("Find another one."),
                            GetUserAnswer);
  var queryDoYouLikeIt
    = new DecisionTreeQuery("Do you like it?",
                            queryAreYouSure,
                            queryIsItAGoodBook,
                            GetUserAnswer);
  var queryDoYouWantABook
    = new DecisionTreeQuery("Do you want a book?",
                            queryDoYouLikeIt,
                            new DecisionTreeResult("Maybe you want a pizza."),
                            GetUserAnswer);
  return queryDoYouWantABook;
}
abstract public class DecisionTreeCondition
{
  protected string Sentence { get; private set; }
  abstract public void Evaluate();
  public DecisionTreeCondition(string sentence)
  {
    Sentence = sentence;
  }
}
public class DecisionTreeQuery : DecisionTreeCondition
{
  private DecisionTreeCondition Positive;
  private DecisionTreeCondition Negative;
  private Func<string, bool> UserAnswerProvider;
  public override void Evaluate()
  {
    if ( UserAnswerProvider(Sentence) )
      Positive.Evaluate();
    else
      Negative.Evaluate();
  }
  public DecisionTreeQuery(string sentence,
                           DecisionTreeCondition positive,
                           DecisionTreeCondition negative,
                           Func<string, bool> userAnswerProvider)
    : base(sentence)
  {
    Positive = positive;
    Negative = negative;
    UserAnswerProvider = userAnswerProvider;
  }
}
public class DecisionTreeResult : DecisionTreeCondition
{
  public override void Evaluate()
  {
    Console.WriteLine(Sentence);
  }
  public DecisionTreeResult(string sentence)
    : base(sentence)
  {
  }
}
0 голосов
/ 05 октября 2019

Ваш дизайн дерева решений очень хорош, просто как вы спрашиваете пользователя, нет. Вы заранее задаете все вопросы и позже переходите к дереву решений. Конечно, дерево не может задавать вопросы без ответа. Это не проблема дерева решений.

вы должны поместить эти пары строк:

    Console.WriteLine("Are you sure? true/false");
    client.Answer[2] = bool.Parse(Console.ReadLine());

в метод Evaluate, или вы сделаете обратный вызов или любой другой архитектурный проект, чтобы отделить логику отпользовательский интерфейс. Дерево решений должно задавать или, по крайней мере, знать вопрос. Ответ не нужно хранить, вы можете оценить его сразу после ввода.

Может быть, вы передадите что-нибудь, чтобы попросить пользователя оценить метод

public interface IAsk
{
    bool Question(string message);
}

И для вашего образца вы создадите экземплярчто с помощью

public class ConsoleAsk : IAsk
{
    bool Question(string message)
    {
         Console.WriteLine(message);
         return  bool.Parse(Console.ReadLine());
    }
}

и вы изменяете свою Оценку следующим образом:

    public override void Evaluate(IAsk ask)
    {
        bool result = ask.Question("do you feel good or bad");
        string resultAsString = result ? "yes" : "no";

        Console.WriteLine($"\t- {this.Title}? {resultAsString}");

        if (result) this.Positive.Evaluate(ask);
        else this.Negative.Evaluate(ask);
    }

, конечно, вы заменяете вопрос свойством заголовка.

...