2013-12-23 4 views
6

Odd ...scala Map.getOrElse - wie Funktion für Standard bieten

val h = new HashMap[Long, Int]() 

    def mydefault0():Int = 101 
    println(h.getOrElse(99, default=mydefault0 _)) // Prints <function0> 

    def mydefault1(key:Long):Int = 102 
    println(h.getOrElse(98, default=mydefault1 _)) // Prints <function1> 

The docs sagen, dass Standard-Typ sein müssen: => B

Wenn ich richtig verstehe, eine no- Arg-Funktion gibt in diesem Fall einen Int zurück.

  1. Warum wird das Beispiel, das mydefault1 verwendet, kompiliert, da es ein Argument benötigt und die Spezifikation erfüllt?

  2. Warum werden Funktionen zurückgegeben, anstatt dass Funktionen aufgerufen werden, um einen Standardwert zu erhalten? Anscheinend wurde die Typsicherheit aufgehoben, weil getOrElse ein Int und keine Funktion zurückgeben muss. (Wenn ich die Dokumente missverstanden habe und fälschlicherweise eine Funktion angegeben habe, bei der ein Int-Wert benötigt wurde, warum ließ mich der Compiler eine Funktion anstelle eines Int bereitstellen?).

bearbeiten

klar:

  • erweitert HashMap und übergeordneten Standard oder
  • angeben mit HashMap.withDefault

auch eine Funktion von verwendeten lassen ein Standardwert Was ich möchte, ist in der Lage zu sein, den Standard mit einer Funktion zu überschreiben, die zum Zeitpunkt der Suche bereitgestellt wird (d.h. die Funktion kann sich während der Lebensdauer der Karte ändern)

Ist das möglich?

Antwort

14

The definition of getOrElse:

getOrElse[B1 >: B](key: A, default: => B1): B1 

default Das Argument ist nicht funktions die () => B1 nehmen würde, aber eine lazily zugegriffen Wert vom Typ B1. Diese parameterlose "Funktion" => B1 wird manchmal auch thunk genannt. Der richtige Weg, es zu benutzen ist wie folgt:

import collection.mutable 

val h = new mutable.HashMap[Long, Int]() 

def mydefault0(): Int = 101 

println(h.getOrElse(99, default = mydefault0())) 

Also, was ist es, dass Sie mit mydefault0 _ sehen? Der zurückgegebene Wert hat eindeutig den Typ B1, der ein allgemeiner Supertyp des Werttyps der Karte Int und der Standardwert sein muss. Der Standardwert ist Function0. Wenn Sie das Ergebnis zuweisen, sehen Sie, dass der Supertyp Any ist:

val x = h.getOrElse(99, default = mydefault0 _) // x: Any = <function0> 

So ist der Irrtum, anzunehmen ist, dass Sie in einer Funktion übergeben müssen, wenn tatsächlich Sie einen Ausdruck besagen, die träge ausgewertet wird. Der Aufruf mydefault0() wird nur aufgerufen, wenn der Standardwert benötigt wird. Formal ist das Argument als ein call-by-name Argument definiert.


bearbeiten: Kommentar In Bezug.Call-by-Name bedeutet, dass Sie tun jedes Mal ein neues Array erhalten.

val m = Map("foo" -> Array(1, 2, 3)) 

def myDefault = { 
    println("called-default") 
    Array(4, 5, 6) 
} 

val a1 = m.getOrElse("foo", myDefault) // myDefault not called 
val a2 = m.getOrElse("bar", myDefault) // myDefault called 
val a3 = m.getOrElse("baz", myDefault) // myDefault called 
a2 == a3 // false!! 
+0

Danke, das hat meinen Fehler geklärt. Aber zurück zur Frage ... Angenommen, ich wollte eine Map [String, Array [Int]], ich wäre nicht in der Lage, getOrElse zu verwenden, um ein neues Standard-Array zu erstellen, wenn der Schlüssel fehlt, nicht wahr? Dies würde dazu führen, dass jeder Wert das gleiche Objekt ist. Mit anderen Worten, es gibt keine Möglichkeit, eine Funktion zum Generieren eines Standardwerts bei jedem Suchvorgang bereitzustellen (das Überschreiben des Standardwerts von HashMap oder das Verwenden von Map.withDefault erlaubt mir nicht, den Standardgenerator anzugeben, wenn ich den Suchvorgang durchführe). – user48956

+1

Es ist sehr wichtig zu verstehen, was by-name Parameter sind und wie sie sich verhalten (und warum sie * nicht * "faul" sind). Das wichtigste, was man über Namensparameter wissen sollte, ist, dass die Methode * called * bestimmt, wie oft der tatsächliche Parameterausdruck ausgewertet wird, und das kann 0, 1 * oder mehr * mal sein. Ein Lazy-Parameter wird nur 0 oder 1 Mal ausgewertet. –

+0

Danke - das macht jetzt alles Sinn. – user48956