Pregunta

En el ContainsIngredients método en el código siguiente, es posible que la caché de la p.Ingredientes valor de referencia explícitamente varias veces?Esto es bastante trivial ejemplo que acabo de cocinado para fines ilustrativos, pero el código que estoy trabajando en valores de referencias profundo dentro de p por ejemplo. p.InnerObject.ExpensiveMethod().Valor

editar:Estoy usando el PredicateBuilder de http://www.albahari.com/nutshell/predicatebuilder.html

public class IngredientBag
{
    private readonly Dictionary<string, string> _ingredients = new Dictionary<string, string>();

    public void Add(string type, string name)
    {
        _ingredients.Add(type, name);
    }

    public string Get(string type)
    {
        return _ingredients[type];
    }

    public bool Contains(string type)
    {
        return _ingredients.ContainsKey(type);
    }
}

public class Potion
{
    public IngredientBag Ingredients { get; private set;}
    public string Name {get; private set;}        

    public Potion(string name) : this(name, null)
    {

    }

    public Potion(string name, IngredientBag ingredients)
    {
        Name = name;
        Ingredients = ingredients;
    }

    public static Expression<Func<Potion, bool>> 
        ContainsIngredients(string ingredientType, params string[] ingredients)
    {
        var predicate = PredicateBuilder.False<Potion>();
        // Here, I'm accessing p.Ingredients several times in one 
        // expression.  Is there any way to cache this value and
        // reference the cached value in the expression?
        foreach (var ingredient in ingredients)
        {
            var temp = ingredient;
            predicate = predicate.Or (
                p => p.Ingredients != null &&
                p.Ingredients.Contains(ingredientType) &&
                p.Ingredients.Get(ingredientType).Contains(temp));
        }

        return predicate;
    }

}


[STAThread]
static void Main()
{
    var potions = new List<Potion>
    {
        new Potion("Invisibility", new IngredientBag()),
        new Potion("Bonus"),
        new Potion("Speed", new IngredientBag()),
        new Potion("Strength", new IngredientBag()),
        new Potion("Dummy Potion")
    };

    potions[0].Ingredients.Add("solid", "Eye of Newt");
    potions[0].Ingredients.Add("liquid", "Gall of Peacock");
    potions[0].Ingredients.Add("gas", "Breath of Spider");

    potions[2].Ingredients.Add("solid", "Hair of Toad");
    potions[2].Ingredients.Add("gas", "Peacock's anguish");

    potions[3].Ingredients.Add("liquid", "Peacock Sweat");
    potions[3].Ingredients.Add("gas", "Newt's aura");

    var predicate = Potion.ContainsIngredients("solid", "Newt", "Toad")
        .Or(Potion.ContainsIngredients("gas", "Spider", "Scorpion"));

    foreach (var result in 
                from p in potions
                where(predicate).Compile()(p)
                select p)
    {
        Console.WriteLine(result.Name);
    }
}
¿Fue útil?

Solución

No puedes simplemente escribir su expresión booleana aparte en una función estática que puedes llamar desde tu lambda - pasando p.Ingredientes como parámetro...

private static bool IsIngredientPresent(IngredientBag i, string ingredientType, string ingredient)
{
    return i != null && i.Contains(ingredientType) && i.Get(ingredientType).Contains(ingredient);
}

public static Expression<Func<Potion, bool>>
                ContainsIngredients(string ingredientType, params string[] ingredients)
{
    var predicate = PredicateBuilder.False<Potion>();
    // Here, I'm accessing p.Ingredients several times in one 
    // expression.  Is there any way to cache this value and
    // reference the cached value in the expression?
    foreach (var ingredient in ingredients)
    {
        var temp = ingredient;
        predicate = predicate.Or(
            p => IsIngredientPresent(p.Ingredients, ingredientType, temp));
    }

    return predicate;
}

Otros consejos

Han considerado que la Memoization?

La idea básica es esta;si usted tiene una cara llamada a la función, hay una función que calcule el valor caro, en primera convocatoria, pero volver a una versión en caché a partir de entonces.La función de este aspecto;

