From 8bf99cbbde9cb01681f980b527d06abc3fe22d95 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Thu, 21 Jul 2016 19:27:44 -0400 Subject: [PATCH] make x[...] .= ... assign in-place (fixes bug in #17510) --- doc/manual/functions.rst | 4 ++++ src/julia-syntax.scm | 16 +++++++++++++--- test/broadcast.jl | 8 ++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/doc/manual/functions.rst b/doc/manual/functions.rst index e898f663201ef..1a97ca273e989 100644 --- a/doc/manual/functions.rst +++ b/doc/manual/functions.rst @@ -660,6 +660,10 @@ calls do not allocate new arrays over and over again for the results except that, as above, the ``broadcast!`` loop is fused with any nested "dot" calls. For example, ``X .= sin.(Y)`` is equivalent to ``broadcast!(sin, X, Y)``, overwriting ``X`` with ``sin.(Y)`` in-place. +If the left-hand side is a ``getindex`` expression, e.g. +``X[2:end] .= sin(Y)``, then it translates to ``broadcast!`` on a ``view``, +e.g. ``broadcast!(sin, view(X, 2:length(X)), Y)``, so that the left-hand +side is updated in-place. (In future versions of Julia, operators like ``.*`` will also be handled with the same mechanism: they will be equivalent to ``broadcast`` calls and diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index c08d48843e963..d1b7da8301c7b 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -1542,6 +1542,15 @@ (cadr expr) ;; eta reduce `x->f(x)` => `f` `(-> ,argname (block ,@splat ,expr))))) +(define (ref-to-view expr) + (if (and (pair? expr) (eq? (car expr) 'ref)) + (let* ((ex (partially-expand-ref expr)) + (stmts (butlast (cdr ex))) + (refex (last (cdr ex))) + (nuref `(call view ,(caddr refex) ,@(cdddr refex)))) + `(block ,@stmts ,nuref)) + expr)) + ; fuse nested calls to expr == f.(args...) into a single broadcast call, ; or a broadcast! call if lhs is non-null. (define (expand-fuse-broadcast lhs rhs) @@ -1657,14 +1666,15 @@ (cons farg new-fargs) (cons arg new-args) renames varfarg vararg)))))) (cf (cdadr f) args '() '() '() '() '())) e)) ; (not (fuse? e)) - (let ((e (compress-fuse (dot-to-fuse rhs)))) ; an expression '(fuse func args) if expr is a dot call + (let ((e (compress-fuse (dot-to-fuse rhs))) ; an expression '(fuse func args) if expr is a dot call + (lhs_ (ref-to-view lhs))) ; x[...] expressions on lhs turn in to view(x, ...) to update x in-place (if (fuse? e) (if (null? lhs) (expand-forms `(call broadcast ,(from-lambda (cadr e)) ,@(caddr e))) - (expand-forms `(call broadcast! ,(from-lambda (cadr e)) ,lhs ,@(caddr e)))) + (expand-forms `(call broadcast! ,(from-lambda (cadr e)) ,lhs_ ,@(caddr e)))) (if (null? lhs) (expand-forms e) - (expand-forms `(call broadcast! identity ,lhs ,e)))))) + (expand-forms `(call broadcast! identity ,lhs_ ,e)))))) ;; table mapping expression head to a function expanding that form (define expand-table diff --git a/test/broadcast.jl b/test/broadcast.jl index 31a2166a4ee14..52aeacc859041 100644 --- a/test/broadcast.jl +++ b/test/broadcast.jl @@ -265,6 +265,14 @@ let x = [1:4;], y = x @test y === x == [8,8,8,8] y .-= 1:4 @test y === x == [7,6,5,4] + x[1:2] .= 1 + @test y === x == [1,1,5,4] + x[1:2] .+= [2,3] + @test y === x == [3,4,5,4] + x[:] .= 0 + @test y === x == [0,0,0,0] + x[2:end] .= 1:3 + @test y === x == [0,1,2,3] end # PR 16988