/span>:<span class="hljs-number">100000</span>]</pre></div><figure id="7d84"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*mJr8xoWYWNG19wyJB-KWVg.png"><figcaption></figcaption></figure><figure id="55f3"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*nNdZEzW3xr7HS3eB-lpioA.png"><figcaption></figcaption></figure><p id="db7a">Considering all of the above, it certainly seems nice to be able to use comprehensions everywhere. The limitations of the typical programming language’s syntax in a comprehension is usually the largest downfall of the comprehension, but in Julia; things are a bit different. There is a lot more that you can do inside of a comprehension like this, additionally, the code can be incredibly readable. In most cases, this sort of code in something like Python would become an absolute mess.</p><div id="f1ea"><pre><span class="hljs-attr">x</span> = [if y < <span class="hljs-number">5</span> <span class="hljs-number">10</span> end for y in <span class="hljs-number">1</span>:<span class="hljs-number">10</span>]</pre></div><p id="23ec">I am not going to go incredibly in-depth on this concept here, so if you would like to read more on the topic, I do have an entire article on them that might be helpful:</p><div id="0b70" class="link-block">
<a href="https://readmedium.com/the-awesomeness-of-array-comprehension-syntax-in-julia-f6bf61479cf5">
<div>
<div>
<h2>The Awesomeness Of Array Comprehension Syntax in Julia</h2>
<div><h3>Some weird syntax rules that come with the comprehensions of Julia.</h3></div>
<div><p>medium.com</p></div>
</div>
<div>
<div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/1*By1PRdHD-ZNPQgvylQqfhw.jpeg)"></div>
</div>
</div>
</a>
</div><h1 id="2c23">Absurdity</h1><p id="07e1" type="7">notebook</p><p id="79fe">Okay, so we have witnessed some basic examples of comprehensions being flexible. Now it is time to take things to a new level by creating a crazy loop, then comparing the same loop in a comprehension. The loop I am going to create for this demonstration is going to be pretty simple, the loop will simply randomly jostle an array around, switching elements and providing a return of each number’s switch. The number of switches will be changed using an argument. To start the function, we initialize our return and then begin our loop.</p><div id="cf8a"><pre><span class="hljs-keyword">function</span> jostle(x::<span class="hljs-built_in">Vector</span>{<span class="hljs-built_in">Int64</span>}, count::<span class="hljs-built_in">Integer</span> = <span class="hljs-number">2000000</span>)
jostles = <span class="hljs-built_in">Vector</span>{<span class="hljs-built_in">Pair</span>{<span class="hljs-built_in">Int64</span>, <span class="hljs-built_in">Vector</span>{<span class="hljs-built_in">Int64</span>}}}()
<span class="hljs-keyword">for</span> shift <span class="hljs-keyword">in</span> <span class="hljs-number">1</span>:count</pre></div><p id="ca13">Inside of the loop, we simply grab two random indexes and then flip them before pushing this data into jostles.</p><div id="0f49"><pre>for shift in <span class="hljs-number">1</span>:count
<span class="hljs-keyword">switch</span><span class="hljs-number">1</span><span class="hljs-punctuation">,</span> <span class="hljs-keyword">switch</span><span class="hljs-number">2</span> <span class="hljs-operator">=</span> rand(<span class="hljs-number">1</span>:length(<span class="hljs-keyword">x</span>))<span class="hljs-punctuation">,</span> rand(<span class="hljs-number">1</span>:length(<span class="hljs-keyword">x</span>))
if <span class="hljs-keyword">switch</span><span class="hljs-number">1</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-keyword">switch</span><span class="hljs-number">2</span>
<span class="hljs-keyword">switch</span><span class="hljs-number">2</span> <span class="hljs-operator">=</span> rand(<span class="hljs-number">1</span>:length(<span class="hljs-keyword">x</span>))
<span class="hljs-keyword">end</span>
n<span class="hljs-number">1</span><span class="hljs-punctuation">,</span> n<span class="hljs-number">2</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">x</span>[<span class="hljs-keyword">switch</span><span class="hljs-number">1</span>]<span class="hljs-punctuation">,</span> <span class="hljs-keyword">x</span>[<span class="hljs-keyword">switch</span><span class="hljs-number">2</span>]
<span class="hljs-keyword">x</span>[<span class="hljs-keyword">switch</span><span class="hljs-number">1</span>] <span class="hljs-operator">=</span> n<span class="hljs-number">2</span>
<span class="hljs-keyword">x</span>[<span class="hljs-keyword">switch</span><span class="hljs-number">2</span>] <span class="hljs-operator">=</span> n<span class="hljs-number">1</span>
push!(jostles<span class="hljs-punctuation">,</span> shift <span class="hljs-operator">=</span>> [n<span class="hljs-number">1</span><span class="hljs-punctuation">,</span> n<span class="hljs-number">2</span>])
<span class="hljs-keyword">end</span></pre></div><p id="ef3a">Finally, we return <code>jostles</code> , completing our method:</p><div id="0c15"><pre>function jostle(<span class="hljs-keyword">x</span>::Vector{Int<span class="hljs-number">64</span>}<span class="hljs-punctuation">,</span> count::Integer <span class="hljs-operator">=</span> <span class="hljs-number">2000000</span>)
jostles <span class="hljs-operator">=</span> Vector{Pair{Int<span class="hljs-number">64</span><span class="hljs-punctuation">,</span> Vector{Int<span class="hljs-number">64</span>}}}()
for shift in <span class="hljs-number">1</span>:count
<span class="hljs-keyword">switch</span><span class="hljs-number">1</span><span class="hljs-punctuation">,</span> <span class="hljs-keyword">switch</span><span class="hljs-number">2</span> <span class="hljs-operator">=</span> rand(<span class="hljs-number">1</span>:length(<span class="hljs-keyword">x</span>))<span class="hljs-punctuation">,</span> rand(<span class="hljs-number">1</span>:length(<span class="hljs-keyword">x</span>))
if <span class="hljs-keyword">switch</span><span class="hljs-number">1</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-keyword">switch</span><span class="hljs-number">2</span>
<span class="hljs-keyword">switch</span><span class="hljs-number">2</span> <span class="hljs-operator">=</span> rand(<span class="hljs-number">1</span>:length(<span class="hljs-keyword">x</span>))
<span class="
Options
hljs-keyword">end</span>
n<span class="hljs-number">1</span><span class="hljs-punctuation">,</span> n<span class="hljs-number">2</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">x</span>[<span class="hljs-keyword">switch</span><span class="hljs-number">1</span>]<span class="hljs-punctuation">,</span> <span class="hljs-keyword">x</span>[<span class="hljs-keyword">switch</span><span class="hljs-number">2</span>]
<span class="hljs-keyword">x</span>[<span class="hljs-keyword">switch</span><span class="hljs-number">1</span>] <span class="hljs-operator">=</span> n<span class="hljs-number">2</span>
<span class="hljs-keyword">x</span>[<span class="hljs-keyword">switch</span><span class="hljs-number">2</span>] <span class="hljs-operator">=</span> n<span class="hljs-number">1</span>
push!(jostles<span class="hljs-punctuation">,</span> shift <span class="hljs-operator">=</span>> [n<span class="hljs-number">1</span><span class="hljs-punctuation">,</span> n<span class="hljs-number">2</span>])
<span class="hljs-keyword">end</span>
jostles
<span class="hljs-keyword">end</span></pre></div><p id="ac3f">Writing this into a comprehension is incredibly straightforward.</p><div id="d452"><pre>function compjostle(<span class="hljs-keyword">x</span>::Vector{Int<span class="hljs-number">64</span>}<span class="hljs-punctuation">,</span> count::Integer <span class="hljs-operator">=</span> <span class="hljs-number">2000000</span>)
[<span class="hljs-keyword">begin</span>
<span class="hljs-keyword">switch</span><span class="hljs-number">1</span><span class="hljs-punctuation">,</span> <span class="hljs-keyword">switch</span><span class="hljs-number">2</span> <span class="hljs-operator">=</span> rand(<span class="hljs-number">1</span>:length(<span class="hljs-keyword">x</span>))<span class="hljs-punctuation">,</span> rand(<span class="hljs-number">1</span>:length(<span class="hljs-keyword">x</span>))
if <span class="hljs-keyword">switch</span><span class="hljs-number">1</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-keyword">switch</span><span class="hljs-number">2</span>
<span class="hljs-keyword">switch</span><span class="hljs-number">2</span> <span class="hljs-operator">=</span> rand(<span class="hljs-number">1</span>:length(<span class="hljs-keyword">x</span>))
<span class="hljs-keyword">end</span>
n<span class="hljs-number">1</span><span class="hljs-punctuation">,</span> n<span class="hljs-number">2</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">x</span>[<span class="hljs-keyword">switch</span><span class="hljs-number">1</span>]<span class="hljs-punctuation">,</span> <span class="hljs-keyword">x</span>[<span class="hljs-keyword">switch</span><span class="hljs-number">2</span>]
<span class="hljs-keyword">x</span>[<span class="hljs-keyword">switch</span><span class="hljs-number">1</span>] <span class="hljs-operator">=</span> n<span class="hljs-number">2</span>
<span class="hljs-keyword">x</span>[<span class="hljs-keyword">switch</span><span class="hljs-number">2</span>] <span class="hljs-operator">=</span> n<span class="hljs-number">1</span>
shift <span class="hljs-operator">=</span>> [n<span class="hljs-number">1</span><span class="hljs-punctuation">,</span> n<span class="hljs-number">2</span>]
<span class="hljs-keyword">end</span> for shift in <span class="hljs-number">1</span>:count]
<span class="hljs-keyword">end</span></pre></div><p id="5530">Not only is that incredibly short, it is also really readable and could be interpreted as subjectively better in terms of syntax by some. Here is the comparison!</p><div id="a70f"><pre><span class="hljs-keyword">x</span> <span class="hljs-operator">=</span> [<span class="hljs-number">5</span><span class="hljs-punctuation">,</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span> <span class="hljs-number">6</span><span class="hljs-punctuation">,</span> <span class="hljs-number">7</span><span class="hljs-punctuation">,</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span> <span class="hljs-number">4</span><span class="hljs-punctuation">,</span> <span class="hljs-number">5</span>]
<span class="hljs-title">@benchmark</span> y <span class="hljs-operator">=</span> jostle(<span class="hljs-keyword">x</span>)</pre></div><figure id="8aaf"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*0Fci2lp69Pc3GPuZopBT8A.png"><figcaption></figcaption></figure><div id="1af9"><pre><span class="hljs-variable">@benchmark</span> y = <span class="hljs-built_in">compjostle</span>(x)</pre></div><figure id="7c33"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*nhH4K4Gfm7YGfY3v-H3bTQ.png"><figcaption></figcaption></figure><h1 id="e7da">To Conclude</h1><p id="2798">Comprehensions provide a very big and very bold boost to performance in a multitude of entirely different programming languages. Needless to say, this is probably a great one to use in your toolbox. That being said, this could be an easy one to accidentally overuse. Then the question becomes what constitutes overuse? The conventional explanation for why one should not use comprehensions all of the time comes in the form of keeping code looking pretty. I think this can be a great choice, because it is better to have something that performs slightly worse that people can actually work on.</p><p id="d6d1">However, with Julia the comprehension syntax is a lot more dynamic, capable, and of course; readable. Thanks to the begin/end block, we can wrap entire expressions into a comprehension! This is something that is pretty awesome and, at least in this manor, unique to Julia. There is a great boost to performance, but I am hesitant to say that comprehensions should always be used because of the inherit difference between a comprehension and a loop. A comprehension applies a function across an iterable, whereas iteration literally allocates each value into memory and approaches things more tangibly. This means that awesome things like break or continue will not be available in your comprehension, and generally, just writing things in a comprehension could be more difficult. I have also found that while CPU process times decrease when using comprehensions, memory usage typically increases, albeit slightly.</p><p id="22e3">Thank you so much for reading this article! I hope this cool little overview on comprehensions was fun to read. Do you think all Julians should just replace for loops with comprehensions at all times, given that Julia does allow it and it provides speed and syntactical benefits?</p></article></body>
Should We Just Stop Using For Loops In Julia?
Julia’s syntax is pretty dynamic, can we use this to avoid traditional iterative ‘for’ loops?
Introduction
One of the most obvious, yet difficult problems to solve in computing is how to translate human logic into that of a computational central processing units. Humans do not read bits and bytes very often in real life unless they are robo-cop or have a ridiculous amount of spare time. Instead, thanks to the legends in the Computer-Science field before us, we use modern interpreters and compilers that bring our data down to the processor without requiring any work with bits, bytes, registries, or even a faint glance at memory. Modern high-level languages have done an absolutely incredible job of driving the high-level, declarative landscape of today’s most popular programming languages, and of this group there is one rather new language that stands out, Julia.
For the uninitiated, Julia is a high-performance programming language released out of MIT around a decade ago. While the language is still young, it has already made some rather impressive feats — such as micro-benchmarks that often trail or C and Rust performance in many applications. Julia is also a member of the “ petaflop” club, which you can read more about in this overview on Reddit:
In a lot of ways, Julia has been effective of separating the software from the hardware. This has been done by remaining an easy-to-use, declarative, dynamically-typed language, but matching speeds of more traditional and statically-typed languages like C. The speed is only a very small portion of the reason I am such a big fan of the language, but speed is an important topic.
With any language, there are shortcomings and with every quantity of speed there becomes a new upper limit to what the software is capable. Thus, even in a language like Julia, where rarely you need to worry about performance unless you are actually software engineering or trying to optimize something.
For context on that last sentence, I am from the Scientific domain; where environments are often ‘ interactive.’
In the past, I have done a lot of comparisons between various aspects of this language and others, and one thing that I really enjoy is taking a simple idea that could potentially benefit or hinder Julia performance and investigating and sharing how exactly the performance is going to differ. A great example of this format of post is my view of the effects of Julia’s annotations on performance:
Today the performance focus is on looping and comprehensions. The question I wanted to pose today is if it could be possible to use comprehensions in place of most conventional for loops in Julia. Furthermore, if this is possible to do, then is it something we should do? Additionally, what kind of effect will this have on the performance?
First, we need to find out when we can and cannot replace a for loop with a comprehension. In most other languages that include this feature, a comprehension is not as capable as a for loop. As a result, comprehensions are generally reserved to do a lot less as far as actually calculating things. Comprehensions also always provide a return, so that is certainly a substantial difference between the two as well. If you wanted to get a “ return” from a for loop, you would really create a new vector outside of the loop and then add values to it on each iteration. Needless to say, that is far less efficient when compared to a comprehension.
As discussed prior, comprehensions are typically used for simple statements. Consider the following example:
x = Vector{Int64}()
for y in 1:10
push!(x, y)
end
This is certainly one that I would resign to a much more simple — perhaps more readable version using a comprehension.
x = [y for y in 1:10]
Needless to say, this can save a lot of time. Turning up the range on these loops, it also becomes quite obvious that the performance is also substantially better when using comprehensions:
x= Vector{Int64}()
@benchmark for y in 1:100000
push!(x, y)
end@benchmarkx= [y for y in 1:100000]
Considering all of the above, it certainly seems nice to be able to use comprehensions everywhere. The limitations of the typical programming language’s syntax in a comprehension is usually the largest downfall of the comprehension, but in Julia; things are a bit different. There is a lot more that you can do inside of a comprehension like this, additionally, the code can be incredibly readable. In most cases, this sort of code in something like Python would become an absolute mess.
x = [if y < 510 end for y in 1:10]
I am not going to go incredibly in-depth on this concept here, so if you would like to read more on the topic, I do have an entire article on them that might be helpful:
Okay, so we have witnessed some basic examples of comprehensions being flexible. Now it is time to take things to a new level by creating a crazy loop, then comparing the same loop in a comprehension. The loop I am going to create for this demonstration is going to be pretty simple, the loop will simply randomly jostle an array around, switching elements and providing a return of each number’s switch. The number of switches will be changed using an argument. To start the function, we initialize our return and then begin our loop.
function jostle(x::Vector{Int64}, count::Integer = 2000000)
jostles = Vector{Pair{Int64, Vector{Int64}}}()
for shift in1:count
Inside of the loop, we simply grab two random indexes and then flip them before pushing this data into jostles.
for shift in 1:count
switch1,switch2= rand(1:length(x)), rand(1:length(x))
if switch1==switch2switch2= rand(1:length(x))
end
n1, n2=x[switch1],x[switch2]
x[switch1] = n2x[switch2] = n1
push!(jostles, shift => [n1, n2])
end
Finally, we return jostles , completing our method:
function jostle(x::Vector{Int64}, count::Integer =2000000)
jostles = Vector{Pair{Int64, Vector{Int64}}}()
for shift in 1:count
switch1,switch2= rand(1:length(x)), rand(1:length(x))
if switch1==switch2switch2= rand(1:length(x))
end
n1, n2=x[switch1],x[switch2]
x[switch1] = n2x[switch2] = n1
push!(jostles, shift => [n1, n2])
end
jostles
end
Writing this into a comprehension is incredibly straightforward.
function compjostle(x::Vector{Int64}, count::Integer =2000000)
[beginswitch1,switch2= rand(1:length(x)), rand(1:length(x))
if switch1==switch2switch2= rand(1:length(x))
end
n1, n2=x[switch1],x[switch2]
x[switch1] = n2x[switch2] = n1
shift => [n1, n2]
end for shift in 1:count]
end
Not only is that incredibly short, it is also really readable and could be interpreted as subjectively better in terms of syntax by some. Here is the comparison!
x= [5,1,5,6,7,1,4,5]
@benchmark y = jostle(x)
@benchmark y = compjostle(x)
To Conclude
Comprehensions provide a very big and very bold boost to performance in a multitude of entirely different programming languages. Needless to say, this is probably a great one to use in your toolbox. That being said, this could be an easy one to accidentally overuse. Then the question becomes what constitutes overuse? The conventional explanation for why one should not use comprehensions all of the time comes in the form of keeping code looking pretty. I think this can be a great choice, because it is better to have something that performs slightly worse that people can actually work on.
However, with Julia the comprehension syntax is a lot more dynamic, capable, and of course; readable. Thanks to the begin/end block, we can wrap entire expressions into a comprehension! This is something that is pretty awesome and, at least in this manor, unique to Julia. There is a great boost to performance, but I am hesitant to say that comprehensions should always be used because of the inherit difference between a comprehension and a loop. A comprehension applies a function across an iterable, whereas iteration literally allocates each value into memory and approaches things more tangibly. This means that awesome things like break or continue will not be available in your comprehension, and generally, just writing things in a comprehension could be more difficult. I have also found that while CPU process times decrease when using comprehensions, memory usage typically increases, albeit slightly.
Thank you so much for reading this article! I hope this cool little overview on comprehensions was fun to read. Do you think all Julians should just replace for loops with comprehensions at all times, given that Julia does allow it and it provides speed and syntactical benefits?