Learning Programming Concepts by Jumping in at the Deep End
There are many different ways to learn something; one that I enjoy from time to time is what I’d call the “jump in at the deep end”: Look at the solution to a problem and study each of its details until you understand all the concepts.
This is in contrast to a tutorial-style learning where you learn one thing after another and gradually build more complex things. I like the “deep end” approach because it’s a very personal learning style. It often gives me a way to discover interesting aspects of things myself, try to find out how parts work together, and play around with the material until I think I’ve got how it works. And while the road to understanding might be full of little frustrations and missing knowledge, I find it a very rewarding exercise. (Or maybe I’m just one of those people who like to geek out about neat concepts.)
Recently, I came across this task: Count the individual words in a given text. The solution I found seemed like a good “deep end” problem to me. It’s short and yet full of different concepts.
Here’s what I’d write (in Rust):
use std::collections::HashMap;
fn count_words(text: &str) -> HashMap<&str, usize> {
text.split(' ').fold(
HashMap::new(),
|mut map, word| { *map.entry(word).or_insert(0) += 1; map }
)
}
It’s just 8 lines! (You can play with the code here.)
And here are the concepts you should study to fully understand what’s going on (in no particular order; I tried to add a lot of links):
- Functions:
fn count_words - Modules and imports:
use std::… - Strings and characters:
&strand' 'are guaranteed to be UTF-8 - Slices: views into/references to memory and, in Rust, their lifetimes: the
&strs in the hash map are slices of the inputtext - Iterators:
splitreturns a type that implementsIteratorfold(a.k.a.reduce): A core part of functional programming (more information)Iterator: a trait
- Map data type:
HashMap- Hashing
- Generic data types:
Split<'a, P>,HashMap<K, V>
- Rust’s entry API: A view into a map with the ability to transform/add items
- closures:
|x, y| { … } - Implicit
return, last expression in a block gets returned:{ …; map }returnsmap - Mutable vs. immutable variable bindings:
mut map - References, pointers, dereferencing:
*thingyand&thingy - Integer sizes:
usize - Built-in operators:
+=
Note that most of these concepts are not special to Rust. I’ve just written this in Rust because it’s the language I currently use the most in my free time, and also because it’s a language that exposes you to a lot of interesting concepts.
Just for comparison, here’s the same in JavaScript (play with it on JSBin):
function count_words(text) {
return text.split(' ')
.reduce((map, word) => {
map[word] = map[word] ? map[word] + 1 : 1;
return map;
}, {});
}