2013-06-08 8 views
5

Ist es möglich, die Stream-Fusion bei der Verarbeitung einer vector-Funktion beizubehalten, wenn die Funktion unsafeUpdate_ verwendet wird, um einige Elemente einer vector zu aktualisieren? Die Antwort scheint Nein in dem Test zu sein, den ich gemacht habe. Für den folgenden Code wird temporärer Vektor in upd Funktion erzeugt, wie im Kern bestätigt:Keine Stream-Fusion mit unsafeUpdate_ im ungepackten Vektor

module Main where 
import Data.Vector.Unboxed as U 

upd :: Vector Int -> Vector Int 
upd v = U.unsafeUpdate_ v (U.fromList [0]) (U.fromList [2]) 

sum :: Vector Int -> Int 
sum = U.sum . upd 

main = print $ Main.sum $ U.fromList [1..3] 

Im Kern $wupd Funktion in sum verwendet wird - wie unten zu sehen ist, erzeugt es neue bytearray:

$wupd :: Vector Int -> Vector Int 
$wupd = 
    \ (w :: Vector Int) -> 
    case w `cast` ... of _ { Vector ipv ipv1 ipv2 -> 
    case main11 `cast` ... of _ { Vector ipv3 ipv4 ipv5 -> 
    case main7 `cast` ... of _ { Vector ipv6 ipv7 ipv8 -> 
    runSTRep 
     (\ (@ s) (s :: State# s) -> 
     case >=# ipv1 0 of _ { 
      False -> case main6 ipv1 of wild { }; 
      True -> 
      case newByteArray# (*# ipv1 8) (s `cast` ...) 
      of _ { (# ipv9, ipv10 #) -> 
      case (copyByteArray# ipv2 (*# ipv 8) ipv10 0 (*# ipv1 8) ipv9) 
        `cast` ... 

Es gibt eine schöne, enge Schleife im Kern für sum Funktion, aber kurz vor dieser Schleife gibt es einen Aufruf an $wupd Funktion, und so, eine temporäre Generation.

Gibt es eine Möglichkeit, temporäre Generierung im hier gezeigten Beispiel zu vermeiden? Die Art, wie ich darüber nachdenke, besteht darin, einen Vektor in Index i zu aktualisieren, indem man einen Stream analysiert, aber nur auf den Stream in Index i einwirkt (den Rest überspringt) und das Element dort durch ein anderes Element ersetzt. Also sollte das Aktualisieren eines Vektors an einem beliebigen Ort die Stream-Fusion nicht unterbrechen, richtig?

+0

Abgesehen: 'yarr' unterstützt Fusion und Abrollen zusammen mit an Ort und Stelle zu ändern: http: //hackage.haskell .org/packages/archive/yarr/1.3.1/doc/html/Daten-Yarr-Repr-Foreign.html # t: F – leventov

+0

@leventov, sehr nett! Da Sie scheinen, der Autor dieser Bibliothek zu sein, würden Sie in der Lage sein, zu teilen, wie man in-Stelle schmelzbare Update-Funktion für Vektorbeispiel oben schreibt? Sicher, was du für Yarr getan hast, sollte hier anwendbar sein, oder? – Sal

+0

großer, großer Nachteil von 'yarr-1. * 'Es ist eng mit IO verbunden. In-Place-Bearbeitung ist nur "do {... write arr 0 2; ...} '. – leventov

Antwort

5

Ich kann nicht 100% sicher sein, denn mit vector ist es Schildkröten den ganzen Weg hinunter (Sie erreichen nie wirklich die tatsächliche Implementierung, es gibt immer eine andere Indirektion), aber soweit ich es verstehe, zwingen die update Varianten ein neue temporäre durch Klonen:

unsafeUpdate_ :: (Vector v a, Vector v Int) => v a -> v Int -> v a -> v a 
{-# INLINE unsafeUpdate_ #-} 
unsafeUpdate_ v is w 
    = unsafeUpdate_stream v (Stream.zipWith (,) (stream is) (stream w)) 

unsafeUpdate_stream :: Vector v a => v a -> Stream (Int,a) -> v a 
{-# INLINE unsafeUpdate_stream #-} 
unsafeUpdate_stream = modifyWithStream M.unsafeUpdate 

und modifyWithStream Anrufe clone (und new),

modifyWithStream :: Vector v a 
       => (forall s. Mutable v s a -> Stream b -> ST s()) 
       -> v a -> Stream b -> v a 
{-# INLINE modifyWithStream #-} 
modifyWithStream p v s = new (New.modifyWithStream p (clone v) s) 

new :: Vector v a => New v a -> v a 
{-# INLINE_STREAM new #-} 
new m = m `seq` runST (unsafeFreeze =<< New.run m) 

-- | Convert a vector to an initialiser which, when run, produces a copy of 
-- the vector. 
clone :: Vector v a => v a -> New v a 
{-# INLINE_STREAM clone #-} 
clone v = v `seq` New.create (
    do 
    mv <- M.new (length v) 
    unsafeCopy mv v 
    return mv) 

und ich keine Möglichkeit sehen, dass vector loswerden würde davon unsafeCopy wieder.

+0

nach diesem [Vektor-Recycling-Papier] (http://www.cse.unsw.edu.au/~rl/publications/recycling.pdf) sollen Klon/neu zusammenschmelzen, obwohl ich es nicht sehe meine Experimente. – Sal

1

Wenn Sie ein oder sehr wenige Elemente ändern müssen, gibt es in repa und yarr Bibliotheken gute Lösungen. Sie bewahren die Fusion (ich bin nicht sicher, repa) und Haskell-idiomatische.

Repa, unter Verwendung von fromFunction:

upd arr = fromFunction (extent arr) ix 
    where ix (Z .: 0) = 2 
     ix i = index arr i 

Yarr, unter Verwendung von Delayed:

upd arr = Delayed (extent arr) (touchArray arr) (force arr) ix 
    where ix 0 = return 2 
     ix i = index arr i