2013-10-18 15 views
5

Was ist der beste Weg, um ein Element in einer Sammlung mit Linsen zu aktualisieren? Zum Beispiel:Scala-Objektiv für Sammlungs-Parameter

case class Ingredient(name: String, quantity: Int) 
case class Recipe(val ingredients: List[Ingredient]) 

Wenn ich will, Linsen verwenden, um ein neues Rezept mit der Menge eines einzelnen Bestandteils zu schaffen geändert, was ist der beste Weg, es zu tun?

Der Ansatz, den ich ausprobiert habe, ist eine Linse im laufenden Betrieb zu erstellen: Lens[List[Ingredient], Ingredient]. Das fühlt sich ein wenig klobig aber:

case class Recipe(val ingredients: List[Ingredient]) { 
    import Recipe._ 
    def changeIngredientQuantity(ingredientName: String, newQuantity: Int) = { 
    val lens = ingredientsLens >=> ingredientLens(ingredientName) >=> Ingredient.quantityLens 
    lens.set(this, newQuantity) 
    } 
} 

object Recipe { 
    val ingredientsLens = Lens.lensu[Recipe, List[Ingredient]](
    (r, i) => r.copy(ingredients = i), 
    r => r.ingredients 
) 
    def ingredientLens(name: String) = Lens.lensu[List[Ingredient], Ingredient](
    (is, i) => is.map (x => if (x.name == name) i else x), 
    is => is.find(i => i.name == name).get 
) 
} 

case class Ingredient(name: String, quantity: Int) 

object Ingredient { 
    val quantityLens = Lens.lensu[Ingredient, Int](
    (i, q) => i.copy(quantity = q), 
    i => i.quantity 
) 
} 
+0

Sie 'val' bei Klassenparameter weglassen können, sind sie standardmäßig vals – 4lex1v

Antwort

5

Sie können keine Linse zwischen einer Liste [T] und T bei einem bestimmten Index erstellen, da eine Linse erfordert, dass das Objekt, auf das Sie fokussieren, immer vorhanden ist. Im Falle eines Nachschlagens in einer Liste oder in einer anderen Sammlung ist jedoch möglicherweise kein Element am Index vorhanden.

Sie könnten jedoch ein Traversal verwenden, eine Art Linse, die sich auf 0 bis viele Elemente konzentriert. Mit Monocle, würden Sie die Index-Funktion verwenden, um eine Traversal von einer Liste zu einem Element zu einem bestimmten Index zu erstellen:

import monocle.SimpleLens 
import monocle.syntax.lens._  // to use |-> and |->> instead of composeLens, composeTraversal 
import monocle.functions.Index._ // to use index Traversal 

// monocle also provides a macro to simplify lens creation 
val ingredientsLens = SimpleLens[Recipe, List[Ingredient]](_.ingredients, (recipe, newIngredients) => recipe.copy(ingredients = newIngredients)) 
val quantityLens = SimpleLens[Ingredient, Int](_.quantity   , (ingredient, newQuantity) => ingredient.copy(quantity = newQuantity)) 

val applePie = Receipe(List(Ingredient("apple", 3), Ingredient("egg", 2), ...)) 


applePie |-> ingredientsLens |->> index(0) headOption // Some(Ingredient("apple", 3)) 
applePie |-> ingredientsLens |->> index(999) headOption // None 
applePie |-> ingredientsLens |->> index(0) |->> quantityLens headOption // 3 
applePie |-> ingredientsLens |->> index(0) |->> quantityLens set 5 
// Receipe(List(Ingredient("apple", 5), Ingredient("egg", 2), ...)) 
Verwandte Themen