Reminiscing on my academic career, there are certainly things I did right; however, there are certainly things I did wrong. Aside from the rights and the wrongs, there are things I had wished I had done sooner. Today, I hope to shed some light on some of the these things.
For context, as of a month ago I am an alumni of Missouri University of Science and Technology, and engineering college in Missouri. It has equipped me very well in mathematics[1], physics[2], and computer science[3]. I wrote roughly 159,141 lines of code in college, so I feel as if I got my money’s worth. Thanks to Missouri S&T, I have a fairly diverse background in many things.
Below is advice I would recommend to anyone currently studying computer science (or programming in general).
Get A Computer You’ll Love
I got a 13-inch MacBook Pro my freshmen year of college, and it was the best decision I made in my academic career. At the beginning of my senior year, I got a 15-inch MacBook Pro with an LG UltraFine 5K Display, it was the second best decision I made in my academic career.
Getting a great computer is not just to speed up compile times; it’s to make programming more enjoyable. During a typical programming day, I’ll be on my computer 8-12 hours. 33%-50% of my my time that day will be on a computer. Reducing friction 33%-50% of the day is almost invaluable.
Although a MacBook was perfect for me, it might not necessarily be right for anyone else. Find a computer you love, you won’t regret it.
Learn LaTeX
LaTeX was probably my most used programming tool in my academic career. It was a Swiss Army knife for any form of documents I might need:
- Notes
- Presentations
- Assignments
- Papers
- Documentation
Some samples of my LaTeX work can be found under my project page.
Learn A Different Language
At Missouri S&T, C++ is the first (and during my time there, the only) language one learned. The problem with this? One language, whether it be the can-all that C++ is, is not always appropriate for the job. There were times where Python was a much better choice. Sometimes it was C. Sometimes it was Swift. It all depends on the project. My recommendation is if you learned Python, learn C++. If you learned C++, learn Python. From there, find languages that will be useful for your domain.
Learn Your Text Editor
I didn’t pick up Vim until I was a junior; this was a giant mistake on my part. Vim didn’t just make it easier to delete a particular line of code; Vim made it easier to open files quickly, explore codebases faster, modify text quicker, etc.
Vim is not for everyone. Vim might not be right for you. Find a good text editor, and learn everything you can about it. It’ll make the friction of actually writing code much less.
Take Higher-Level Classes Early
Nearly all Computer Science programs require one to take higher-level classes as electives; in our school, it was roughly five graduate level classes. I made the mistake of taking all five my last year.
Emphasis on mistake.
Take them early; chances are you won’t be bogged down with job finding, apartment hunting, or the likes. Taking 18 hours, with three very difficult classes, in one term is not something I would wish on anyone.
Find Interests In Computer Science
When you find topics that interest you in Computer Science, chances are you will like your major more. I found a particular interests in AI related courses, but it is not for everyone. Computer Science has a breadth of interesting topics; check to see what your school has to offer, like:
- Security
- AI
- Data Mining and Big Data
- Machine Learning
- Databases
- UI/UX Design
Find Interests Outside Computer Science
Burnout is a serious issue in the industry. It can be a serious issue inside school as well. Foster passions outside of the classroom. Maybe join a design team, fraternity, or a sports team. There will be plenty of time at work to learn new things.
In addition, take genuinely interesting elective (not the easy ones). I learned I had an interest in Physics, and I carry that with me everyday.
Intern As Much As Possible
Internships are not only a gentle introduction into the industry, but a great way to determine what job you will want outside of school. Internships are often joked as “A 12-week interview processes”, and this can be true. If you find a good company that you can do good work for, it’s a great way to secure a job upon graduation.
Have Fun
The most important lesson I learned in college was to have fun (in moderation). College is all about learning who you are, what you want out of life, how to interact with others, and how to simply have fun. Lessons you learn in college will get carried over after graduating. Why not use this time to make life-long friends, create great memories, and know who you really are?
In Closing
This guide is not comprehensive; but it’s something I wish I read when I was starting. There’s always room to grow, so figure out what works for you. Do what makes you happy.
If there’s anything I am missing, feel free to ask.
Ever wondered how sorting algorithms looked in Swift? I did too — so I implemented some. Below you can find the following:
- Bubble Sort
- Selection Sort
- Insertion Sort
- Merge Sort
- Quicksort
As a preface: some of the algorithms use a self-implemented swap function — it is as follows:
extension Array {
mutating func swap(i: Int, _ j: Int) {
(self[i], self[j]) = (self[j], self[i])
}
}
Bubble Sort
func bubbleSort(var unsorted: Array<Int>) -> Array<Int> {
for i in 0..<unsorted.count - 1 {
for j in 0..<unsorted.count - i - 1 {
if unsorted[j] > unsorted[j + 1] {
unsorted.swap(j, j + 1)
}
}
}
return unsorted
}
Selection Sort
func selectionSort(var unsorted: Array<Int>) -> Array<Int> {
for i in 0..<unsorted.count - 1 {
var minimumIndex = i
for j in i + 1 ..< unsorted.count {
if unsorted[j] < unsorted[minimumIndex] {
minimumIndex = j
}
unsorted.swap(minimumIndex, i)
}
}
return unsorted
}
Insertion Sort
func insertionSort(var unsorted: Array<Int>) -> Array<Int> {
for j in 1...unsorted.count - 1 {
let key = unsorted[j];
var i = j - 1
while i >= 0 && unsorted[i] > key {
unsorted[i + 1] = unsorted[i]
i--
}
unsorted[i + 1] = key
}
return unsorted
}
Merge Sort
func merge(leftArray left: Array<Int>, rightArray right: Array<Int>) -> Array<Int> {
var result = [Int]()
var leftIndex = 0, rightIndex = 0
// Calculated variable to determine if the indexes have reached end of their respective arrays
var indexesHaveReachedEndOfArrays: Bool { return leftIndex < left.count && rightIndex < right.count }
while indexesHaveReachedEndOfArrays {
if left[leftIndex] < right[rightIndex] {
result.append(left[leftIndex])
leftIndex++
}
else if left[leftIndex] > right[rightIndex] {
result.append(right[rightIndex])
rightIndex++
}
else {
result.append(left[leftIndex])
result.append(right[rightIndex])
leftIndex++
rightIndex++
}
}
result += Array(left[leftIndex..<left.count])
result += Array(right[rightIndex..<right.count])
return result
}
func mergesort(unsorted: Array<Int>) -> Array<Int> {
let size = unsorted.count
if size < 2 { return unsorted }
let split = Int(size / 2)
let left = Array(unsorted[0 ..< split])
let right = Array(unsorted[split ..< size])
let sorted = merge(leftArray: mergesort(left), rightArray: mergesort(right))
return sorted
}
Quick Sort
func quicksort(var unsorted: Array<Int>) -> Array<Int> {
quicksort(&unsorted, firstIndex: 0, lastIndex: unsorted.count - 1)
return unsorted
}
private func quicksort(inout unsorted: Array<Int>, firstIndex: Int, lastIndex: Int) -> Array<Int> {
if firstIndex < lastIndex {
let split = partition(&unsorted, firstIndex: firstIndex, lastIndex: lastIndex)
quicksort(&unsorted, firstIndex: firstIndex, lastIndex: split - 1)
quicksort(&unsorted, firstIndex: split + 1, lastIndex: lastIndex)
}
return unsorted
}
private func partition(inout unsorted: Array<Int>, firstIndex: Int, lastIndex: Int) -> Int {
let pivot = unsorted[lastIndex]
var wall = firstIndex
for i in firstIndex ... lastIndex - 1 {
if unsorted[i] <= pivot {
unsorted.swap(wall, i)
wall++
}
}
unsorted.swap(wall, lastIndex)
return wall
}
As I go throughout my undergraduate studies, I have gotten really good at digging deeper until I completely understand a subject; however, when I observed around me, I realized that I am in the minority. Often times, students only desired to know how to get from question to answer without any intermediate thinking. When trying to hypothesize an answer, I cannot think of a definitive one. However, I know one possible contribution: encapsulation[1].
The most famous example I came across is the derivative of any trigonometric function besides $\sin x$ or $\cos x$. As any student who’s taken Calculus can tell you, $$\frac{d}{dx} \left(\tan x\right) = \sec^2 x $$
However, most students couldn't tell you:
$$\begin{align*}
\frac{d}{dx} \left(\tan x\right) &= \frac{d}{dx} \left(\frac{\sin x}{\cos x}\right) \\
&= \frac{\cos x \cos x + \sin x \sin x}{\cos^2 x} \\
&= \frac{\cos^2 x + \sin^2 x}{\cos^2 x} \\
&= \frac{1}{\cos^2 x} \\
&= \sec^2 x
\end{align*}$$
At first glance, it's hardly useful information. As a matter of fact you might think of it as piece of trivia — until you see the amount of people that miss it on a quiz or test. And entry-level Calculus is not the only place this is relevant; physics, thermodynamics, mathematics of any kind, computer science, and the like.
A question you might be thinking, why does it matter? If it's worth a couple of points on a test, is it that harmful? Maybe not, until you realize you conceptually do not understand the subject — only mechanically.
The next time you're learning something, ask yourself: "why?". The outcome might delight you.
- The act of enclosing in a capsule; the growth of a membrane around (any part) so as to enclose it in a capsule. ↩︎
It’s not so often I come across a problem that works out so beautifully yet requires so many different aspects in competitive programming. One particular problem, courtesy of Kattis, really did impress me.
The problem was House of Cards, and the problem was this (skip for TL;DR at bottom):
Brian and Susan are old friends, and they always dare each other to do reckless things. Recently Brian had the audacity to take the bottom right exit out of their annual maze race, instead of the usual top left one. In order to trump this, Susan needs to think big. She will build a house of cards so big that, should it topple over, the entire country would be buried by cards. It’s going to be huge!
The house will have a triangular shape. The illustration to the right shows a house of height 6 and Figure 1 shows a schematic figure of a house of height 5.

