# Is \`let\` the right tool for this task?

**URL:** <https://discourse.julialang.org/t/is-let-the-right-tool-for-this-task/59763>\
**Category:** New to Julia\
**Created:** [April 22, 2021, 12:21am UTC](https://discourse.julialang.org/t/is-let-the-right-tool-for-this-task/59763 "2021-04-22T00:21:13Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![maxkapur](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/maxkapur/32/21208_2.png) [@maxkapur](https://discourse.julialang.org/u/maxkapur)\
**Post date:** [April 22, 2021, 12:21am UTC](https://discourse.julialang.org/t/is-let-the-right-tool-for-this-task/59763/1 "2021-04-22T00:21:13Z")

</div>

I would like to define a certain function that takes a vector as input and does some computations that involving varying just one of the entries. For example, if the input vector is `a`, I would like to compute `f(a)` whlie “pretending” that `a[1] = 0`.

One way to do this is `b = copy(a); b[1] = 0, f(b)`, but this requires copying `a` _each time_ I call `f`. The problem is that I will need to do this many times, letting `a[1] = 0.0`, `a[1] = 0.1`, etc., and then similar for `a[2]` (think numerical integration).

Here is a working but inefficient function that creates copies:

```julia
# Desired behavior, but makes a bunch of copies
function foo(a::Vector)
    for m in 0:0.1:1
        b = copy(a)
        b[1] = m
        # do some computations on b
        @show b
    end
end

foo(rand(3))

#= 
    b = [0.0, 0.70594453750852, 0.21204039022374865]
    b = [0.1, 0.70594453750852, 0.21204039022374865]
    b = [0.2, 0.70594453750852, 0.21204039022374865]
    b = [0.3, 0.70594453750852, 0.21204039022374865]
    b = [0.4, 0.70594453750852, 0.21204039022374865]
    b = [0.5, 0.70594453750852, 0.21204039022374865]
    b = [0.6, 0.70594453750852, 0.21204039022374865]
    b = [0.7, 0.70594453750852, 0.21204039022374865]
    b = [0.8, 0.70594453750852, 0.21204039022374865]
    b = [0.9, 0.70594453750852, 0.21204039022374865]
    b = [1.0, 0.70594453750852, 0.21204039022374865]
=#

```

Here I use `let` to avoid making copies, but I have to tuple-unpack the vector, so it only works for vectors of length 3:

```julia
# Working version using let; avoids copying, but only
# works for 3-vectors
function bar(z::Vector)
    a, b, c = z
    
    for m in 0:0.1:1
        let a = m
            # do some computations
            @show [a, b, c]
        end
    end
end

bar(rand(3))

#= 
    [a, b, c] = [0.0, 0.2899170892058196, 0.9979264085173909]
    [a, b, c] = [0.1, 0.2899170892058196, 0.9979264085173909]
    [a, b, c] = [0.2, 0.2899170892058196, 0.9979264085173909]
    [a, b, c] = [0.3, 0.2899170892058196, 0.9979264085173909]
    [a, b, c] = [0.4, 0.2899170892058196, 0.9979264085173909]
    [a, b, c] = [0.5, 0.2899170892058196, 0.9979264085173909]
    [a, b, c] = [0.6, 0.2899170892058196, 0.9979264085173909]
    [a, b, c] = [0.7, 0.2899170892058196, 0.9979264085173909]
    [a, b, c] = [0.8, 0.2899170892058196, 0.9979264085173909]
    [a, b, c] = [0.9, 0.2899170892058196, 0.9979264085173909]
    [a, b, c] = [1.0, 0.2899170892058196, 0.9979264085173909]
=#

```

Here is what I would really like to do:

```julia
# Attempt to do this without making so many copies of a
function baz(a::Vector)
    for m in 0:0.1;1
        let a = a, a[1] = m
            # do some computations on a
            @show a
        end
    end
end

baz(rand(3))

#= 
    syntax: invalid let syntax around In[66]:4

    Stacktrace:
     [1] top-level scope at In[66]:2
     [2] include_string(::Function, ::Module, ::String, ::String) at .\loading.jl:1091
=#

```

Am I on the right track? Is there a better way to accomplish this task?

---

<div class="post-metadata">

**Author:** ![jzr](https://avatars.discourse-cdn.com/v4/letter/j/eb9ed0/32.png) [@jzr](https://discourse.julialang.org/u/jzr)\
**Post date:** [April 22, 2021, 12:28am UTC](https://discourse.julialang.org/t/is-let-the-right-tool-for-this-task/59763/2 "2021-04-22T00:28:50Z")

</div>

Are you always changing the same entry `b[1]` at each iteration? If so, do you need to copy `b` every time, or can you just reuse the same one?

---

<div class="post-metadata">

**Author:** ![maxkapur](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/maxkapur/32/21208_2.png) [@maxkapur](https://discourse.julialang.org/u/maxkapur)\
**Post date:** [April 22, 2021, 12:31am UTC](https://discourse.julialang.org/t/is-let-the-right-tool-for-this-task/59763/3 "2021-04-22T00:31:54Z")

</div>

There is an inner loop in which I am just taking `a[1] = m` for `m in 0:0.1:1`, and then an outer loop where I change `a[i]` for `i in 1:length(a)`. So I can move `b = copy(a)` to the outside of the inner loop, but this is only a partial solution, because it still requires creating `length(a)` copies of `a` in the outer loop.

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [April 22, 2021, 12:40am UTC](https://discourse.julialang.org/t/is-let-the-right-tool-for-this-task/59763/4 "2021-04-22T00:40:58Z")

</div>

> [@maxkapur](#):
>
> ```julia
> let a = a, a[1] = m
> # do some computations on a
> @show a
> end
> 
> ```

Even if this were valid syntax, it wouldn’t do what you want anyway because `let a=a` doesn’t change the fact that `a` points to the _same_ value, so mutating that array will still mutate the input array (since they’re all the same array).

I’m not sure you need any copies at all, though. Can you just look up the current value of `a[I]` and store that, then restore that single value after the end of the inner loop?

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [April 22, 2021, 12:54am UTC](https://discourse.julialang.org/t/is-let-the-right-tool-for-this-task/59763/5 "2021-04-22T00:54:34Z")

</div>

I think you should be more explicit about what kind of computation do you need to do with `b`. But if it is something that really requires it as a vector, I would create a struct as

```julia
struct B{T} <: Vector{T}
  i::Int
  b::T
  a1:: Vector{T}
end
function Base.getindex(x::B,i)
  if i == x.i
    return x.b
  else
    return x.a[i]
  end
end
  

```

(Overload `size` probably as well, but I understand that you will not modify the values of b in the computation part, thus no need to overload `setindex!` and make B mutable)

With that you can do

```julia
b = B(5, 2.8, a)

```

and work with it as an array without having to copy the whole a. This `b` will behave as vector identical to `a` except at position `5`.

Edit: or simply copy the value of `a[i]` in a temporary variable, change it, and restore it later, as rdeits said, if you don’t need both a and b in the computation)

---

<div class="post-metadata">

**Author:** ![maxkapur](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/maxkapur/32/21208_2.png) [@maxkapur](https://discourse.julialang.org/u/maxkapur)\
**Post date:** [April 22, 2021, 4:01am UTC](https://discourse.julialang.org/t/is-let-the-right-tool-for-this-task/59763/6 "2021-04-22T04:01:09Z")

</div>

This has proven to be the best solution for the problem at hand.

I guess I just expected to be some a built-in way to do this kind of “substitution,” sort of like using `filter()` instead of storing a new array or the various masking features in NumPy. However, I have no real basis for this expectation, such as analogous functionality in another language, so I won’t complain.

---

<div class="post-metadata">

**Author:** ![jzr](https://avatars.discourse-cdn.com/v4/letter/j/eb9ed0/32.png) [@jzr](https://discourse.julialang.org/u/jzr)\
**Post date:** [April 22, 2021, 5:55am UTC](https://discourse.julialang.org/t/is-let-the-right-tool-for-this-task/59763/7 "2021-04-22T05:55:22Z")

</div>

Masked arrays are discussed in [Mask on a 1D array - #2 by rdeits](https://discourse.julialang.org/t/mask-on-a-1d-array/42740/2)
