2016-06-29 22 views
1

Motivation zu vermeiden:Scheme Neuberechnung

Aus Gründen der Abstraktheit habe ich eine Methode eine Liste von Objekten zu behandeln. Hier zeige ich eine vereinfachte Version für Illustrationszwecke (mit python2.7 hier):

def foo(obj_lst, f): 
    return list(map(f, obj_lst)) 

jedoch in einigen Fällen könnte der Eingang foo([obj] * 1000, f) sein, dann in den Funktionsaufruf ich 1000 mal von f neu zu berechnen haben (obj). Wir könnten es möglicherweise vermeiden, weil alle diese genau dasselbe Objekt sind.

Meine Lösung:

Ich kann immer das Berechnungsergebnis zwischenzuspeichern, wie

def foo2(obj_lst, f): 
    cache_map = {} 
    def foo_single(obj): 
     if id(obj) not in cache_map: 
      cache_map[id(obj)] = f(obj) 
     return cache_map[id(obj)] 
    result_lst = [] 
    for obj in obj_lst: 
     result_lst.append(foo_single(obj)) 
    return result_lst 

Und das tut genau den Job ich will, und es kann in der Tat die erneute Berechnung Kopf Speedup.

Meine Frage:

Diese Lösung nicht genug ordentlich für mich ist, weil ich manuell tun, in jeder Funktion haben, wird eine bessere Lösung zur Vermeidung eines allgemeinen „same-Objekt-Neuberechnung“ da sein für nicht zufällige Funktionen? Eine globale cache_map mit Schlüsseln aus der Funktions-ID und allen Argumenten scheint nicht zu funktionieren, da die Objekt-IDs nur während ihrer Lebensdauer eindeutig sind.

Im Allgemeinen verstehe ich, dass dies in Python nicht zu viel Sinn macht, weil diese Objekte veränderbar sind. Darf ich fragen, ob es ein existierendes Schema in funktionalen Programmiersprachen wie Scala gibt, das dieses Problem für unveränderliche Objekte behandelt? Vielen Dank!

+0

Vielleicht eine Art von Funktion Dekorateur? –

+0

Hi @ScottHunter, tut mir leid, das ist mir nicht ganz geläufig, kannst du mir ein paar Hinweise geben? –

+0

Warum müssen Sie es manuell in jeder Funktion machen? Sie übergeben die Funktion als Argument, nicht wahr? – Dima

Antwort

4

Sie beschreiben memoization.

Dies kann durch creating your own helper/decorator function oder mit functools.lru_cache aus der Standardbibliothek (Python 3.2 +)

+0

Super! Ich werde es versuchen. –

+0

Rechts. Es ist Memoisierung. Eine Alternative, die ich gemacht habe, ist, den Speicher Call-Location-spezifisch zu machen. In unserem speziellen Gebrauch machte dies einen enormen Unterschied. –

+0

Ich habe das gerade versucht, aber habe ein paar Fragen. Manchmal ist der Hashcode-Overhead möglicherweise zu groß, wenn wir ein großes Objekt haben, und wir wollen genau wissen, ob es dasselbe Objekt im Speicherpool ist oder nicht. Aber die Verwendung von 'id (obj) 'kann ein Nicht-Eindeutigkeits-Problem außerhalb der Lebensdauer von' obj 'verursachen. Wie können wir den Dekorateur richtig definieren, damit er wirklich reflektiert, dass es das gleiche Objekt ist, das wir gesehen haben? (Angenommen, Objekte sind immer unveränderlich) –

0

etwas wie dies vielleicht getan werden?

import java.util.{Collections, WeakHashMap} 
case class Memoized[T,R](f: T => R) extends Function1[T,R] { 
    val mem = Collections.synchronizedMap(new WeakHashMap[T,R]) 
    def apply(t: T) = Option(mem.get(t)).getOrElse { 
     val r = f(t) 
     mem.put(t, r) 
     r 
    } 
} 

Sie können dann nur tun (unter anderem verwendet) foo(objectsWithDupes, Memoized(f))

+0

Das hängt davon ab T muss hashable sein, oder? –

+0

In gewisser Weise ja ...Es muss eine Möglichkeit geben zu sagen, ob die beiden Instanzen "gleich" sind oder nicht. Sie können 't' durch' id (t) 'in' get' und 'put' ersetzen, wenn Sie bevorzugen. – Dima

+0

Hat Scala das Äquivalent von Python 'id'? Manchmal sind die Objekte selbst riesig, also ist Hashcode selbst nicht ausreichend ... –