Think Julia Exercise 10.1

Hey,

i think i made some progress and i am close to finding it.
This is what i have for now :

function nestedsum(t)
total = 0
for i = 1:length(t)
x1=0
for j = 1:length(t[i])
x1 = x1 .+ t[i]

  end

  total = total .+ x1

end
end
nestedsum(t)

now, for the last part, I got DimensionMismatch(“arrays could not be broadcast to a common size; got a dimension with lengths 2 and 3”) and truthfully, I don’t know how to get over it…

Thank you all for your help

Nice effort AKL.
I think the problem with your code is your use of the broadcast operator.

+ and .+ are different things.
Try the following:

a = 2
b = 3
c = a + b
println(c)

Now try this

a = [1,2,3]
b = [3,4,5]
c = a .+ b
println(c)

There’s also an indexing bug. Your code should look like this:

function nestedsum(t)
    total = 0
    for i = 1:length(t)
        x1 = 0
        for j = 1:length(t[i])
            #t[i] will get the list inside the list of lists
            #t[i][j] will get the item inside the list inside the list of lists
            x1 = x1 + t[i][j]
        end
        total = total + x1
    end 
    return total
end

But what if we told you you could iterate over your collections like the following:

for i in t
        x1 = 0
        for j in i
            ...
        end
        ...
    end 

How might your code look then?

.+ is broadcasted +. Since you should be adding only scalars in this problem, you shouldn’t need to use it. The reason you found you did need it is on this line:

x1 = x1 .+ t[i]

Consider: what is t[i]?

you’re right, i took out the broadcast and succeeded !

function nestedsum(t)
total = 0
for i = 1:length(t)
x1=0
for j = 1:length(t[i])
x1 = x1 + t[i][j]
end
total = total + x1
end
println(total)
end

1 Like

Great AKL.

Now let’s do the Julia thing and see if we can make your code simpler, and learn some things in the process.

Do you see any redundancies in your code? For example, do we need to tally the sum of each inner loop in a seperate variable? Or could we just use one variable to count the entire sum(total)?

1 Like

yes i am now working on what you told me with i in j
i’ll post what i have when it works :sweat_smile:

1 Like

It doesn’t have to work to be posted :). But your best effort and a description of what you tried is perfectly acceptable. You’re learning something new - perfection isn’t expected. No rush either.

10.1 is a great example to show some of the ways julia lets you handle things. So I’m more than willing to walk you through a bit more. Point isn’t for you to remember everything, but just remember there are alternatives and kind of guide you along a new way of thinking.

1 Like

You are telling me to use only one variable(total) to compil the code

so :sweat_smile:

function nestedsum1(t)
for i in t
        x1 = 0
        for j in i
            x1 = x1 + t[i][j]  #here, I don't understand I how I can replace the [i][j]
        end
        x1   #here, if i have only variable, should i simply say that x1 = x1+i?
    end 
	println(x1)
end

Close,
Look at what you are doing in the first loop. You are resetting this value to 0 at each iteration.

Does this will help

function nestedsum1(t)
    total = 0.0
    for i in t
        #i is now a list in t
        for j in i
            #j is now an item in i (remember i  is a list in t)
            total = total + j  
        end
    end 
    println(total)
end

oh !
I think i understood a bit better how this works !

Thank you !

1 Like

No problemo.

So basically we can iterate over collections/iterables like lists/arrays/etc using in. This will do what we expect - read each collection from the first element to the last element.

The way you originally wrote the code used the indices to iterate over your collections.

Both of these choices are mostly identical but appear a little different in code. One is easier to read, and maybe will reduce human errors, but, a programmer could prefer one or the other based on what they are comfortable with :).

Here’s another fun thing, in Julia we can rewrite nested loops like the following,

function nestedsum1(t)
    total = 0.0
    for i in t, j in i
        total = total + j  
    end 
    println(total)
end

Again, this is the same as above but, it’s nice and concise. Maybe a programmer wouldn’t want to do this because it’s a little muddled to read, but very neat.

Again, you don’t have to do any of this, but, knowing your way around loops, broadcasting, etc will help you read other peoples code :).

going further we can introduce another operator

total += j

this is equivalent too total = total + j. but looks neat and saves our fingers some extra work.

I totally agree that it is good to learn to write this using low-level constructs like loops, even though sum(sum, a) is efficient and concise.

One tricky part about explicit loops is making them type-generic—if you are writing flexible libraries, experienced Julia programmers work hard to make the code composable with arbitrary user-defined types…

If you want a loop to work for summing anything efficiently (e.g. complex numbers, matrices, different precisions, unitful values…), then you should initialize your sum to zero(eltype(eltype(a))) instead of 0 or 0.0. (Or better yet, initialize the sum using the type of the first element, in the case of non-empty arrays; this is what sum does if it can in order to handle abstractly typed containers.)

On the other hand, this is not something I would worry about for your first steps in learning Julia. Not all code needs to be generic (or fast)!

5 Likes

This may be an aside/seperate thread but I wonder what order of priority is most effective for pedagogy.

Off the top of my head I am seeing it something like the following

  1. Declaration/Instantiation
  2. Inspection/Printing/basic I/O
  3. Control Structure
  4. Types/Objects/Classes/Instances
  5. Loading/Importing/etc
  6. Recursion
  7. Algorithms
  8. Performance

Something like that anyway…

2 Likes

Unfortunately, that will not work when said eltype is Any or something else for which zero is not defined.

Yes, that’s why I said it is even better to use the first element, if the array is non-empty, to initialize the sum. That’s what sum does. If the container is abstractly typed and empty, then the only thing we can currently do is throw an error:

julia> sum(Any[])
ERROR: MethodError: no method matching zero(::Type{Any})

In Julia 1.6 there is an init keyword to specify the accumulation starting point/type (https://github.com/JuliaLang/julia/pull/36188). Generic programming is tricky (but rewarding!).

(This has long been a source of contention/confusion. See behavior of sum, prod, mapreduce... on empty arrays · Issue #8092 · JuliaLang/julia · GitHub, Mean of an empty collection · Issue #28777 · JuliaLang/julia · GitHub, sum(f, itr) when itr is an empty collection should return zero · Issue #36262 · JuliaLang/julia · GitHub, Defining zero() seriously · Issue #34003 · JuliaLang/julia · GitHub, etc.)

2 Likes

I think that in this respect, Julia is navigating uncharted territory (unprecedented combination of language featues) but will gradually develop its own solutions. At the moment replicating what language built-ins like collect are doing is tricky, but approaches like

are very promising.

4 Likes

As Chapter 10 has a subsection about map, filter and reduce, the most “logical” solution in context is trying to use those.

It is easy to see that sum of a if a is an array of Ints is reduce(a, +) (or sum(a), as shown in the same subsection).

Sum of aoa where aoa is an array of arrays is just sum of all sums of inner arrays, i.e. sum(map(sum, aoa)).

So,

julia> nestedsum(aoa) = sum(map(sum, aoa))
nestedsum (generic function with 1 method)

julia> t = [[1, 2], [3], [4, 5, 6]];

julia> nestedsum(t)
21

With multiple dispatch (which is introduced only in Chapter 17), one can write a function that works with arbitrarily nested arrays:

julia> nestedsum(a) = sum(map(nestedsum, a))
nestedsum (generic function with 1 method)

julia> nestedsum(x::Number) = x
nestedsum (generic function with 2 methods)

julia> t = [[1, 2], [3], [4, [5, 6]]];

julia> nestedsum(t)
21
8 Likes