2011-07-27 14 views
5

Implementé un algoritmo de derivación-yarda mejorado para analizar una expresión aritmética. Un aspecto del algoritmo es que mantiene un Queue y un Stack.Manera segura para almacenar dos tipos de objetos en una colección

En mi implementación, el Queue contiene Expressions y Operators. El Stack contiene Operators y Parenthesis.

Expressions, Parenthesis, y Operators no tienen nada en común que garantice que dos de ellos tengan una interfaz compartida.

Enfoques:

  • Mi implementación actual consiste en Expression y la implementación de un OperatorINotParanthesis. Operator y Paranthesis implementan un INotExpression. Luego declaro Queue <INotParanthesis> y Stack <INotExpression>.

    No me gusta esta implementación: estas interfaces parecen un truco para el propósito de un código de algoritmo más limpio. También creo que las interfaces deben describir qué es un objeto, en oposición a lo que no es.

  • Por otro lado, tampoco quiero usar colecciones de <Object>, ya que puede ser difícil estar seguro de la exactitud de dicho código.

  • El único que he encontrado, hasta ahora, es la implementación de mis propios contenedores NonParanthesisQueue y NonExpressionStack. Esto tiene la ventaja de un control de tipo más consistente en los objetos que se extraen de esos contenedores, y la desventaja de tener muchos más códigos.

¿Hay alguna alternativa razonable a mi enfoque?

Respuesta

4

Suena como lo que realmente quiere es un tipo de suma. Aunque C# no tiene estos integrados, hay un truco de la programación funcional que puede usar llamado codificación Church para lograr esto. Es completamente seguro, sin moldes, sin embargo, es un poco raro usarlo en C# principalmente debido a las limitaciones de la inferencia de tipo.

El truco principal es que en lugar de usar propiedades y comprobaciones para recuperar una de las dos alternativas, tenemos una función de orden superior Map que toma dos funciones como argumentos y llama la adecuada según la alternativa que esté presente. He aquí cómo usted lo utilizaría:

var stack = new Stack<IEither<Operator, Parenthesis>>(); 

stack.Push(new Left<Operator, Parenthesis>(new Operator())); 
stack.Push(new Right<Operator, Parenthesis>(new Parenthesis())); 

while (stack.Count > 0) 
{ 
    stack.Pop().Map(op => Console.WriteLine("Found an operator: " + op), 
        par => Console.WriteLine("Found a parenthesis: " + par)); 
} 

Aquí está la implementación de IEither, Left y Right. Son completamente genéricos y podrían usarse en cualquier lugar que desee un tipo de suma.

public interface IEither<TLeft, TRight> 
{ 
    TResult Map<TResult>(Func<TLeft, TResult> onLeft, Func<TRight, TResult> onRight); 
    void Map(Action<TLeft> onLeft, Action<TRight> onRight); 
} 

public sealed class Left<TLeft, TRight> : IEither<TLeft, TRight> 
{ 
    private readonly TLeft value; 

    public Left(TLeft value) 
    { 
     this.value = value; 
    } 

    public TResult Map<TResult>(Func<TLeft, TResult> onLeft, Func<TRight, TResult> onRight) 
    { 
     return onLeft(value); 
    } 

    public void Map(Action<TLeft> onLeft, Action<TRight> onRight) 
    { 
     onLeft(value); 
    } 
} 

public sealed class Right<TLeft, TRight> : IEither<TLeft, TRight> 
{ 
    private readonly TRight value; 

    public Right(TRight value) 
    { 
     this.value = value; 
    } 

    public TResult Map<TResult>(Func<TLeft, TResult> onLeft, Func<TRight, TResult> onRight) 
    { 
     return onRight(value); 
    } 

    public void Map(Action<TLeft> onLeft, Action<TRight> onRight) 
    { 
     onRight(value); 
    } 
} 

Referencias:

+0

¡Gracias! Esto es exactamente lo que estaba buscando. – Vladislav

+0

@Vladislav esto es similar a la respuesta de rationull (básicamente la idea es usar un tipo de tupla para contener datos heterogéneos) pero supongo que podría combinar las ideas de ambos para hacerlo aún más corto (digo esto porque te gusta que sea menos detallado). – nawfal

1

Tal vez podría definir un tipo de titular pequeño para cada uno, uno con una propiedad Expresión y una propiedad Operador y el otro con una propiedad Operador y una propiedad de paréntesis. Los usuarios y constructores pueden afirmar o garantizar que solo uno se rellene. La cola y la pila contendrían el tipo de soporte apropiado.

Un poco incómodo pero seguro y manejable.

Esperemos que alguien tenga una idea más inteligente.

+0

Gracias - que no es algo que yo he considerado - y definitivamente tiene sentido, aunque un litt le verbose – Vladislav

0

Si el nombre le molesta, ¿qué tal IQueueable y IStackable? Aquí hay algunas otras preguntas de stackoverflow similares a la suya (sobre las interfaces de marcador).

What is the purpose of a marker interface?

Interface without any members - bad practice?

+0

Mi preocupación es que estoy proporcionando estas clases con interfaces, con el único propósito de implementar este algoritmo. No hay nada inherentemente inestable sobre las expresiones, y no hay nada intrínsecamente inquebrantable sobre paréntesis. Si cambio mi algoritmo de análisis, estas interfaces serán peso muerto en la clase. – Vladislav

Cuestiones relacionadas