100 Exercises with Solutions in Julia

Based on 100 NumPy Exercises and inspired by a similar initiative I created 100 Julia Exercises with Solutions.

By doing this I have learned a lot on Julia (Mainly better use of Array Comprehensions) and had a great time.
I also tried to make some questions clearer (Hopefully I got them right at all).

Feel free to share and also teach me better ways to do the questions.
I’d also love suggestions for new questions.
I am pretty sure that going through those exercises will get anyone much better user of Julia.

25 Likes

Nice job! Those last ones in particular are a real trial of perseverance…

General notes:

  • Style: obviously you are free to use whatever style you like, and it’s a matter of personal preference. That said:
    • The generally accepted style in julia is to use snake_case instead of camelCase for everything except types, which are TitleCase.
    • The reason ii is historically used in matlab is because i is the imaginary number (so is j, hence jj). In julia, that issue doesn’t exist, so you should use i instead of ii. Likewise for jj, kk, and all other eyesore indices. :wink:
    • In my opinion, Idx is no more informative, and far less readable than simply i. Likewise where you use for (elmIdx, elmVal) in enumerate(vA), I think it’s much preferred to write for (i, a) in enumerate(A).
    • I understand that names like vA, mA are meant to denote vectors and matrices, but to be honest I don’t find it to be the most convincing convention (if I saw it in code, I wouldn’t blindly trust that the author got it right, and if I’m going to go through and confirm things anyway, why not just write e.g. a and A?).
  • I know you use global to work around the local scope created by Literate.jl, but for all of the cases where you use global I’ve seen, you should really be putting that code in a function anyway (even if it didn’t need the ugly globals) and then calling that function on the given values.
  • Completely setting aside your solutions for a moment, some of the questions just aren’t very good… Some are simply unsuited for julia, others are worded poorly or ambiguously, and some just aren’t useful things to do or learn. That’s got nothing to do with you obviously, but now that I’ve seen them all, I wouldn’t personally suggest this set of 100 questions as julia exercises :man_shrugging:

Notes on specific exercises:

    1. How about a comprehension here:
      Int[i ∈ (1, n) || j ∈ (1, m) for i in 1:n, j in 1:m]
    1. And here: Int[isodd(i+j) for i in 1:8, j in 1:8]
    1. You can do x -> 3 < x < 8 in the map (you do it below in the broadcast, just wanted to note it’s always possible).
    1. ' can actually give “row-like” shape to a range! So you can do:
      repeat((0:4)', 5, 1)
      edit: a comprehension would work here too: [j for _ in 0:4, j in 0:4]
    1. no need for collect, simply [x for x in 1:10] will do.
    1. use sort! to do it in place
    1. Don’t do this in the global scope. You should write a function for this exercise (it even asks for one)! By the way, how general can you be with this? What about iterators that don’t have a length method?
    1. Like I suggested to the previous poster of this question set, can you make the output match the shape of the input? It can be non-trivial to do that, but probably more in line with the spirit of the question.
    1. If the maximum appears more than once, this will only replace the first occurrence. Maybe that doesn’t matter though :man_shrugging:
    1. Meshgrid is a horrible invention. It should literally never be used, ever. What about something simpler and clearer (and with a more useful result), like
      [(i, j) for i in 0:0.25:1, j in 0:0.25:1]

edit: or way crazier: tuple.(0:0.25:1, (0:0.25:1)')

    1. the reason I suggested (in the other post) to do this one with a function rather than the broadcast is that abs.(vA .- inputVal) allocates a vector, of which you subsequently take the argmin. That allocation isn’t strictly necessary, though, and if you were concerned with performance, it’s worth eliminating.
    1. This is the correct result from working through the linear algebra, but what if you did it “as described”, and preferably with a helper function, what would that look like? I don’t know off the top of my head which way is faster, but I can predict which one is clearer…
    1. This one is impossible in julia. Types of elements cannot be changed in-place (in a strictly typed container, anyway). However, that’s also not possible in numpy, so I can’t imagine what the authors meant…
    1. what about using a helper function? e.g. defining gaussian(μ, σ, x)
    1. there is a function partialsortperm that you can use to find only the 2nd number without performing the full sort.
    1. This question is ambiguous I guess, but the main thing that stands out is that you rarely ever use 1xn or nx1 matrices in julia. You would instead see length-n vectors, and the transposes of those. Also note that you don’t need to collect the values with [] in order to perform the sum (it’s actually less efficient to do so). So it would be more natural to write: sum(i+j for i in a, for j in b') for vectors a and b. That’s technically not what the question gives you, but it’s a bad question for julia…
    1. Both of those one-liners are the sort of thing you would curse yourself for writing when you need to debug it 6 months down the line. Consider that you can extract a “pixel” from that image with image[i, j, :]. Does that simplify things? If you were going to do it with the method you used, it would be kinder to future you to group those operations in an intuitive and clearer way! One-liners are neat, but there’s no intrinsic value in doing something in one line instead of three.
    1. You used a 4-dimensional array here by mistake it seems. Also note that the dims argument to sum can be a tuple. E.g. sum(A, dims=(1,2))

To be honest, my eyes glaze over when I look at most of the the 3 star exercises. This isn’t really your fault; they’re not well written exercises (i.e. the language is unclear and convoluted), and the operations they want you to perform are often contrived and not very instructive in my opinion. I would, however, make the same general suggestions as above: put your code in functions, use helper functions wherever they help, group operations intuitively when you can.
I skimmed a few more and then gave up…

    1. numDiagElements = min(size(mA, 1), size(mB, 2)); probably taking the min here is ill-advised. If they’re not equal you don’t have a valid matrix product on your hands.
    1. LinearAlgebra provides the Symmetric type (which I now notice you call in your constructor, which is weird…)
    1. No need to resort to a string here. You can use digits with base=2 to get the bits as a vector, or you can even go with a series of bitshifts (with >> or <<) and then check if the last bit is set with, e.g. isodd, or x&1 == 1, etc…
11 Likes

First, appreciate your thorough feedback.
I must say that while I’m not an advanced Julia user I can not be considered a starter, mainly due to similarity of Julia to MATLAB and my experience with it.

I know my coding style doesn’t match what most people use.
As an experience MATLAB user I can approve this is why I started using ii, I keep using it as I switch between the 2 a lot, so it stays :-).

I took some of your suggestions and pushed them with the proper credit.
Some remarks:

I am well aware of it. My idea was to try out slicers. I wish eachslice() would support a tuple for the dim parameter. So something like for vPx in eachslice(mI, dims = (1, 2)) would work.

Matrix multiplication is valid when size(mA, 2) == size(mB, 1). This is not what I did. What I did is the number of diagonal elements. Think of Fat and Thin matrices.

The exercise requires a type which allows updating data. With symmetric you need to update the original data. I also used this exercise to show how to create a simple type based on AbstractArray.

Regarding the questions, I agree. They are not perfect. I think more clarity is needed. But according to the popularity of the original repository I guess they are good. So probably they are on the good enough category.

If you have ideas for some new questions, let me know, I will add them.

1 Like