Quick 'for' and 'if' one-line loop

Hi,
For some very simple function like +=1 if the element of this array is 0, is there any concise way?

c=[0,1,0]
map(x->x=0, c) #but I wish to add a **if** in this one-line for loop

#otherwise how verbose my usual solution is:
function plusone(c) #empirically saying, need to make it a function for outputing
#the changed variable to main macro (instead of losing) 
    for i in 1:length(c)
        if i == 0
            c[i] = 1
        end
        return c  #output make no dif
    end
end
3 Likes

In general, you can use && as an if condition as in i==0 && c[i]+=1 (|| as well)
In this case, you could also do

for i in 1:length(c)
    c[i] += (c[i] == 0)
end

It should also be noted that the function is not the same as the original map. map does not modify c in place. You could do map!(x -> x + (x==0), c, c) for the same effect though.

It is almost always possible to condense short for loops and if conditions into a single line, but it’s not often worth it IMO.

1 Like

map(x-> x==0 ? 1 : x, c)

4 Likes

Your code example does not match your question.

I will use your written question, which is to replace zeros with ones. In that case you should use the replace! function:

replace!(c, 0=>1)
3 Likes

A generic approach

c .= ifelse.(c .== 0, 1, c)
5 Likes

This has the downside of making several copies.

If you’re referring to the c .= ... solution above, that works in-place with no copies or temporary arrays.

2 Likes

Yeah, I tried to delete, but messed up since I’m on a phone atm

#Comparison

using BenchmarkTools
c=[0,1,0]
@btime map!(x -> x + (x==0), c, c) #25.426 ns (0 allocations: 0 bytes)##perform the best
c=[0,1,0]
@btime map(x-> x==0 ? 1 : x, c) #288.844 ns (2 allocations: 128 bytes)
c=[0,1,0]
@btime c .= ifelse.(c .== 0, 1, c) #  774.766 ns (4 allocations: 96 bytes)
1 Like

Due to the small array and the very low timings these results are misleading:

julia> c=rand(Bool,100000);

julia> @benchmark map!(x -> x + (x==0), $c, $c)
BenchmarkTools.Trial:
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     78.399 μs (0.00% GC)
  median time:      78.600 μs (0.00% GC)
  mean time:        84.175 μs (0.00% GC)
  maximum time:     284.300 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark map(x-> x==0 ? 1 : x, $c)
BenchmarkTools.Trial:
  memory estimate:  97.78 KiB
  allocs estimate:  3
  --------------
  minimum time:     91.101 μs (0.00% GC)
  median time:      116.000 μs (0.00% GC)
  mean time:        119.357 μs (3.91% GC)
  maximum time:     6.092 ms (95.87% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark c .= ifelse.($c .== 0, 1, $c)
BenchmarkTools.Trial:
  memory estimate:  96 bytes
  allocs estimate:  4
  --------------
  minimum time:     90.599 μs (0.00% GC)
  median time:      90.700 μs (0.00% GC)
  mean time:        92.172 μs (0.00% GC)
  maximum time:     300.601 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

Still the first variant is best.

But what you actually do is:

julia> @benchmark trues(100000)
BenchmarkTools.Trial:
  memory estimate:  12.41 KiB
  allocs estimate:  2
  --------------
  minimum time:     460.000 ns (0.00% GC)
  median time:      1.710 μs (0.00% GC)
  mean time:        2.379 μs (14.36% GC)
  maximum time:     180.490 μs (98.80% GC)
  --------------
  samples:          10000
  evals/sample:     10

or in our case

julia> trues(3)
3-element BitArray{1}:
 1
 1
 1

I guess its only a MWE for something more complex :slight_smile:

1 Like

I think you also need to interpolate the global variable into the benchmarking expression:

julia> c=rand(0:1, 100_000);

julia> @btime map!(x -> x + (x==0), $c, $c);
50.999 μs (0 allocations: 0 bytes)

julia> @btime map(x-> x==0 ? 1 : x, $c);
73.101 μs (3 allocations: 781.34 KiB)

julia> @btime $c .= ifelse.($c .== 0, 1, $c);
51.399 μs (0 allocations: 0 bytes)                
3 Likes

Didn’t I? oh, I forgot one. Timings don’t change on my PC.

1 Like

Sorry I actually missed that you did - I copied @1634’s example, and saw large differences between interpolated/not interpolated.

1 Like

Why in second test case map is used instead of map!? It looks like different things are compared.

What’s the purpose of having interpolation of global variable into the benchmark? Is this for mimicing the indexing of variable in a function or macro?

2 Likes

Thanks @nilshg for sharing the link. Unfortunately the link’s broken. Here’s a new one:
BenchmarkTools Manual, Sub-Heading:Interpolating values into benchmark expressions

1 Like