static Func<T> Remember<T>(Func<T> GetExpensiveValue)
{
    bool isCached= false;
    T cachedResult = default(T);

    return () =>
    {
        if (!isCached)
        {
            cachedResult = GetExpensiveValue();
            isCached = true;
        }
        return cachedResult;

    };
}

Esto significa que usted puede escribir en este;

    // here's something that takes ages to calculate
    Func<string> MyExpensiveMethod = () => 
    { 
        System.Threading.Thread.Sleep(5000); 
        return "that took ages!"; 
    };

    // and heres a function call that only calculates it the once.
    Func<string> CachedMethod = Remember(() => MyExpensiveMethod());

    // only the first line takes five seconds; 
    // the second and third calls are instant.
    Console.WriteLine(CachedMethod());
    Console.WriteLine(CachedMethod());
    Console.WriteLine(CachedMethod());

Como estrategia general, que podría ser de ayuda.

Bueno, en este caso, si usted no puede utilizar Memoization, está bastante restringido, ya que sólo puede utilizar la pila como la memoria caché:Usted no tiene ninguna manera de declarar una nueva variable en el ámbito de aplicación que usted necesita.Todo lo que puedo pensar (y no estoy diciendo que va a ser bastante) que va a hacer lo que quieras, pero retener el composability usted necesita sería algo así como...

private static bool TestWith<T>(T cached, Func<T, bool> predicate)
{
    return predicate(cached);
}

public static Expression<Func<Potion, bool>>
                ContainsIngredients(string ingredientType, params string[] ingredients)
{
    var predicate = PredicateBuilder.False<Potion>();
    // Here, I'm accessing p.Ingredients several times in one 
    // expression.  Is there any way to cache this value and
    // reference the cached value in the expression?
    foreach (var ingredient in ingredients)
    {
        var temp = ingredient;
        predicate = predicate.Or (
            p => TestWith(p.Ingredients,
                i => i != null &&
                     i.Contains(ingredientType) &&
                     i.Get(ingredientType).Contains(temp));
    }

    return predicate;
}

Usted puede combinar los resultados de varios TestWith se llama a una más compleja expresión booleana donde se requiere de caché de la correspondiente cara de valor con cada llamada o puede anidar dentro de las funciones lambda pasa como segundo parámetro para lidiar con su complejo de las jerarquías profundas.

Sería bastante difícil de leer, aunque el código y ya que usted podría ser la introducción de un montón más de la pila de transiciones con todos los TestWith llamadas, si mejora el rendimiento dependerá de cuán caro su ExpensiveCall() era.

Como nota, no habrá ninguna inline en el ejemplo original como sugiere otra respuesta ya que la expresión compilador no hace que el nivel de optimización que yo sepa.

Yo diría que no en este caso.Supongo que el compilador puede averiguar de que se utiliza el p.Ingredients variable 3 veces y se mantenga la variable cercanía a en la pila o los registros o lo que utiliza.

Turbulentos Intelecto tiene exactamente la respuesta correcta.

Solo quiero avisar que puede eliminar algunos de los valores nulos y excepciones de los tipos que se están utilizando para hacer más amigable el uso de ellos.

    public class IngredientBag
    {
      private Dictionary<string, string> _ingredients = 
new Dictionary<string, string>();
      public void Add(string type, string name)
      {
        _ingredients[type] = name;
      }
      public string Get(string type)
      {
        return _ingredients.ContainsKey(type) ? _ingredients[type] : null;
      }
      public bool Has(string type, string name)
      {
        return name == null ? false : this.Get(type) == name;
      }
    }

    public Potion(string name) : this(name, new IngredientBag())    {    }

Entonces, si usted tiene los parámetros de la consulta en esta estructura...

Dictionary<string, List<string>> ingredients;

Usted puede escribir la consulta como esta.

from p in Potions
where ingredients.Any(i => i.Value.Any(v => p.IngredientBag.Has(i.Key, v))
select p;

PS, ¿por qué readonly?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top