For aesthetic reasons, the cards used to build the tower should feature each of the four suits (clubs, diamonds, hearts, spades) equally often. Depending on the height of the tower, this may or may not be possible. Given a lower bound $h_0$ on the height of the tower, what is the smallest possible height $h \geq h_0$ such that it is possible to build the tower?
TL;DR: Using Figure 1 as a reference, you are given a lower bound on a height for a tower of cards. However, there must be an equal distribution of all four suites; clubs, diamonds, hearts, and spades.
This implies that the number of cards have to be divisible by $4$. Seeing as the input was huge $1 \leq h_0 \leq 10^{1000}$, there was no brute forcing this. So, first thought: turn this into a closed-form series, and solve the series.
Getting the values for the first five heights, I got the following set:
$${2, 7, 15, 25, 40, \ldots}$$
I was able to turn this set into a series quite easily:
$$\sum _{n = 1} ^{h_0} \left(3n - 1\right)$$
This turned into the following equation:
$$\frac{1}{2} h_0(3,h_0 + 1)$$
So, all I had to do was plug $h_0$ in the equation, and increment while the number was not divisible by $4$. Then, I realized how large the input really was. The input size ($1*10^{1000}$) was orders of magnitudes larger than typical, large data types would allow ($1.84 * 10^{19}$).
I realized this couldn’t be tested against a intensive data set, because there is only one number to calculate. I thought, since the series always subtracts one, the minimum times I must increment should roughly be four. Keeping this in mind, I decided to use Python. Python can work with arbitrarily large numbers, making it ideal in this situation.
I sat down, hoped for the best, and wrote the following code.
def getNumberOfCards(x):
return (3*pow(x, 2) + x) // 2
height = int(input())
while getNumberOfCards(height) % 4 != 0:
height += 1
print(height)
It worked.
I avoid the Mac App store as much as possible. It’s buggy. It’s slow. Apps are getting pulled from it left and right. But, I could not avoid it permanently because of software updates. As macOS Sierra 10.5 rolled out, I clicked on the App Store, followed by updates, and...
I could not get the updates page opened. Killed App Store. Restart computer. Everything.
After doing some research, I discovered there is a way to update software from the terminal: softwareupdate
. So, after running one command (sudo softwareupdate -iv
), I am writing this for the latest version of macOS Sierra.
After using a fairly large, matured language for a reasonable period of time, find peculiarities in the language or the libraries is guaranteed to happen. However, given it’s history, I have to say C++ definitely allows for some of the strangest peculiarities in it’s syntax. Below I list three that are my favorite.
Ternaries Returning lvalues
You might be familiar with ternaries as condition ? do something : do something else
, and they become quite useful in comparison to the standard if-else. However, if you’ve dealt with ternaries a lot, you might have noticed that ternaries also return lvalues/rvalues. Now, as the name suggests suggests, you can assign to lvalues (lvalues are often referred to as locator values). So something like so is possible:
std::string x = "foo", y = "bar";
std::cout << "Before Ternary! ";
// prints x: foo, y: bar
std::cout << "x: " << x << ", y: " << y << "\n";
// Use the lvalue from ternary for assignment
(1 == 1 ? x : y) = "I changed";
(1 != 1 ? x : y) = "I also changed";
std::cout << "After Ternary! ";
// prints x: I changed, y: I also changed
std::cout << "x: " << x << ", y: " << y << "\n";
Although it makes sense, it’s really daunting; I can attest to never seeing it in the wild.
Commutative Bracket Operator
An interesting fact about C++ bracket operator, it’s simply pointer arithmetic. Writing array[42]()
is actually the same as writing *(array + 42)
, and thinking in terms of x86/64 assembly, this makes sense! It’s simply an indexed addressing mode, a base (the beginning location of array) followed by an offset (42). If this doesn’t make sense, that’s okay. We will discuss the implications without any need for assembly programming.
So we can do something like *(array + 42)
, which is interesting; but we can do better. We know addition to be commutative, so wouldn’t saying *(42 + array)
be the same? Indeed it is, and by transitivity, array[42]()
is exactly the same as 42[array]()
. The following is a more concrete example.
std::string array[50];
42[array] = "answer";
// prints 42 is the answer
std::cout << "42 is the " << array[42] << ".";
Zero Width Space Identifiers
This one has the least to say, and could cause the most damage. The C++ standard allows for hidden white space in identifiers (i.e. variable names, method/property names, class names, etc.). So this makes the following possible.
int number = 1;
int number = 2;
int number = 3;
std::cout << number << std::endl; // prints 1
std::cout << number << std::endl; // prints 2
std::cout << number << std::endl; // prints 3
Using \u
as a proxy for hidden whitespace character, the above code can be re-written as such:
int n\uumber = 1;
int nu\umber = 2;
int num\uber = 3;
std::cout << n\uumber << std::endl; // prints 1
std::cout << nu\umber << std::endl; // prints 2
std::cout << num\uber << std::endl; // prints 3
So if you’re feeling like watching the world burn, this would be the way to go.
One of the reasons I kept CodeRunner handy was its ability to quickly compile code. With a single click of a button, I could run any of my frequently used languages. It didn’t matter if it was an interpreted or compiled language, so I could virtually run anything, like:
- C/C
- Python
- Lua
- LaTeX
- Perl
However, in the last few months I started using Vim. Heavily. So much so I was trying to use Vim command in the CodeRunner buffers. So I decided I wanted to have the functionality, and in vim-esque fashion, I mapped to my leader key: <leader>r
. The mnemonic <leader>r
un helped me remember the command on the first few tries.
To get the functionality, just add the following to your .vimrc
.
function! MakeIfAvailable()
if filereadable("./makefile")
make
elseif (&filetype == "cpp")
execute("!clang++ -std=c++14" + bufname("%"))
execute("!./a.out")
elseif (&filetype == "c")
execute("!clang -std=c11" + bufname("%"))
execute("!./a.out")
elseif (&filetype == "tex")
execute("!xelatex" + bufname("%"))
execute("!open" + expand(%:r))
endif
endfunction
augroup spaces
autocmd!
autocmd FileType c nnoremap <leader>r :call MakeIfAvailable()<cr>
autocmd FileType cpp nnoremap <leader>r :call MakeIfAvailable()<cr>
autocmd FileType tex nnoremap <leader>r :call MakeIfAvailable()<cr>
autocmd FileType python nnoremap <leader>r :exec '!python' shellescape(@%, 1)<cr>
autocmd FileType perl nnoremap <leader>r :exec '!perl' shellescape(@%, 1)<cr>
autocmd FileType sh nnoremap <leader>r :exec '!bash' shellescape(@%, 1)<cr>
autocmd FileType swift nnoremap <leader>r :exec '!swift' shellescape(@%, 1)<cr>
nnoremap <leader>R :!<Up><CR>
augroup END
One of the most frustrating things to deal with is the trailing space. Trailing spaces are such a pain because
- They can screw up string literals
- They can break expectations in a text editor (i.e. jumping to a new line or the end of the line)
- They can actually break programming languages
- They are just unflattering
However, in Vim, it takes one autocmd
to alleviate this.
augroup spaces
autocmd!
autocmd BufWritePre \* %s/s+$//e
augroup END
On every buffer save substitute spaces at the end of the line with nothing. Easy!