任何避免在这里的子类中进行Object/强制转换的方法

本文关键字:Object 转换 方法 在这里 子类 任何避 | 更新日期: 2024-09-23 23:38:14

继《人工智能-现代方法》一书之后,我正在尝试实现搜索算法(DFS、A*等),这些算法可以处理不同的问题(从A到B的路径、滑块谜题等),而不仅仅是一个特定的问题。

public abstract class Problem
{
    protected Problem(object initialState)
    {
        InitialState = initialState;
    }
    public object InitialState { get; }
    /// <summary>
    /// Checks if the state is the goal state
    /// </summary>
    public abstract bool IsGoalState(object state);
    /// <summary>
    /// Returns the actions available from the state
    /// </summary>
    public abstract ISet<Action> Actions(object state);
    /// <summary>
    /// Returns the state that results after performing the action on the state
    /// </summary>
    public abstract object ResultState(object state, Action action);
    /// <summary>
    ///  Returns the cost of action to reach from state to reachedState
    /// </summary>
    /// <param name="state">The state from which the action will be performed</param>
    /// <param name="action">One of the actions available in the state</param>
    /// <param name="reachedState">The state that results after performing the action</param>
    public virtual int StepCost(object state, Action action, object reachedState)
    {
        return 1;
    }
}

_

public class Node
{
    public Node(object state)
    {
        State = state;
        PathCost = 0;
    }
    /// <summary>
    /// </summary>
    /// <param name="action">The action that was applied to the parent to generate the node</param>
    /// <param name="stepCost">The cost from the parent node to this node</param>
    public Node(object state, Node parent, Action action, int stepCost)
        : this(state)
    {
        Parent = parent;
        Action = action;
        if (Parent != null)
            PathCost = Parent.PathCost + stepCost;
    }
    public object State { get; }
    public Node Parent { get; }
    /// <summary>
    /// The action that was applied to the parent to generate the node
    /// </summary>
    public Action Action { get; }
    /// <summary>
    /// The cost of the path from the initial statee to the node
    /// </summary>
    public int PathCost { get; }
    public bool IsRootNode => Parent == null;
    public IEnumerable<Node> PathFromRoot()
    {
        var path = new Stack<Node>();
        var node = this;
        while (!node.IsRootNode)
        {
            path.Push(node);
            node = node.Parent;
        }
        path.Push(node); // root
        return path;
    }
}

_

public abstract class Action
{
    /// <summary>
    /// true if it is a "No Operation" action
    /// </summary>
    public virtual bool IsNoOp()
    {
        return false;
    }
    public string Name => GetType().Name;
    public override string ToString()
    {
        return Name;
    }
}
public class NoOp : Action
{
    public override bool IsNoOp()
    {
        return true;
    }
}
public abstract class EightPuzzleAction : Action
{
}
/// <summary>
/// Move the blank tile to the left
/// </summary>
public class MoveLeft : EightPuzzleAction
{
}
// the same MoveRight, MoveTop, MoveBottom

为了做到这一点,我可以实现这个基本Problem类中的方法,并将该具体实例传递给搜索算法(接受Problem)。

class EightPuzzleProblem : Problem
{
    private readonly int[,] _goalState =
    {
        {0, 1, 2},
        {3, 4, 5},
        {6, 7, 8},
    };
    public EightPuzzleProblem(int[,] initialState) : base(initialState)
    { }
    public override bool IsGoalState(object state)
    {
        var puzzleState = (int[,]) state;
        ......... <check if puzzleState is the same as _goalState>
    }
    ............
}
class DepthFirstSearch
{
    public IEnumerable<Action> Search(Problem p)
    {
        return DfsRecursive(p, new Node(p.InitialState));
    }
    public IEnumerable<Action> DfsRecursive(Problem p, Node node)
    {
        if (p.IsGoalState(node.State))
            return node.PathFromRoot();
        .......<simple DFS implementation>
    }
}
// usage example
var dfs = new DepthFirstSearch();
var result = dfs.Search(new EightPuzzleProblem(initialState));
if (!result.Any)
   // fail
if (result.Count == 1 && result[0].IsNoOp)
   // already at goal

foreach (var act in result)
{
    if (act is MoveLeft) .....
}

但我看到了类设计的一个不便之处:在具体的类实现代码中,我需要从object进行大量的强制转换。有什么办法可以避免这种情况吗?如果我将Problem类设为泛型并从Problem<Something>继承ConcreteProblem,那么我将无法通过Problem使用它。。。

任何避免在这里的子类中进行Object/强制转换的方法

为什么不也注入你的GoalState,并使用下面的接口使GameState也可插入。通过这种方式,您可以在具体类和Problem类中的两个States上实现您想要执行的任何操作,只需要调用这些函数。你也可以有不同类型的状态。

public abstract class Problem<T> where T:IGameState
{
    protected Problem(T initialState)
    {
        InitialState = initialState;
    }
    public T InitialState { get; set; }
    /// <summary>
    /// Checks if the state is the goal state
    /// </summary>
    public abstract bool IsGoalState(T state);

}
public class EightPuzzleProblem<T> : Problem<T> where T : IGameState
{
    private readonly T _goalState;
    public EightPuzzleProblem(T initialState, T goalState)
        : base(initialState)
    {
        _goalState = goalState;
    }
    public override bool IsGoalState(T state)
    {
        return _goalState.IsEqual(state);
    }
}
public interface IGameState
{
    bool IsEqual(IGameState state);
}
public class EightPuzzleGameState : IGameState
{
    private readonly int[,] _goalState =
    {
        {0, 1, 2},
        {3, 4, 5},
        {6, 7, 8},
    };
    public bool IsEqual(IGameState state)
    {
        //Compare state with _goalState
    }
}

您实际上可以将Problem设置为泛型,并将泛型参数设置为实际状态。

public abstract class Problem<T> where T : IState{ 
    public abstract bool IsGoalState(T state);
}

在派生类中,您现在可以简单地从Problem派生,传递IState的实际类型作为泛型参数:

class MyDerived : Problem<MyState> {
    public override bool IsGoalState(MyState state) { ... }
}