Given a vector `vec`

of `n`

natural numbers (each number is in the range `[1..n]`

), we need to return "missing" natural numbers that do not appear in `vec`

.

This puzzle is similar to Find the missing natural number in a vector puzzle where we had to find just 1 missing number (looking ahead, that would lead to a completely different implementation).

Input `vec` | Output |

`[1, 1, 3, 4, 5]` | `[2]` |

`[6, 3, 3, 2, 1, 1]` | `[4, 5]` |

`[1, 1]` | `[2]` |

`[1, 2, 3, 4, 5]` | `[]` |

`[1]` | `[]` |

`fn find_missing_numbers_in_vector(vec: &mut Vec<i32>) -> Vec<i32> { for i in 0..vec.len() { let num = vec[i]; let corr_index = (num.abs() - 1) as usize; vec[corr_index] = -1 * vec[corr_index].abs(); } vec.iter() .enumerate() .filter_map(|(i, &num)| if num > 0 { Some((i + 1) as i32) } else { None }) .collect()}`

We can solve the puzzle without using extra space using the negation trick. We know that the input vector `vec`

could contain only numbers in the range `[1..n]`

(where `n`

is the length of the input vector). It means that we can iterate over the vector and for each element `num = vec[i]`

find a corresponding index `corr_index = num.abs() - 1`

and negate the number at that index. By doing so, we effectively mark an element as present by negating another element at the corresponding index.

After this exercise, all positive numbers (not marked by the negation) would represent the missing numbers: for a positive number `num`

at the index `i`

, the corresponding "missing" number would be equal to `i + 1`

.

The time complexity of this solution is `O(n)`

(where `n`

is the size of the input vector). The axiliary space complexity is `O(1)`

.

]]>

Given a vector of non-negative integer numbers `vec`

, arrange the numbers in a way that they form the largest possible number and return its string representation.

Input `vec` | Output |

`[40, 45, 4, 5, 8]` | `"8545440"` |

`[0, 1, 2, 3]` | `"3210"` |

`[10, 5]` | `"510"` |

`[1]` | `"1"` |

`[0, 0]` | `"0"` |

`fn form_largest_number(vec: &mut Vec<u32>) -> String { vec.sort_by(|a, b| { (b.to_string() + a.to_string().as_str()).cmp(&(a.to_string() + b.to_string().as_str())) }); if vec[0] == 0 { String::from("0") } else { vec.iter() .fold(String::from(""), |acc, x| acc + x.to_string().as_str()) }}`

The first idea that could come to mind is just to convert the input vector of numbers `vec`

to the vector of strings and sort it alphabetically in descending order. It is *almost* what we need but not exactly - in some cases it would produce an incorrect result. For this example (`vec = [40, 45, 4, 5, 8]`

), this alphabetical sorting would put `40`

in front of `4`

, which would give us `"404"`

instead of the greater `"440"`

number.

Thus, to resolve the problem for numbers with the same leading digits, we could provide `sort_by`

with a comparator function that compares 2 strings in 2 different orders: `b + a`

and `a + b`

. In our example, `a = 40, b = 4; b + a = "440"; a + b = "404"`

. It means that `"440".cmp("404")`

call would return `Ordering::Greater`

, which means that `b = 4`

would be put *before* `a = 40`

in the ordering. And that is exactly what we need.

After the vector is ordered, we just need to concatenate numbers converted to strings. Also, we handle the special case when the vector contains only zeros - in that case, we return `"0"`

as the result.

The time complexity of the solution is `O(n*log n)`

worst case (where `n`

is the size of the input vector), because that is the time complexity of the `sort_by`

implementation in rust.

The auxiliary space complexity is `O(1)`

(actually, it depends on the `sort_by`

implementation, which might allocate some extra space, but we assume that for small vectors a non-allocating sorting algorithm is used).

]]>

Given two positive integer numbers `a`

and `b`

, we need to find their *common factors*. **A common factor** of `a`

and `b`

is an integer number that divides both `a`

and `b`

without a remainder.

Input `a` | Input `b` | Output |

`18` | `6` | `[1, 2, 3, 6]` |

`5` | `10` | `[1, 5]` |

`3` | `5` | `[1]` |

`252` | `105` | `[1, 3, 7, 21]` |

`fn find_common_factors(a: u32, b: u32) -> Vec<u32> { assert!(a > 0 && b > 0, "a & b must be positive"); fn gcd(mut a: u32, mut b: u32) -> u32 { while a != b { if a > b { a = a - b; } else { b = b - a; } } a } (1..=gcd(a, b)) .filter(|n| a % n == 0 && b % n == 0) .collect()}`

We iterate over `[1..gcd(a, b)]`

numbers and `filter`

in only numbers `n`

that divide both `a`

and `b`

without a remainder (`a % n == 0 && b % n == 0`

). It is enough to iterate up to the Greatest common divisor returned by the `gcd(a, b)`

function call.

And to calculate the `gcd`

we use the simple Euclidean algorithm.

The time complexity of the solution is `O(gcd(a, b))`

. The auxiliary space complexity is `O(1)`

.

]]>

Given a non-negative integer number `n`

, we need to find *the pivot number*. **The pivot number** `p`

is a number such as the sum of all numbers `[0..p]`

equals the sum of all numbers `[p..n]`

.

Input `n` | Output | Explanation |

`1` | `1` | |

`4` | `None` | No pivot |

`5` | `None` | No pivot |

`8` | `6` | `sum[0..6] = sum[6..8] = 21` |

`49` | `35` | `sum[0..35] = sum[35..49] = 630` |

`fn find_pivot_number(n: u32) -> Option<u32> { let sum: u32 = n * (n + 1) / 2; let mut left_sum = 0; (0..=n).find_map(|e| { let right_sum = sum - left_sum; left_sum += e; if right_sum == left_sum { Some(e) } else { None } })}`

`fn find_pivot_number(n: u32) -> Option<u32> { let sum = n * (n + 1) / 2; let p = (sum as f32).sqrt() as u32; (p * p == sum).then(|| p)}`

As per the Arithmetic progression formula, we know that the sum of the first `n`

integer numbers could be calculated as: `n * (n + 1) / 2`

. Thus, we can calculate `sum`

without iterating over the numbers both in **Solution 1** and **Solution 2**.

Then in **Solution 1,** we could iterate over numbers `[0..n]`

maintaining the current `left_sum`

. And since we know the overall `sum`

of the numbers, we could easily figure out the `right_sum = sum - left_sum`

. If `left_sum`

and `right_sum`

match, then we have found the pivot. **Solution 1** is OK but it has `O(n)`

time complexity, which could be improved...

Surprisingly (or not), we could solve the puzzle more efficiently and concisely just by using the `sqrt`

function in **Solution 2**. It is easy to prove that `sqrt`

would give us exactly what we need. We know that the sum of all numbers `sum[0..n] = n * (n + 1) / 2`

. Then `sum[0..p] = p * (p + 1) / 2`

and `sum[p..n] = sum[0..n] - (p * (p + 1) / 2) + p`

. We need to find a number such as `sum[0..p] = sum[p..n]`

, i.e. `p * (p + 1) / 2 = sum[0..n] - (p * (p + 1) / 2) + p`

. If we simplify this equation, that would give us `sum[0..n] = p * p`

. Which means `p = sqrt(sum[0..n])`

.

**Solution 1**: The time complexity of the solution is `O(n)`

(where `n`

is the input number).

**Solution 2**: The time complexity of the solution is `O(1)`

.

The auxiliary space complexity is `O(1)`

for both solutions.

]]>

Given a provided ASCII string `str`

, we need to find the first unique (non-repeating) character.

Input `str` | Output |

`"yesterday"` | `'s'` |

`"ifeelfine"` | `'l'` |

`"ob-la-di,ob-la-da"` | `'i'` |

`"help!"` | `'h'` |

`"help!help!"` | `None` |

`fn find_first_unique_char(str: &str) -> Option<char> { let map = str.chars().fold(HashMap::new(), |mut map, ch| { let count = map.entry(ch).or_insert(0u8); *count += 1; map }); str.chars().find(|c| map.get(c) == Some(&1))}`

One simple way to solve the puzzle is to use the auxiliary `HashMap`

and to iterate the input string characters twice. When we iterate it for the first time using `fold`

, we count how many times each character appears in the string, saving counts to the `map`

. When we iterate the string for the second time using `find`

, we just return the first character (wrapped in `Some`

) appearing in the string exactly once. The function `find`

is short-circuiting, so it will stop processing as soon as the first character is found, or otherwise, it will return `None`

.

The time complexity of the solution is `O(n)`

(where `n`

is the size of the input string).

The auxiliary space complexity is `O(1)`

even though we used the auxiliary `HashMap`

. That is because a possible set of different characters stored in the map is limited (128 in the case of ASCII characters). It means that there is no linear dependency between the size of the input string and the size of the map.

]]>

Given a provided integer number `n`

, we need to check whether this number is *Ugly*. ** Ugly numbers** are a sequence of numbers whose only prime factors are

`2`

, `3`

or `5`

. In other words, these numbers are divisible `2`

, `3`

or `5`

prime numbers (but not by other prime numbers). The first few ugly numbers are `1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18..`

. By convention, `1`

is also included.Input `n` | Output | Explanation |

`10` | `true` | Prime factors: `2, 5` |

`11` | `false` | Prime factors: `11` |

`12` | `true` | Prime factors: `2, 3` |

`13` | `false` | Prime factors: `13` |

`14` | `false` | Prime factors: `2, 7` |

`15` | `true` | Prime factors: `3, 5` |

`fn is_ugly_number(mut n: i32) -> bool { while n > 1 { match n { _ if n % 2 == 0 => n /= 2, _ if n % 3 == 0 => n /= 3, _ if n % 5 == 0 => n /= 5, _ => return false, } } return n == 1;}`

One simple way to solve the puzzle is to iteratively divide the input number `n`

by `2`

as long as it is divisible (`n % 2 == 0`

). If the current number `n`

is not divisible by `2`

, then we try to divide it by `3`

and `5`

. If the current number `n`

that is not divisible by any of `2, 3, 5`

, then we could conclude that the number is not ugly (`return false`

). Otherwise, we continue `while n > 1`

. In the end, the remaining number `n`

should be equal to `1`

for the input number to be deemed Ugly.

The time complexity of the solution is `O(log n)`

(where `n`

is the input integer number). The auxiliary space complexity is `O(1)`

.

]]>

Given 2 strings `s1`

and `s2`

that contain binary numbers, we need to produce another string containing the sum of these numbers.

Input `s1` | Input `s2` | Output |

`10000` | `1` | `10001` |

`1` | `111` | `1000` |

`111` | `111` | `1110` |

`1011` | `1010` | `10101` |

`fn add_binary_strings(s1: &str, s2: &str) -> String { let (long_str, short_str) = if s1.len() > s2.len() { (s1, s2) } else { (s2, s1) }; let mut short_iter = short_str.chars().rev(); let mut carry = 0; let mut res: String = long_str .chars() .rev() .map(|lchar| { let (char, car) = match (lchar, short_iter.next(), carry) { ('0', Some('0'), 0) => ('0', 0), ('0', Some('0'), 1) => ('1', 0), ('0', Some('1'), 0) => ('1', 0), ('0', Some('1'), 1) => ('0', 1), ('1', Some('0'), 0) => ('1', 0), ('1', Some('0'), 1) => ('0', 1), ('1', Some('1'), 0) => ('0', 1), ('1', Some('1'), 1) => ('1', 1), ('0', None, 0) => ('0', 0), ('1', None, 0) => ('1', 0), ('0', None, 1) => ('1', 0), ('1', None, 1) => ('0', 1), _ => panic!("incorrect input"), }; carry = car; char }) .collect(); if carry == 1 { res.push('1'); } res.chars().rev().collect()}`

We iterate over the (*reversed*) longer input string `long_str`

, also moving the iterator over the (*reversed*) shorter input string along the way (`short_iter.next()`

). Also, we maintain the mutable variable `carry`

that keeps a digit (could contain only `0`

or `1`

value) that is transferred from one column of digits to another column of more significant digits (see: Carry in Wikipedia).

And then on each iteration, we just need to process all possible cases, which Rust's pattern matching helps to make very visual.

For example, `('0', Some('0'), 0) => ('0', 0)`

means that we need to sum `'0'`

and `'0'`

, and there is *no* carried `1`

digit from the previous column. As a result, we produce `'0'`

and also indicate that there is *no* `1`

to carry to another column.

For example, `('1', Some('1'), 0) => ('0', 1)`

means that we need to sum `'1'`

and `'1'`

, and there is *no* carried `1`

digit from the previous column. As a result, we produce `'0'`

and also indicate that there *is* `1`

to carry to another column.

For example, `('0', None, 0) => ('0', 0)`

means that there is *no* digit (`None`

) for the shorter string at this point already (in case the input strings have different lengths). As a result, we just return `'0'`

from the longer string and also indicate that there is *no* `1`

to carry to another column.

At the end of the function, we add the rightmost `'1'`

, in case we need to carry one more digit after completing the string traversal (`carry == 1`

), and then reverse the resulting string to produce the final result.

The time complexity of the solution is `O(n)`

(where `n`

is the maximum length of the input strings). The auxiliary space complexity is also `O(n)`

to store the result string.

]]>

For a provided vector of numbers `vec`

we need to find the *peak* element. The peak element is an element that is greater or equal to its neighbors. If there are multiple peak elements in the vector, we can return any of them as the solution.

Input `vec` | Output |

`[7, 8, 9, 1, 4, 5]` | `9` |

`[7, 8, 9, 11, 14]` | `14` |

`[9, 7, 5, 4, 2, 1]` | `9` |

`[1, 2]` | `2` |

`[2, 1]` | `2` |

`[1, 1]` | `1` |

`[1]` | `1` |

`fn find_peak_number(vec: &Vec<i32>) -> i32 { fn find_peak(v: &Vec<i32>, left: usize, right: usize) -> i32 { match (left + right) / 2 { // if mid elem is greater than left and right neighbors, then we found peak elem: m if (m == 0 || v[m] >= v[m - 1]) && (m == v.len() - 1 || v[m] >= v[m + 1]) => v[m], // if elem to the left of mid is greater than mid, then search in the left sub-vec: m if m != 0 && v[m - 1] > v[m] => find_peak(v, left, m - 1), // otherwise, search in the right sub-vec: m => find_peak(v, m + 1, right), } } assert!(!vec.is_empty(), "the input vector must not be empty"); find_peak(vec, 0, vec.len() - 1)}`

One simple way to solve the puzzle is to use the idea inspired by the Binary search algorithm. Recursively, we find a middle index `m = (left + right) / 2`

and then there are 3 possible cases to process:

If the element at the middle index

`v[m]`

is greater than its neighbors (taking into account that there might*not*be a neighbor to the left or the right of`m`

, if`m`

is the first or the last element), then we found the peak element.If the element to the left of

`m`

is greater than`v[m]`

, then we recursively search in the left sub-vector:`find_peak(v, left, m - 1)`

.Otherwise, we recursively search in the right sub-vector:

`find_peak(v, m + 1, right)`

.

The time complexity of the solution is `O(log n)`

(where `n`

is the size of the input vector). The auxiliary space complexity is also `O(log n)`

because we chose to have the recursive binary search implementation, which adds up stack space on every recursive call.

]]>

For a provided vector of numbers `vec`

we need to check if it contains any duplicates.

Input `vec` | Output |

`[1, 2, 3, 4, 2]` | `true` |

`[1, 1]` | `true` |

`[1, 2, 3, 4]` | `false` |

`[1]` | `false` |

`fn contains_duplicate_1(vec: &mut Vec<i32>) -> bool { vec.sort(); (1..vec.len()).any(|i| if vec[i] == vec[i - 1] { true } else { false })}`

`fn contains_duplicate_2(vec: &Vec<i32>) -> bool { let mut set = HashSet::new(); vec.iter().any(|v| { if set.contains(v) { true } else { set.insert(v); false } })}`

If we want to solve the puzzle with constant space `O(1)`

(but with `O(n*log n)`

time) without using additional data structures, then we could use **Solution 1**. The idea here is just to sort the vector `vec`

in-place and then iterate over the vector from the 1st index and check if `vec[i] == vec[i - 1]`

. If that is the case, then we have a duplicate and we return `true`

. We use the function `any`

which is short-circuiting, so it would stop processing and return `true`

as long as the first `true`

is discovered (in other words, when it has found the first duplicate).

**Solution 2** could be used if we want to "exchange space for time" i.e. we could have a linear time complexity `O(n)`

(but with `O(n)`

axillary space). For that, we use an auxiliary `HashSet`

to store numbers we have seen already. If we encounter a number `v`

which is already in the `set`

, then we return `true`

. Otherwise, we insert the number `v`

to the `set`

and return `false`

. The function `any`

would return `true`

as soon as the first `true`

is discovered.

**Solution 1:** `O(n*log n)`

time, `O(1)`

auxiliary space*\*.*

***Actually, `sort`

implementation in rust is adaptive - it does allocate some temporary auxiliary storage, but for short slices a non-allocating insertion sort is used instead. Here we assume that the non-allocating implementation is used.

**Solution 2**: `O(n)`

time, `O(n)`

auxiliary space for `HashSet`

.

]]>

For 2 provided strings `s1`

and `s2`

we need to check if these strings are isomorphic. Two strings `s1`

and `s2`

are isomorphic if it is possible to derive the string `s2`

from the string `s1`

by mapping some of the `s1`

characters to other characters. And similarly, another way around, `s1`

should be derivable from `s2`

.

Input `s1` | Input `s2` | Output | Explanation |

`abcdae` | `zbcdzy` | `true` | If we replace `a -> z` and `e -> y` we can get `s2` from `s1` . And vice versa, if we replace `z -> a` and `y -> e` we can get `s1` from `s2` . |

`cat` | `dog` | `true` | if we replace `c -> d` , `a -> o` , `t -> g` we can get `s2` from `s1` . And vice versa. |

`madc` | `mama` | `false` | we could not derive `mama => madc` because the 2nd `m` could not be mapped to `d` (it is already mapped to itself `m -> m` ). |

`acc` | `abd` | `false` | we could not derive `acc => abd` because the 2nd `c` could not be mapped to `d` (it is already mapped to `b` ). |

`fn is_isomorphic_strings(s1: &str, s2: &str) -> bool { if s1.len() != s2.len() { return false; } let mut map1 = HashMap::new(); let mut map2 = HashMap::new(); s1.chars() .zip(s2.chars()) .all(|(c1, c2)| match (map1.get(&c1), map2.get(&c2)) { (Some(&c2_from_map1), _) if c2_from_map1 != c2 => false, (_, Some(&c1_from_map2)) if c1_from_map2 != c1 => false, _ => { map1.insert(c1, c2); map2.insert(c2, c1); true } })}`

One simple way to solve the puzzle is to use 2 auxiliary `HashMap`

s: `map1`

and `map2`

. We use `all`

function to iterate over the `zip`

ped strings `s1`

and `s2`

at the same time (string characters `c1`

and `c2`

are available for use on each iteration) until on any iteration we encounter `false`

result. On each iteration, we try to get `c1`

key from `map1`

and `c2`

key from `map2`

.

If we have found `c2_from_map1`

character (mapped to `c1`

key) saved to the `map1`

earlier and this character does not equal the character `c2`

that we currently process (`c2_from_map1 != c2`

), it means that the character `c1`

had already been mapped to another `c2`

character previously. In this case, we could return `false`

concluding that the strings `s1`

and `s2`

are not isomorphic. A similar logic applies to the opposite case, where we check if the character `c2`

has already been mapped to another character `c1`

.

Otherwise, we just insert `c1 -> c2`

to `map1`

and `c2 -> c1`

to `map2`

.

The time complexity of the solution is `O(n)`

(where `n`

is the length of the input strings).

The auxiliary space complexity is constant `O(1)`

even though we used `HashMap`

s to store characters. That is because the character set is fixed: though the input strings could be of any length, the set of characters they contain, which is stored in the maps, is limited. For ASCII characters, for example, it would not go beyond 128. In other words, the space needed to store characters in the map does not linearly depend on the length of the input strings.

]]>

For a provided non-negative integer number `n`

we need to check if this number is a power of two. A positive integer number `n`

is a power of `2`

if it could be represented as `n = 2 ^ m`

, where `m`

is some non-negative integer number.

Input `n` | Output | Explanation |

`0` | `false` | |

`1` | `true` | `n = 2^0 = 1` |

`2` | `true` | `n = 2^1 = 2` |

`3` | `false` | |

`4` | `true` | `n = 2^2 = 4` |

`5` | `false` | |

`6` | `false` | |

`7` | `false` | |

`8` | `true` | `n = 2^3 = 8` |

`fn power_of_2(n: u32) -> bool { (n != 0) && (n & n - 1 == 0)}`

The first naive solution for this puzzle that would often come to mind is just to divide the number `n`

by `2`

until it becomes `1`

and see if at some point the remainder of the division is not `0`

- in that case `n`

is not the power of `2`

:

` fn power_of_2(mut n: u32) -> bool { if n == 0 { return false; } while n > 1 { if n % 2 != 0 { return false; } n = n / 2 } return true;}`

The naive solution would have the logarithmic complexity `O(log n)`

. That is not bad but we could do much better just using the magic of numbers and bitwise operations.

The actual solution is based on the observation that the power of `2`

numbers `n`

in binary form look like that (`n = 1, 2, 4, 8, 16...`

): `1, 10, 100, 1000, 10000...`

. In other words, they always have a leftmost `1`

and then all zeros to the right. And if we substruct `n - 1`

from these numbers (`n - 1 = 0, 1, 3, 7, 15...`

), we would get in binary form: `0, 1, 11, 111, 1111...`

. In other words, the binary numbers would always (except for `0`

) have all `1`

s. It means that if we apply the bitwise AND operation to both of these numbers `(n & n - 1)`

we should always get `0`

as a result (the numbers would neutralize each other). Also, we process `n != 0`

specifically, because `0`

could not be a power of `2`

anyway.

The time complexity of the solution is constant `O(1)`

(where `n`

is the number we check for being a power of `2`

).

]]>

Given a non-negative number `n`

, we need to check if the number is a Prime number or not.

Input `n` | Output |

`0` | `false` |

`1` | `false` |

`2` | `true` |

`3` | `true` |

`4` | `false` |

`5` | `true` |

`6` | `false` |

`7` | `true` |

`8` | `false` |

`9` | `false` |

`10` | `false` |

`fn is_prime(n: u32) -> bool { match n { _ if n <= 1 => return false, // prime num has to be > 1 _ if n == 2 => return true, // 2 is prime num _ if n % 2 == 0 => return false, //other even nums could not be prime _ => (), } let sqrt_n = (n as f32).sqrt() as u32; // iterate over odd numbers i=3,5,7... while i <= sqrt(n) // if n is divisible by some i, then n is not prime num let mut i = 3; while i <= sqrt_n { if n % i == 0 { return false; } i += 2; } true}`

First, we do several immediate checks to figure out if the number `n`

is prime or not right away. As per the prime number definition, a prime number has to be `>1`

. Also, an even number (except for `2`

) could not be a prime number because it would be always divisible by `2`

.

Then we iterate over odd numbers only `i = 3, 5, 7...`

while `i <= sqrt(n)`

and check if `n`

is divisible by any `i`

number. If that is the case, then the number `n`

could not be prime. Note, that we could take a shortcut and iterate only up to `sqrt(n)`

instead of doing it up to `n`

. If the number is not prime, iterating till its square root should be enough to identify that, which saves us some execution time.

There are further ways how to optimize the performance of the Primality test. Those improvements take advantage of other observations of how prime numbers could be represented, which could make the computation even more efficient (e.g. less `i`

numbers could be checked when iterating up to `sqrt(n)`

). But we will leave out those further optimizations for this puzzle.

The time complexity of the solution is `O(sqrt(n))`

(where `n`

is the number we check for primality).

]]>

For a provided string `str`

we need to remove adjacent duplicate characters. It is acceptable for this puzzle to return a new string as a result instead of doing the actual in-place mutation.

Input `str` | Output |

`aaabccccddde` | `abcde` |

`abbbc` | `abc` |

`aaa` | `a` |

`fn remove_adjacent_duplicate_chars(str: &str) -> String { let mut prev_char: Option<char> = None; str.chars() .filter(|&char| { if Some(char) != prev_char { prev_char = Some(char); true } else { false } }) .collect()}`

A simple solution is to iterate once over the string characters and `filter`

in only chars not equal to the previously saved character `prev_char`

. For such a character we save it to `prev_char`

and filter it in (return `true`

), otherwise, we filter it out (return `false`

).

The time complexity of the solution is `O(n)`

(where `n`

is the length of the input string).

]]>

For a provided non-negative number `n`

calculate the `n`

-th Fibonacci number.

Input `n` | Output |

`0` | `0` |

`1` | `1` |

`2` | `1` |

`3` | `2` |

`4` | `3` |

`5` | `5` |

`6` | `8` |

`fn fibonacci_number(n: u32) -> u32 { if n <= 1 { return n; } (2..=n) .fold((0, 1), |(prev, curr), _| (curr, prev + curr)) .1}`

A conventional mathematically beautiful way to solve the puzzle is to use recursion:

`fn fibonacci_number(n: u32) -> u32 { if n <= 1 { return n; } fibonacci_number(n - 2) + fibonacci_number(n - 1)}`

This way however is very inefficient since it takes exponential time to continuously recalculate previous sums again and again.

Luckily, there is an easy way to replace the recursion with a simple iteration. In our implementation, we use `fold`

method on `(2..=n)`

range inclusive iterator to iterate and maintain the previous Fibonacci number `prev`

and the current one `curr`

. In the end, we return the current number, which is accessible from the result tuple's second field `.1`

.

The time complexity of the solution is `O(n)`

(where `n`

is the `n`

-th Fibonacci number we calculate).

]]>

Given 2 integer numbers, `b`

and a non-negative `n`

, we need to raise the number `b`

to the power of `n`

.

Input `b` | Input `n` | Output |

`2` | `4` | `16` |

`-2` | `3` | `-8` |

`-2` | `10` | `1024` |

`5` | `0` | `1` |

`fn pow(mut b: i32, mut n: u32) -> i32 { let mut pow: i32 = 1; while n > 0 { if (n % 2) == 1 { pow *= b; } n /= 2; b *= b; } pow}`

A naive one-liner solution could be just to iterate `n`

times and multiply `b`

:

`fn pow(b: i32, n: u32) -> i32 { (0..n).fold(1, |acc, _| acc * b)}`

It would work but would have the linear time complexity `O(n)`

which is not ideal. Instead, the Solution has the time complexity `O(log n)`

.

The idea of the Solution is to divide the number `n`

by `2`

and multiply `b`

to itself `while n > 0`

. This way instead of multiplying `b`

`n`

times one by one, we are taking an "exponential shortcut", so to speak. In other words, instead of doing e.g. `3*3*3*3*3*3*3*3`

like in the naive solution, we do `((3^2)^2)^2`

.

One tricky part here is that we need to add special handling for situations when `n`

becomes an odd number `(n % 2) == 1`

. In essence, that is needed to process cases when the current power of `n`

is not divisible by `2`

like in `3^5`

. In this case, we need an additional multiplication like `((3^2)^2)*3`

.

The time complexity of the solution is `O(log n)`

(where `n`

is the power we need to raise the number to).

]]>

In a provided vector `vec`

of distinct natural numbers `[1, 2, 3...]`

we need to find *one* missing number.

Input `vec` | Output |

`[1, 2, 3, 5, 6, 7]` | `4` |

`[2, 3]` | `1` |

`[1, 3]` | `2` |

`[2]` | `1` |

`fn find_missing_number_in_vector(vec: &Vec<i32>) -> i32 { let sum_vec: i32 = vec.iter().sum(); let n = (vec.len() + 1) as i32; let sum_nat = n * (n + 1) / 2; sum_nat - sum_vec}`

As per the puzzle definition, we know that *exactly one* number is missing in the input vector `vec`

. Also, we know that according to Arithmetic progression formulas, we could calculate the sum of `n`

first natural numbers using the formula: `1 + 2 + 3 + + n = n * (n + 1) / 2`

. It means that if we calculate the sum of all numbers `sum_vec`

provided in the vector `vec`

and then calculate the expected sum of natural numbers `sum_nat`

using the formula, then the difference between the latter and the former would give us the missing number.

The time complexity of this solution is `O(n)`

(where `n`

is the size of the input vector).

]]>

We need to check whether 2 provided strings `s1`

and `s2`

are Anagrams. An anagram is a word or phrase formed by rearranging the letters of a different word or phrase. For this puzzle we could assume that we work only with ASCII characters, there is no distinction between lower and upper case letters, and that we can ignore spaces in the strings.

Input `s1` | Input `s2` | Output |

`"New York Times"` | `"monkeys write"` | `true` |

`"McDonald's restaurants"` | `"Uncle Sam's standard rot"` | `true` |

`"coronavirus"` | `"carnivorous"` | `true` |

`"Daddy"` | `"Mummy"` | `false` |

`fn check_if_anagram_strings(s1: &str, s2: &str) -> bool { let mut map1 = HashMap::new(); let mut map2 = HashMap::new(); s1.chars() .filter(|&c| c != ' ') .zip(s2.chars().filter(|&c| c != ' ')) .for_each(|(char1, char2)| { let count1 = map1.entry(char1.to_ascii_lowercase()).or_insert(0); *count1 += 1; let count2 = map2.entry(char2.to_ascii_lowercase()).or_insert(0); *count2 += 1; }); map1 == map2}`

One simple way to solve the puzzle in linear time is to use 2 `HashMaps`

. Iterating over the 2 strings `s1`

and `s2`

at once (we took `s1.chars()`

iterator and `zip`

ped it with `s2.chars()`

iterator) we could count unique letters in each of the strings, saving counts to the respective maps `map1`

and `map2`

. In the end, if both maps are equal (contain the same letter-count key-value pairs), then it means that both strings contain the same letters an equal number of times. In other words, the strings are anagrams.

The time complexity of this solution is `O(n)`

(where `n`

is the size of the input strings). The auxiliary space complexity is `O(c)`

(where `c`

is the size of the alphabet).

]]>

We need to calculate the square root of a provided integer number `num`

without using built-in functions. The result should be rounded down in case it is not a whole number.

Input `num` | Output | Explanation |

`4` | `2` | |

`16` | `4` | |

`225` | `15` | |

`5` | `2` | `sqrt(5) = 2.23` rounded to `2` |

`fn sqrt(num: i32) -> i32 { assert!(num >= 0); let mut left = 0; let mut right = num; while left < right { let mid = (left + right + 1) / 2; if mid * mid > num { right = mid - 1; } else { left = mid; } } right}`

The first idea that comes to mind could be a simple brute force like:

`fn sqrt(num: i32) -> i32 { let mut sqrt = 1; while sqrt * sqrt <= num { sqrt += 1; } sqrt - 1}`

It would work but, obviously, in terms of run-time complexity, it would not be ideal. So, instead of iterating over *all* numbers, we could improve the solution using Binary Search. The idea is to divide the interval of numbers by `2`

to get the middle number `mid`

and then continue the search within either the left or the right half of the interval, repeating the exercise while `left < right`

.

The time complexity of this solution is `O(lg n)`

(where `n`

is the input number `num`

). The auxiliary space complexity is `O(1)`

.

]]>

We need to check if a provided string `str`

containing brackets `{}[]()`

is syntactically valid. It means that all brackets are complete and in the correct order. The string could not contain characters other than `{}[]()`

.

Input `str` | Output |

`{([[]])}` | `true` |

`[]{}()` | `true` |

`[]` | `true` |

`{([)}` | `false` |

`{]` | `false` |

`{` | `false` |

`fn has_string_valid_brackets(str: &str) -> bool { const OPEN_BRACKETS: &str = "{[("; const CLOSE_BRACKETS: &str = "}])"; let mut stack = Vec::new(); for char in str.chars() { if OPEN_BRACKETS.contains(|c| c == char) { stack.push(char) } else if CLOSE_BRACKETS.contains(|c| c == char) { let popped = stack.pop(); match (popped, char) { (Some('('), ')') | (Some('{'), '}') | (Some('['), ']') => continue, _ => return false, } } else { panic!("the input string must only contain brackets!") } } return stack.is_empty();}`

For a string to be syntactically valid, it needs to have brackets in the correct order and no brackets could be missing. In other words, every *opening* bracket should be timely followed by a *corresponding* *closing* bracket. If that is the case for all characters in the string - then the string is valid. The Stack data structure (represented by `Vec`

in our implementation) is ideal for this scenario.

While iterating over the string characters `char`

we check if this `char`

is an opening bracket. If yes - we `push`

it to the `stack`

. If it is a closing bracket then we `pop`

the last opening bracket (`popped`

) from the `stack`

. If the `popped`

opening bracket corresponds to the current closing bracket `char`

, then all is good and so far the string is valid. Otherwise, we `return false`

right away.

At the end of the function, the `stack`

should be empty for a valid string.

The time complexity of this solution is `O(n)`

(where `n`

is a number of the characters in the string). The auxiliary space complexity is `O(n)`

because we used the stack data structure to solve the puzzle.

]]>

In a provided vector of strings `vec`

we need to find the longest string prefix shared by all strings in the vector.

Input `vec` | Output |

`["abcd", "abcdef", "abc", "abcde"]` | `"abc"` |

`["a", "abc", "ab"]` | `"a"` |

`["def"]` | `"def"` |

`["abc", "defg", "hij"]` | `""` |

`fn find_longest_common_prefix_string<'a>(vec: &Vec<&'a str>) -> &'a str { fn common_prefix<'a>(s1: &'a str, s2: &'a str) -> &'a str { let prefix_end_index = s1 .chars() .zip(s2.chars()) .enumerate() .find(|(_, (char1, char2))| char1 != char2) .map(|(index, _)| index) .unwrap_or(usize::min(s1.len(), s2.len())); &s1[0..prefix_end_index] } if vec.is_empty() { return ""; } (1..vec.len()).fold(vec[0], |prx, i| common_prefix(prx, vec[i]))}`

One way to solve the puzzle is to go through the vector of strings `vec`

and on each iteration find the `common_prefix`

between the common prefix `prx`

saved so far and the current string `vec[i]`

. The accumulating function `fold`

is a good fit here: we provide it with the initial seed (which is the first string `vec[0]`

in the vector) and a closure calling `common_prefix`

function for the accumulator `prx`

and the current string `vec[i]`

. On the first iteration, `prx = common_prefix(vec[0], vec[1])`

which is the common prefix between the first and the second string. On the second iteration, `prx = common_prefix(prx, vec[2])`

which is the common prefix between the common prefix `prx`

from the previous iteration and the third string `vec[2]`

. And so on...

The `common_prefix`

function takes 2 string slices (`s1`

, `s2`

), then it gets the char iterator for `s1`

and `zip`

s it with the char iterator for `s2`

(so that we can iterate over the both strings at the same time until we reach the end for one of them). Then we `find`

the first index where `char1 != char2`

- that would be the end index of the common prefix.

The worst-case time complexity of this solution is `O(s)`

(where `s`

is the sum of all characters of all strings). The auxiliary space complexity is `O(1)`

.

]]>

We need to reverse a provider integer (`i32`

) number `num`

without converting it to a string or using axillary data structures. Also, we should take care of a possible `i32`

overflow when reversing the number - in that case, we should return `0`

.

Input `num` | Output | Explanation |

`1234` | `4321` | |

`120` | `21` | |

`1` | `1` | |

`0` | `0` | |

`-1` | `-1` | |

`-1234` | `-4321` | |

`1463847412` | `2147483641` | |

`1463847413` | `0` | The reversed number would be greater than `i32::MAX = 2147483647` |

`-1463847413` | `0` | The reversed number would be less than `i32::MIN = -2147483648` |

`fn reverse_number(mut num: i32) -> i32 { let mut num_reversed: i32 = 0; while num != 0 { if num_reversed.abs() > i32::MAX / 10 { return 0; } else { num_reversed = num_reversed * 10 + num % 10; num /= 10; } } num_reversed}`

This puzzle is similar to what we implemented in Check if a number is Palindrome puzzle. The idea is to *decompose* the original `num`

in the loop, digit by digit from the right, using the division and modulo operations, and at the same time *compose* a reversed number from these digits. The catch here is to avoid a possible `i32`

overflow in case we try to reverse some very large `i32`

number like `1463847413`

. This number itself could fit in `i32`

range (it is less than `i32::MAX = 2147483647`

) but the reversed number would not fit. In that case, we should return `0`

.

Let's consider the process step by step for `num = 1234`

.

On the 1st iteration,

`num_reversed = num_reversed * 10 + num % 10 = 0 * 10 + 1234 % 10 = 4`

. This is the last digit of`1234`

. Then we also update`num = num / 10 = 1234 / 10 = 123`

.On the 2nd iteration,

`num_reversed = num_reversed * 10 + num % 10 = 4 * 10 + 123 % 10 = 43`

. That is the last 2 reversed digits of`1234`

. Then we also update`num = num / 10 = 123 / 10 = 12`

.On the 3rd iteration,

`num_reversed = num_reversed * 10 + num % 10 = 43 * 10 + 12 % 10 = 432`

. That is the last 3 reversed digits of`1234`

. Then we also update`num = num / 10 = 12 / 10 = 1`

.On the 4th iteration,

`num_reversed = num_reversed * 10 + num % 10 = 432 * 10 + 1 % 10 = 4321`

. That is our full reversed number. Then we also update`num = num / 10 = 1 / 10 = 0`

. End of the`while`

loop.The reversed number

`4321`

is returned from the function.

To process a possible `i32`

overflow scenario, we also have `num_reversed.abs() > i32::MAX / 10`

check, which, if satisfied, would stop the flow and return `0`

from the function. Without this check `num_reversed * 10`

multiplication could go outside `i32::MAX`

(or `i32::MIN`

) range, so this condition checks beforehand if the overflow would happen or not.

]]>

We need to check whether a provided number `num`

is a *Palindrome*. A Palindrome number is a number that reads the same forward and backward. This puzzle does not allow the conversion of the number to a string or the use of other auxiliary data structures.

Input `num` | Output |

`212` | `true` |

`112211` | `true` |

`1` | `true` |

`123` | `false` |

`10` | `false` |

`-121` | `false` |

`fn is_palindrome(mut num: i32) -> bool { match num { n if n == 0 => return true, //0 is palindrome n if n < 0 => return false, //negative num is not n if n % 10 == 0 => return false, //num ending 0 is not _ => (), } //reversing the half of the num (reversing the whole number could //cause int overflow): let mut num_reversed = 0; while num > num_reversed { num_reversed = num_reversed * 10 + num % 10; num /= 10; } //if num originally had an even number of digits e.g. 1221 then //[num == num_reversed == 12]. //if num had an odd number of digits e.g. 12321 then //[num == 12, num_reversed = 123], the mid number (3) //could be ignored in this case. num == num_reversed || num == num_reversed / 10}`

The first idea that would usually come to mind about this puzzle is to convert the number to a string and then work with the string to see if it is a palindrome or not. But the conversion is not allowed by the puzzle requirement.

Conceptually, we could *decompose* the `num`

by applying the division and modulo operations. At the same time, we could *compose* the *reversed* number `num_reversed`

. If the `num`

is equal to the `num_reversed`

, then it is a palindrome. One setback with this approach is that, in theory, we could overflow `i32`

when reversing some ridiculously large value like `i32::MAX`

. To get around that, we could reverse only half of the number - and it still would be enough to determine if the number is a palindrome or not.

Let's consider the process for the input `num = 1221`

:

On the first iteration,

`num_reversed = num_reversed * 10 + num % 10 = 0 * 10 + 1221 % 10 = 1`

. That is the last digit of`1221`

. Then we update`num = num / 10 = 1221 / 10 = 122`

.On the second iteration,

`num_reversed = num_reversed * 10 + num % 10 = 1 * 10 + 122 % 10 = 12`

. That is the reversed last 2 digits of`1221`

. Then we update`num = num / 10 = 122 / 10 = 12`

. That is the end of the`while`

loop.The function returns

`true`

because`num == num_reversed == 12`

.

In the case of a number with an odd number of digits e.g. `12321`

, the last step would require an additional division of the `num_reversed`

by `10`

because we would have `num == 12`

and `num_reversed = 123`

. The division would help us to eliminate that last digit `3`

from the comparison - which is safe because the middle digit (`3`

is the middle digit of `12321`

number) is irrelevant to our process anyway.

The time complexity of this solution is `O(log n)`

(`n`

is the input number `num`

). The auxiliary space complexity is `O(1)`

.

]]>

In a provided vector `vec`

we need to find the *pivot index*. The pivot index is an index of the *pivot element*, the element before which all elements are less than the pivot and after which all elements are greater than the pivot.

*Input:* `vec = [5, 3, 4, 6, 2, 7, 10, 8]`

*Output:* `5`

*Explanation*: The element `7`

(with the index `5`

) is the pivot because all elements before `7`

are less and all elements after are greater.

*Input:* `vec = [3, 1, 12, 10, 23, 50, 25]`

*Output:* `4`

*Explanation*: The element `23`

(with the index `4`

) is the pivot because all elements before `23`

are less and all elements after are greater.

*Input:* `vec = [-4, 1, 25, 50, 8, 10, 23]`

*Output:* `0`

*Explanation*: The element `-4`

(with the index `0`

) is the pivot because all elements before `-4`

are less (the empty sub-vector is assumed to always satisfy) and all elements after are greater.

*Input:* `vec = [3, 2, 1]`

*Output:* `None`

*Explanation*: There is no any pivot element in the vector.

`fn find_pivot_index_in_vector(vec: &Vec<i32>) -> Option<usize> { let mut map = HashMap::new(); //first (reversed) traversal: for each index save to the map current min value (0..vec.len()).rev().fold(i32::MAX, |min, i| { map.insert(i, min); if vec[i] < min { vec[i] } else { min } }); //second traversal: find the pivot index let mut max_left = i32::MIN; (0..vec.len()).find_map(|i| { let min_right = map[&i]; let found = if vec[i] > max_left && vec[i] < min_right { Some(i) } else { None }; if vec[i] > max_left { max_left = vec[i] } found })}`

To solve the puzzle in linear time we use an auxiliary data structure: `HashMap`

.

First, we iterate over the vector `vec`

in the reversed order to find a current minimum element to the right of every index `i`

, and save the index `i`

and the corresponding minimum value to the `map`

. We use `fold`

for that because it allows us to maintain a current `min`

value while we are traversing the vector.

On the second traversal, we go through the vector `vec`

again, but this time from left to right. While doing the traversal, we maintain the current maximum value `max_left`

to the left of every index `i`

. Also we extract a corresponding `min_right`

value (the minimum value to the right of the index `i`

). If the current element `vec[i]`

is greater than the maximum element on the left and less than the minimum element on the right, it means that we have found the pivot element. In that case, we wrap its index in `Some`

, otherwise we return `None`

for that iteration. And since we use `find_map`

function, it would find and unwrap from `Some`

the first pivot we have found, and would return `None`

in case there is no any pivot element.

The time complexity of this solution is `O(n)`

(`n`

is a size of the input vector), even though we had to traverse the vector twice. The auxiliary space complexity is `O(n)`

because we used the additional `HashMap`

data structure to solve the puzzle.

]]>

In a provided vector `vec`

we need to find the minimum index of a repeating element.

*Input:* `vec = [6, 7, 4, 5, 4, 7, 5]`

*Output:* `1`

*Explanation*: There are several repeating elements in the vector but the element `7`

has the lowest index `1`

.

*Input:* `vec = [1, 2, 5, 3, 4, 7, 3, 5, 8, 9]`

*Output:* `2`

*Explanation*: There are several repeating elements in the vector but the element `5`

has the lowest index `2`

.

*Input:* `vec = [1, 2, 3, 4, 5, 6]`

*Output:* `None`

*Explanation*: There are no repeating elements in the vector.

`fn find_min_index_of_repeating_element_in_vector(vec: &Vec<i32>) -> Option<usize> { let mut set = HashSet::new(); (0..vec.len()) .rev() .flat_map(|i| { let found = if set.contains(&vec[i]) { Some(i) } else { None }; set.insert(vec[i]); found }) .last()}`

To solve the puzzle in linear time we can use an auxiliary data structure: `HashSet`

. We go backwards over the *reversed* iterator of the vector indices `i`

. When we encounter an element `vec[i]`

that is already contained in the `set`

(which means the element is repeating), we wrap the index `i`

of that element in `Some`

, otherwise we return `None`

for that iteration. Also, on each iteration we insert the current element `vec[i]`

to the `set`

. And since we use `flat_map`

adapter, it would automatically unwrap found indices from `Some`

and would discard `None`

results. All transformations up to `last`

are *lazy* and would not trigger any traversal. But the `last`

call would trigger the resulting iterator and would traverse it until the index of the last repeating element from the resulting sequence is obtained. That would be the lowest index of the repeating element wrapped in `Some`

, or `None`

if there are no repeating elements.

The time complexity of this solution is `O(n)`

(`n`

is a size of the input vector). The auxiliary space complexity is `O(n)`

because we used the additional `HashSet`

data structure to solve the puzzle.

]]>

We need to partition a provided vector `vec`

into 2 slices, each having the same sum of the elements.

*Input:* `vec = [7, -5, -4, 2, 4]`

*Output:* `([7, -5], [-4, 2, 4])`

*Explanation*: Each of the 2 partitions has the equal sum of the elements `2`

.

*Input:* `vec = [7, -6, 3, -5, 1]`

*Output:* `([], [7, -6, 3, -5, 1])`

*Explanation*: Each of the 2 partitions has the equal sum of the elements `0`

(the empty partition is assumed to have `0`

sum).

*Input:* `vec = [1, 3, 5, 7]`

*Output:* `None`

*Explanation*: This vector could not be partitioned (there are no slices with an equal sum).

`fn partition_vector_into_2_slices_with_equal_sum(vec: &Vec<i32>) -> Option<(&[i32], &[i32])> { let sum_all = vec.iter().fold(0, |sum, x| sum + x); let mut sum_left = 0; (0..vec.len()).find_map(|i| { let sum_right = sum_all - sum_left; let found = if sum_left == sum_right { Some((&vec[0..i], &vec[i..vec.len()])) } else { None }; sum_left += vec[i]; found })}`

To solve the puzzle in linear time and without using auxiliary data structures, we could first precalculate the sum of all vector elements `sum_all`

. And then when iterating over the vector `vec`

for the second time, we could maintain `sum_left`

of the elements we have seen so far, and on each iteration check if the current `sum_left`

is equal to `sum_right`

(we could figure out `sum_right`

because we know `sum_all`

and the current `sum_left`

). If that is the case, then we have found 2 partitions (slices) with the equal sum and we wrap them in `Some`

, otherwise we return `None`

for the current iteration. Note that we use `find_map`

function which would return the first `Some`

result (the first pair of found partitions with equal sum) and would ignore `None`

results.

The time complexity of this solution is `O(n)`

(`n`

is a size of the input vector), but we had to iterate over the vector `vec`

twice. The auxiliary space complexity is constant (`O(1)`

).

]]>

We need to sort a provided vector `vec`

using Quicksort algorithm. It is a commonly used divide and conquer sorting algorithm that on average has `O(n log n)`

time complexity.

*Input:* `vec = [-4, 1, 25, 50, 8, 10, 23]`

*Output:* `[-4, 1, 8, 10, 23, 25, 50]`

*Input:* `vec = [2, 1, 6, 10, 4, 1, 3, 9, 7]`

*Output:* `[1, 1, 2, 3, 4, 6, 7, 9, 10]`

`fn quick_sort(vec: &mut Vec<i32>) -> &Vec<i32> { fn partition(slice: &mut [i32]) -> usize { let pivot_idx = slice.len() - 1; let pivot = slice[pivot_idx]; let mut next_swap_idx = 0; (0..slice.len()).for_each(|idx| { if slice[idx] < pivot { slice.swap(next_swap_idx, idx); next_swap_idx += 1; } }); slice.swap(pivot_idx, next_swap_idx); next_swap_idx } fn qsort(slice: &mut [i32]) { let pivot_idx = partition(slice); let len = slice.len(); if pivot_idx > 0 { qsort(&mut slice[0..pivot_idx]); } if pivot_idx < len - 1 { qsort(&mut slice[pivot_idx + 1..len]); } } if !vec.is_empty() { qsort(&mut vec[..]); } vec}`

Usually *Quicksort* is implemented using 2 functions: a `partition`

function and a recursive `qsort`

function. The `qsort`

function initially takes the original full slice of data (`&mut vec[..]`

) based on the provided vector `vec`

. Then it uses `partition`

function call to *partition* the slice *in-place* around the selected *pivot* element and gets the pivot index as a result. The partitioning means that all elements less than the pivot are moved to the left of the pivot, and all elements greater than the pivot are moved to the right of the pivot. And then `qsort`

function calls itself recursively for those 2 parts (for the left part and for the right part) until resulting parts are empty.

Let's go through the process step-by-step using our sample input: `vec = [-4, 1, 25, 50, 8, 10, 23]`

.

When `qsort`

is called for the first time, it gets the whole slice `[-4, 1, 25, 50, 8, 10, 23]`

to work with. When we call `partition`

, the slice is re-partitioned to be `[-4, 1, 8, 10, 23, 50, 25]`

. The element `23`

was selected as the pivot (in our implementation we select the last element as a pivot, but in other implementations it could be e.g. the first element, the middle element, or a random one). The `partition`

function moved all elements less than `23`

to the left of the pivot and greater elements to the right of the pivot, and returned the pivot index `pivot_idx = 4`

.

Now `qsort`

function calls itself recursively for each of those 2 parts (left and right): `[-4, 1, 8, 10]`

and `[50, 25]`

.

- When the
`partition`

function is called for`[-4, 1, 8, 10]`

, it partitions the slice to be`[-4, 1, 8, 10]`

and returns`pivot_idx = 3`

(no changes in the slice because`10`

was selected as the pivot, and the elements less than the pivot were already left to the pivot).- The
`qsort`

is called for the left`[-4, 1, 8]`

part again (and it is not called for the right part because it is empty). When the`partition`

function is called for`[-4, 1, 8]`

, it partitions the slice to be`[-4, 1, 8]`

and returns`pivot_idx = 2`

(no changes in the slice because`8`

was selected as the pivot, and the elements less than the pivot were already left to the pivot).- The
`qsort`

is called for the left`[-4, 1]`

part again (and it is not called for the right part because it is empty). When the`partition`

function is called for`[-4, 1]`

, it partitions the slice to be`[-4, 1]`

and returns`pivot_idx = 1`

(no changes in the slice because`1`

was selected as the pivot, and the element less than the pivot was already left to the pivot).- The
`qsort`

is called for the left`[-4]`

part again (and not called for the right part because it is empty). When the`partition`

function is called for`[-4]`

, it obviously returns the same slice and`pivot_idx = 0`

. The`qsort`

would not be called again because all parts are exhausted.

- The

- The

- The
- When the
`partition`

function is called for`[50, 25]`

, it partitions the slice to be`[25, 50]`

and returns`pivot_idx = 0`

. The element`25`

was selected as the pivot and the other element was moved to the right of it because it was greater.- The
`qsort`

is called for the right`[50]`

part again (and is not called for the left part because it is empty). When the`partition`

function is called for`50`

, it obviously returns the same slice and`pivot_idx = 0`

. The`qsort`

would not be called again because all parts are exhausted.

- The

Thus, at the end of the run we end up having the ordered vector `[-4, 1, 8, 10, 23, 25, 50]`

.

The average time complexity of this solution is `O(n log n)`

(`n`

is a size of the input vector). The worst-case auxiliary space complexity is `O(n)`

to reflect possible `n`

nested recursive calls.

]]>

In a vector `vec`

we need to find all pairs of the elements with a provided difference `diff`

.

*Input:* `vec = [1, 5, 2, 2, 2, 5, 5, 4]`

, `diff = 3`

*Output:* `[(1, 4), (2, 5)]`

*Explanation*: `(1, 4)`

and `(2, 5)`

pairs have the difference of the elements `3`

.

*Input:* `vec = [1, 5, 6]`

, `diff = 2`

*Output:* `[]`

*Explanation*: There are no pairs with the difference of the elements `2`

.

`fn find_pairs_with_difference(vec: &Vec<i32>, diff: i32) -> Vec<(i32, i32)> { let mut set = HashSet::new(); vec.iter().for_each(|e| { set.insert(e); }); vec.iter() .flat_map(|&e| { let another = e + diff; if set.contains(&another) { set.remove(&another); Some((e, another)) } else { None } }) .collect()}`

One easy way to solve this puzzle in linear time is using an auxiliary `HashSet`

data structure. First, we iterate over the vector `vec`

and add all elements to the `set`

. And then we iterate over the vector `vec`

again and for each element `e`

check if a corresponding `another`

element (`e + diff`

) exists in the `set`

. If that is the case, then have found the counterpart for `e`

and we wrap the pair as `Some((e, another))`

. If not, then we return `None`

for the current element. Since we use `flat_map`

, our found pairs would be automatically unwrapped from `Some`

, while `None`

results would be discarded. Also note that when we have found a pair, we remove the corresponding element from the set `set.remove(&another)`

- that is a way to avoid having duplicated pairs in the result (which would happen if the vector `vec`

contains duplicates).

The time complexity of this solution is `O(n)`

(`n`

is a size of the input vector), even though we iterated over the vector `vec`

twice. The auxiliary space complexity is `O(n)`

since we used an additional `HashSet`

.

]]>

In a provided vector `vec`

we need to find a slice with the largest sum of the elements. This problem is commonly referred to as Maximum subarray problem.

*Input:* `vec = [-2, 1, -3, 4, -1, 2, 1, -5, 4]`

*Output:* `[4, -1, 2, 1]`

*Explanation*: The slice `[4, -1, 2, 1]`

has the largest sum of elements (`6`

).

*Input:* `vec = [-4, 1, 2, 3, -5]`

*Output:* `[1, 2, 3]`

*Explanation*: The slice `[1, 2, 3]`

has the largest sum of elements (`6`

).

*Input:* `vec = [-4, 1, -5]`

*Output:* `[1]`

*Explanation*: The slice `[1]`

has the largest sum of elements (`1`

).

*Input:* `vec = [-4, -3, -5]`

*Output:* `[-3]`

*Explanation*: The slice `[-3]`

has the largest sum of elements (`-3`

).

`fn find_max_sum_slice(vec: &Vec<i32>) -> &[i32] { if vec.is_empty() { return &[]; } let mut best_sum = vec[0]; let mut best_start_index = 0; let mut best_end_index = 0; let mut current_sum = vec[0]; let mut current_start_index = 0; (1..vec.len()).for_each(|i| { if current_sum <= 0 { //if the current sum is <= 0, we start tracking a new sequence from the current element current_start_index = i; current_sum = vec[i]; } else { //otherwise we continue the currently tracked sequence current_sum += vec[i]; } //then we update the best sum if the current sum is greater if current_sum >= best_sum { best_sum = current_sum; best_start_index = current_start_index; best_end_index = i; } }); &vec[best_start_index..best_end_index + 1]}`

The solution for this problem uses Kadane's algorithm. We iterate over the vector `vec`

searching for a sequence of elements that could potentially become the sequence with the largest sum. If `current_sum <= 0`

then we "reset" the currently tracked sequence and start tracking a new sequence starting from the current index (update `current_start_index`

to the current index `i`

and `current_sum`

to the current element `vec[i]`

), because having a negative `current_sum`

doesn't help the currently tracked sequence to become the largest anyway. But if `current_sum`

is positive then we continue tracking the current sequence (we don't update `current_start_index`

, but we add the current element `vec[i]`

to `current_sum`

). Then during the same iteration we check if `current_sum >= best_sum`

and update `best_sum`

we have found so far and its indices (`best_start_index`

, `best_end_index`

).

The time complexity of this solution is `O(n)`

(`n`

is a size of the input vector). The auxiliary space complexity is constant (`O(1)`

).

]]>

In a provided vector `vec`

we need to find the majority element, if it is present. The *majority element* is the element that appears in the vector more than `n / 2`

times, where `n`

is a size of the vector `vec`

.

*Input:* `vec = [4, 8, 7, 4, 4, 5, 4, 3, 1, 4, 4]`

*Output:* `4`

*Explanation*: The majority element is `4`

because it appears in the vector `6`

times (more than `n / 2 = 11 / 2 = 5`

).

*Input:* `vec = [4, 8, 7, 4, 4, 5, 4, 3, 1, 4]`

*Output:* `None`

*Explanation*: There is no the majority element in the vector. Even though `4`

is the most common element, it is not the majority element, because it appears only `5`

times (not more than `n / 2 = 10 / 2 = 5`

times).

*Input:* `vec = [1]`

*Output:* `1`

*Input:* `vec = [1, 2]`

*Output:* `None`

`fn find_majority_element(vec: &Vec<i32>) -> Option<i32> { let mut m = 0; let mut i = 0; //first pass: vec.iter().for_each(|&x| { if i == 0 { m = x; i = 1; } else if m == x { i = i + 1; } else { i = i - 1; } }); //second pass: let count = vec .iter() .fold(0, |count, &x| if x == m { count + 1 } else { count }); //return Some(m) if actual majority, None otherwise: (count > vec.len() / 2).then(|| m)}`

The solution for this problem uses BoyerMoore majority vote algorithm:

`Initialize an element m and a counter i with i = 0For each element x of the input sequence: If i = 0, then assign m = x and i = 1 else if m = x, then assign i = i + 1 else assign i = i 1Return m`

Also we need the second pass over the vector `vec`

because, even when there is no the majority element, the algorithm would yield one of the elements as the result (`m`

). On the second pass, we calculate a number of times (`count`

) we encounter `m`

element to check if that is the actual majority (it appears more than `vec.len() / 2`

times). If that is the case - we found the majority element. If not - we return `None`

.

The time complexity of this solution is `O(n)`

(`n`

is a size of the input vector), but we need to iterate over the input vector twice. The auxiliary space complexity is constant (`O(1)`

).

]]>

In a provided vector `vec`

we need to move all `0`

elements to the end of the vector. A relative order of other elements should not change.

*Input:* `vec = [5, 0, 0, 2, 3, 0, 4, 0, 1]`

*Output:* `[5, 2, 3, 4, 1, 0, 0, 0, 0]`

*Input:* `vec = [0, 0, 8, 6, 0, 0]`

*Output:* `[8, 6, 0, 0, 0, 0]`

*Input:* `vec = [1, 2, 3]`

*Output:* `[1, 2, 3]`

`fn move_zeros_to_end(vec: &mut Vec<i32>) -> &Vec<i32> { let mut zero_count = 0; (0..vec.len()).for_each(|index| { if vec[index] == 0 { zero_count += 1; } else if zero_count > 0 { vec[index - zero_count] = vec[index]; vec[index] = 0; } }); return vec;}`

We can solve the puzzle in a single pass over the provided vector `vec`

. As we go through the vector `vec`

, we maintain `zero_count`

of `0`

elements we come across. At the same time, we shift non-zero elements to the left to `index - zero_count`

position, and fill their place with `0`

.

The time complexity of this solution is `O(n)`

(`n`

is a size of the input vector).

]]>

We need to find all equilibrium indices in the provided vector `vec`

. *An equilibrium index* in the vector `vec`

is an index `i`

where the sum of `vec[0..i-1]`

elements is equal to the sum of `vec[i+1..len]`

elements.

*Input:* `vec = [0, -3, 5, -4, -2, 3, 1, 0]`

*Output:* `[0, 3, 7]`

*Explanation*: `0`

index is in the result because the sum of the elements left to the index `0`

is considered to be `0`

, and the sum of the right elements is `0`

as well (`-3 + 5 + (-4) + (-2) + 3 + 1 + 0 = 0`

). `3`

index is in the result because `0 + (-3) + 5 = -2 + 3 + 1 + 0`

. `7`

index is in the result because `0 + (-3) + 5 + (-4) + (-2) + 3 + 1 + 0 = 0`

.

*Input:* `vec = [2, 3, 5, 1, 2, 2]`

*Output:* `[2]`

*Explanation*: `2`

index is in the result because `2 + 3 = 1 + 2 + 2`

.

*Input:* `vec = [1, 3, 5]`

*Output:* `[]`

*Explanation*: The result is empty because there are no equilibrium indices in the vector.

`fn find_equilibrium_indices_in_vector(vec: &Vec<i32>) -> Vec<usize> { let mut map: HashMap<usize, i32> = HashMap::new(); let mut right_sum: i32 = 0; (0..vec.len()).rev().for_each(|index| { map.insert(index, right_sum); right_sum = right_sum + vec[index]; }); let mut left_sum: i32 = 0; vec.iter() .enumerate() .filter(|(index, elem)| { let found_equilibrium: bool = left_sum == map[index]; left_sum = left_sum + *elem; found_equilibrium }) .map(|(index, _)| index) .collect()}`

On the first iteration over the *reversed* `vec`

iterator, we accumulate and save to the created `map`

sums of the *right* elements for the each `index`

value. On the second iteration, we accumulate sums of the *left* elements for the each `index`

value. At the same time we check if for the current `index`

our accumulated `left_sum`

is equal to the *right* sum extracted from the `map`

for this `index`

. If that is the case, then we have `found_equilibrium`

and the `filter`

's predicate would satisfy for that `index`

(which means that `index`

would be included in the result).

The time complexity of this solution is `O(n)`

(`n`

is a size of the input vector), but we need to iterate the vector twice. Also the solution requires `O(n)`

extra space for the `HashMap`

we created, which is not ideal and could be improved. It is possible to implement it without using the extra space (observant readers can suggest a solution in the comments!).

]]>

We need to sort a provided binary vector `vec`

(a vector that contains only `0`

or `1`

numbers) in linear time.

*Input:* `vec = [1, 0, 1, 0, 1, 0, 0, 1]`

*Output:* `[0, 0, 0, 0, 1, 1, 1, 1]`

*Input:* `vec = [0, 0, 1, 0, 1, 1, 0, 1, 0, 0]`

*Output:* `[0, 0, 0, 0, 0, 0, 1, 1, 1, 1]`

*Input:* `vec = [0, 0, 1, 1]`

*Output:* `[0, 0, 1, 1]`

`fn sort_binary_vector_in_linear_time(vec: &mut Vec<u8>) -> &Vec<u8> { let count_0 = vec.iter().fold(0, |acc, e| match e { 0 => acc + 1, 1 => acc, _ => panic!("The vector is not binary"), }); for i in 0..vec.len() { if i < count_0 { vec[i] = 0; } else { vec[i] = 1; } } return vec;}`

Since we know that the vector `vec`

is a binary vector as per the requirement (could contain only values `0`

and `1`

), we could avoid using more general sorting algorithms. Instead, we could just count a number of `0`

elements (or alternatively a number of `1`

) using a simple `fold`

operation. And then we iterate once again over the vector `vec`

to fill elements with the index *below* that `count_0`

with `0`

, and the rest of the elements with `1`

.

The time complexity of this solution is `O(n)`

(`n`

is a size of the input vector).

]]>

We need to find a maximum length slice (contiguous sub-vector) with a provided `sum`

of the elements in a given vector `vec`

.

*Input:* `vec = [5, 6, -5, 5, 3, 4, 1]`

, `sum = 7`

*Output:* `[-5, 5, 3, 4]`

*Explanation:* This is the longest slice (length = `4`

) that has a sum of the elements equals to `7`

.

*Input:* `vec = [5, 6, -5, 5, 3, 4, 1]`

, `sum = 5`

*Output:* `[4, 1]`

*Explanation:* This is the longest slice (length = `2`

) that has a sum of the elements equals to `5`

.

*Input:* `vec = [1, 2, 3]`

, `sum = 10`

*Output:* `None`

*Explanation:* There are no slices having a sum of the elements equals to `10`

.

`fn find_max_length_slice_with_given_sum(vec: &Vec<i32>, sum: i32) -> Option<&[i32]> { let mut map: HashMap<i32, usize> = HashMap::from([(0, 0)]); let mut max_length: usize = 0; let mut max_length_start_index: Option<usize> = None; let mut max_length_end_index: Option<usize> = None; let mut current_sum: i32 = 0; for (index, elem) in vec.iter().enumerate() { current_sum += *elem; if let Some(start_position) = map.get(&(current_sum - sum)) { let length: usize = index - start_position + 1; if length > max_length { max_length = length; max_length_start_index = Some(*start_position); max_length_end_index = Some(index); } } map.entry(current_sum).or_insert(index + 1); } match (max_length_start_index, max_length_end_index) { (Some(start), Some(end)) => Some(&vec[start..end + 1]), _ => None, }}`

The solution is similar to what we implemented in Rust Interview Puzzles: Check if a slice with 0 sum exists in a vector but slightly more complex. We iterate over the vector `vec`

and on each iteration update `current_sum`

of the elements (this `current_sum`

is later saved to the `map`

we created earlier, together with the current `index + 1`

value). Then we check if `current_sum - sum`

value has already been saved to the `map`

earlier. If it was, then it means that at some point *earlier* we had `current_sum`

which was equal to `current_sum`

value which we have *now* (minus the target value `sum`

provided to the function). In other words, the difference between the *earlier* `current_sum`

value and the `current_sum`

value that we have *now* is exactly `sum`

(which means we've found the slice with the given `sum`

as per the requirement). Then we just need to make sure the slice we found is the longest one, so we need to compare it with the current longest slice we have had so far. If `length`

we found is larger than the current `max_length`

, then we update the `max_length`

and also save the starting and the ending index of the current longest slice (`max_length_start_index`

, `max_length_end_index`

).

The time complexity of this solution is `O(n)`

(`n`

is a size of the input vector). Also the solution requires `O(n)`

extra space for the `HashMap`

we used.

]]>

We need to find a maximum product of 2 numbers in a provided vector of numbers `vec`

. In math, *a product of 2 numbers* is a result of multiplication of these 2 numbers.

*Input:* `vec = [-10, -3, 5, 7, -2]`

*Output:* `35`

*Explanation:* A product of `(5, 7)`

is the max product.

*Input:* `vec = [-10, -3, 5, 4, -2]`

*Output:* `30`

*Explanation:* A product of `(-10, -3)`

is the max product.

*Input:* `vec = [-5, 0, 10]`

*Output:* `0`

*Explanation:* A product of `(-5, 0)`

or `(0, 10)`

is the max product.

`fn find_max_product_of_2_numbers(vec: &Vec<i32>) -> i32 { let mut max_pos_1 = 0; let mut max_pos_2 = 0; let mut min_neg_1 = 0; let mut min_neg_2 = 0; for e in vec { if *e > 0 && *e > max_pos_1 { max_pos_2 = max_pos_1; max_pos_1 = *e; } else if *e > 0 && *e > max_pos_2 { max_pos_2 = *e; } if *e < 0 && *e < min_neg_1 { min_neg_2 = min_neg_1; min_neg_1 = *e; } else if *e < 0 && *e < min_neg_2 { min_neg_2 = *e; } } let product_of_max_positives = max_pos_1 * max_pos_2; let product_of_min_negatives = min_neg_1 * min_neg_2; match (product_of_max_positives, product_of_min_negatives) { (pos, neg) if pos != 0 && neg != 0 && pos >= neg => pos, //product of positives is larger (pos, neg) if pos != 0 && neg != 0 && pos <= neg => neg, //product of negatives is larger (pos, 0) if pos != 0 => pos, //has only a product of positives (0, neg) if neg != 0 => neg, //has only a product of negatives _ if max_pos_1 != 0 && min_neg_1 != 0 && vec.len() == 2 => max_pos_1 * min_neg_1, //has 1 pos & 1 neg number _ => 0, //otherwise returns 0 }}`

In general, a maximum product number would come from either multiplying 2 maximum positive numbers OR 2 minimum negative numbers. It means that we need to iterate once over the vector `vec`

to find these 4 numbers (`max_pos_1`

, `max_pos_2`

, `min_neg_1`

, `min_neg_2`

). Then we could find a product of 2 maximum positive numbers AND a product of 2 minimum negative numbers. A maximum value from these products would give us a maximum product overall.

Also we need to handle special cases when there are no positive (or negative) pairs in the vector `vec`

, or when the vector contains zeros.

The time complexity of this solution is `O(n)`

(`n`

is a size of the input vector).

]]>

We need to check if an unsorted vector of numbers `vec`

contains a *contiguous* sub-vector (slice) of `0`

sum.

*Input:* `vec = [4, 2, -3, 1, 6]`

*Output:* `true`

*Explanation:* Because it contains a contiguous sub-vector `[2, -3, 1]`

.

*Input:* `vec = [4, 2, 0, 1, 6]`

*Output:* `true`

*Explanation:* Because it contains `[0]`

.

*Input:* `vec = [-3, 2, 3, 1, 6]`

*Output:* `false`

*Explanation:* Though it contains a sub-vector `[-3, 3]`

, but it's not contiguous.

`fn check_if_slice_with_0_exists(vec: &Vec<i32>) -> bool { let mut set = HashSet::from([0]); let mut sum = 0; for e in vec { sum = sum + e; if set.contains(&sum) { return true; } else { set.insert(sum); } } return false;}`

We iterate over the vector `vec`

, accumulating `sum`

of the vector values `e`

along the way. And on each iteration we check if the current `sum`

of the elements is in the HashSet `set`

we created earlier. If the `sum`

is not yet in the `set`

, then we just add it to the `set`

. If it is in the `set`

, then we immediately return `true`

as a result. The idea here is that if we encounter again a `sum`

value that we've seen already before (the value that is already in the `set`

), it means that after we saw that `sum`

value for the first time, further elements added to the `sum`

negated that original `sum`

value, so that we encountered the same value again. That is only possible if the vector contains a contiguous sub-vector with `0`

sum.

The time complexity of this solution is `O(n)`

(`n`

is a size of the input vector). Also the solution requires `O(n)`

extra space for the `HashSet`

we created.

]]>

In an unsorted vector `vec`

of numbers we need to find any pair (`Tuple`

) with a provided `sum`

.

*Input:* `vec = [8, 7, 2, 5, 3, 1]`

, `sum = 10`

*Output:* `(2, 8)`

*Input:* `vec = [5, 2, 6, 8, 6, 9]`

, `sum = 12`

*Output:* `(6, 6)`

*Input:* `vec = [5, 2, 6, 8, 1, 9]`

, `sum = 12`

*Output:* `None`

`fn find_pair_with_given_sum(vec: &Vec<i32>, sum: i32) -> Option<(i32, i32)> { let mut set = HashSet::new(); for e in vec { let another = sum - e; match set.get(&another) { Some(_) => return Some((*e, another)), _ => { set.insert(e); } } } return None;}`

We iterate over elements in the vector `vec`

and for each element `e`

try to find `sum - e`

element in the created earlier HashSet `set`

. If we could not find `sum - e`

in the set, then we just add the element `e`

to the `set`

. If we've found `sum - e`

in the `set`

, then we immediately return `(e, sum - e)`

as the found pair. If we've iterated over the entire vector and didn't find a pair, then we just return `None`

.

The time complexity of this solution is `O(n)`

(`n`

is a size of the input vector). Also the solution requires `O(n)`

extra space for the `HashSet`

we created.

]]>

`Copy`

trait is used in Rust to indicate that a type implementing it has a Primitive times like `i32`

already implement `Copy`

:

`let a = 1;let b = a;`

When we assign `a`

to `b`

, essentially we bind the variable `b`

to a new *implicitly* created copy of `a`

allocated on the stack.

We can implement `Copy`

for our custom type. One prerequisite for that is that all constituents of our type should also implement `Copy`

:

`#[derive(Copy, Clone)]struct MyType { a: i32, b: i32,}`

The easiest way to implement it is to use `derive(Copy, Clone)`

attribute that will automatically generate `Copy`

trait implementation for our type (note that we also have to implement `Clone`

because it's a supertrait of `Copy`

). Since all constituents (`a`

, `b`

) implement `Copy`

, the parent type `MyType`

also could implement it.

Not every type could implement `Copy`

trait. For example, this would not compile:

`#[derive(Copy, Clone)]struct MyBadType { a: i32, b: String, // this field does not implement `Copy`}// compilation error: the trait `Copy` may not be implemented for this type`

`MyBadType`

could not implement `Copy`

because `String`

doesn't implement `Copy`

. `String`

type is one example of a type that could not implement `Copy`

safely, because of the way how strings are organized in memory:

`let s = String::from("abcdef");`

The string `abcdef`

(bound to the variable `s`

) consists of 2 parts: a small data structure allocated on the stack that points to the actual string content allocated on the heap. That is different from the primitive types like `i32`

which have *only* the stack part.

It means that for `String`

, a simple *shallow copying* of the stack part only would just lead to having several references to the same object residing on the heap. For example:

`let s1 = String::from("abcdef");let s2 = s1;`

`Copy`

semantics implies doing a simple stack-based copy of objects that could be *fully* allocated on the stack (e.g. objects of primitive types like `i32`

). But `String`

type doesn't comply with this semantics because copying only a stack part (*shallow copy*) would not copy the actual referenced object allocated on the heap. In the example above, 2 variables (`s1`

and `s2`

) now reference the same object `abcdef`

allocated on the heap.

Also it's not clear now which variable (`s1`

or `s2`

) would actually *own* the object allocated on the heap, and ultimately how Rust should release the memory to avoid doing it twice (first time when `s1`

goes out of scope and second time when `s2`

goes out)?

A solution for that in Rust is that a variable of a type that doesn't implement `Copy`

trait (i.e. a type that doesn't have a simple stack-based copy semantics) is rather **moved** when assigning it to another variable. *Moving* means that apart from biding that another variable to a shallow copy of the first variable, the first variable is *invalidated* (the compiler would *not* allow us to use it after the move):

`let s1 = String::from("abcdef");let s2 = s1;println!("{s2}");//println!("{s1}"); //compilation error: borrow of moved value: `s1``

The string `s1`

is moved to the variable `s2`

. In means that `s2`

is now bound to a shallow copy of `s1`

, and that `s2`

takes **ownership** of the actual string `abcdef`

stored on the heap. It's no longer possible to use `s1`

after the move. And since `s2`

is now the owner, only when `s2`

variable goes out of scope, the corresponding heap memory will be freed up (unless the ownership was moved to another variable before that).

Similar to a variable assignment, passing a value to a function or returning a value from a function would also transfer ownership. There is a way to avoid transferring ownership by using *references* and *borrowing* , which might be explored in the next article. Thanks for reading!

`str`

) and `String`

. Now we are going to explore how they work, how they are related and how they are different.`str`

)String slice (`str`

) is UTF-8 encoded byte array of a fixed length. The length of the array is fixed at run-time but *unknown* at compile time. And since the size is unknown for the compiler, it means that `str`

itself could not be stored on the stack directly and, for example, can't be used as a type for local variables. Hence we work with string slices via a reference `&str`

: a reference size is known at compile time and can be allocated on the stack.

So, intuitively, we should think of a string slice as a *reference* to some UTF-8 encoded byte array of a fixed length. Another analogy, is to think of it as a *view* into a block of memory. Practically, we can even think of it as a *read-only view* of that data, even though that is not exactly correct (`&mut str`

is also possible in some cases, though it's rather unusual).

And an underlying *referent* object could be allocated in the *static storage*, on the *heap* or on the *stack* . Let's consider each case separately:

`// [Example 1]:let s1: &str = "Hi";// [Example 2]:let u2: String = String::from("Hi");let s2: &str = &u2[..];// [Example 3]:let mut u3: String = String::from("Hi");let s3: &mut str = &mut u3[..];s3.make_ascii_lowercase();assert_eq!(s3, "hi");// [Example 4]:let u4: &[u8] = &[b'H', b'i'];let s4: &str = str::from_utf8(u4).unwrap();`

In **[Example 1]**, we have a string literal `Hi`

which is stored in the static storage, which basically means that it's hard-coded directly into the final binary. Obviously, it means that, by definition, it's immutable in this case.

In **[Example 2]**, `u2`

variable points to `String`

data allocated on the heap. And then we get an *immutable view* on that `String`

data (`&str`

).

In **[Example 3]**, we instead take a *mutable view* on the data located on the heap, and then we call a mutating method `make_ascii_lowercase`

. It's quite uncommon to work with `&mut str`

and there is a reason for that... Since a slice's length is fixed, mutating operations should not lead to any slice size changes. For instance, we should not be able to replace a 1-byte ASCII character with a 2-byte UTF-8 character because that would overflow our slice. Also, we should not be able to apply operations that would attempt to shrink our slice in any way. Both of these situations would violate safety guarantees - which is why only a limited number of *safe* mutating functions is available for string slices, like `make_ascii_lowercase`

in our example. This function operates only 1-byte ASCII characters and doesn't change a slice length in any way - so it's safe.

In **[Example 4]**, `s4`

string slice represents an immutable view on the byte array `u4`

allocated on the stack (arrays have a fixed size and thus they are allocated on the stack). This example demonstrates that it's possible to have a string slice view on some data on the stack, in case that data has a known size.

In the examples above, our string slices were referencing the *full* original allocated strings. But one of the main benefits of using slices is that we can take a view on the *part* of the string, without allocating a new string. For example:

`let s: String = String::from("Hi there");let s1: &str = &s[3..8];assert_eq!("there", s1);`

Here `s1`

variable is bound to a reference to `there`

portion of the original string.

Earlier we defined *String slice* (`str`

) as *UTF-8 encoded byte array of a fixed length*. In contrast to that, `String`

can be defined as **UTF-8 encoded vector of bytes of a dynamic length**. Since `String`

size could grow as needed, its size could *not* be known at compile time, which means the actual data is always allocated on the heap.

When we create a `String`

like the below:

`let s: String = String::from("Hi");let s1: String = s;let s2: &str = &s1[..];`

Variable `s`

is bound *not* to the actual string content `Hi`

(which is allocated on the heap), but rather to a small data structure allocated on the stack which contains a pointer to the actual string, together with information about the length/capacity of the string:

When we assign `s`

to another variable `s1`

, the actual string content `Hi`

is not copied, but instead the variable `s1`

is bound to a copy of the data structure (allocated on the stack) referencing the original string `Hi`

(allocated on the heap).

And when we create a string slice `s2`

, it also references the same `Hi`

string allocated on the heap.

Note that `s`

, `s1`

and `s2`

are not plain references. These can be thought of as special small data structures (sometimes called "fat pointers", though some people would argue this term is not correct in this specific case) that contain actual pointers, together with additional information about the string/slice size.

And `String`

values could grow, as needed:

`let mut s: String = String::from("Hi");s.push_str("!!!");assert_eq!(s, "Hi!!!");`

Adding another string at the end of the original string might require allocating new memory, if that would exceed a currently allocated *capacity* of the string (recall that `Capacity`

field in the diagram we saw earlier).

In this article we touched the surface of **strings** in Rust: explored *String slices* (`str`

) and `String`

types. It's a common belief that strings in Rust are more complex than in some other programming languages, but actually they could be rather more transparent, flexible and safe. Thanks for reading!

We are going to break down each below case and consider it separately, with examples:

*Immutable variable*bound to a value (`let t: T = v`

)*Mutable variable*bound to a value (`let mut t: T = v`

)*Immutable variable*bound to*Immutable reference*(`let t: &T = &v`

)*Immutable variable*bound to*Mutable reference*(`let t: &mut T = &mut v`

)*Mutable variable*bound to*Immutable reference*(`let mut t: &T = &v`

)*Mutable variable*bound to*Mutable reference*(`let mut t: &mut T = &mut v`

)

`let t: T = v`

)`// [Example 1]:let t: i32 = 1; t = 2; //compilation error: cannot assign twice to immutable variable `t1`// [Example 2]:struct A { a: i32}let t1: A = A {a:1}; t1 = A {a:2}; //compilation error: cannot assign twice to immutable variable `t`t1.a = 3; //compilation error: cannot assign to `t1.a`, as `t1` is not declared as mutable`

In the first example, a variable `t`

was declared as *immutable* (was not explicitly marked as mutable), so it was not possible to re-bind it to a new value `2`

. Similarly, in the second example, it was not possible to re-bind an immutable variable `t1`

to a new structure value `A {a: 2}`

. And also, since `t1`

was declared immutable, it was not possible to update a structure field `t1.a`

to a new value `3`

.

`let mut t: T = v`

)`// [Example 1]:let mut t: i32 = 1; t = 2; //compiles OK// [Example 2]:struct A { a: i32 }let mut t1: A = A {a:1}; t1 = A {a:2}; //compiles OKt1.a = 3; //compiles OK`

In the first example, a variable `t`

was declared as *mutable*, so it was possible to re-bind it to a new value `2`

. Similarly, in the second example, a mutable variable `t1`

was bound to a structure value `A {a:1}`

, and then it was possible to re-bind the variable to a new structure value `A {a:2}`

. Also, it was possible to mutate the field `t1.a`

*inside the structure* to a new value `3`

.

`let t: &T = &v`

)`let t: &i32 = &1; t = &2; //compilation error: cannot assign twice to immutable variable `t` *t = 2; //compilation error: cannot assign to `*t`, which is behind a `&` reference`

A variable `t`

was declared as *immutable*, so it was not possible to re-bind it to a new immutable reference `&2`

(a new immutable reference pointing to another value `2`

). Also it was not possible to update the actual *referent* value `*t`

via the reference `t`

, because the reference `t`

was itself defined as immutable `&i32`

.

`let t: &mut T = &mut v`

)`let t: &mut i32 = &mut 1; t = &mut 2; //compilation error: cannot assign twice to immutable variable `t` *t = 2; //compiles OK`

A variable `t`

was defined as *immutable* (even though it was bound to a mutable reference `&mut 1`

), so it was not possible to re-bind it to a new mutable reference `&mut 2`

(a new mutable reference pointing to another value `2`

). But it was OK to update the actual *referent* value `*t`

via the reference `t`

, because the reference `t`

was itself defined as mutable `&mut i32`

.

`let mut t: &T = &v`

)`let mut t: &i32 = &1; t = &2; //compiles OK *t = 2; //compilation error: cannot assign to `*t`, which is behind a `&` reference`

A variable `t`

was declared as *mutable*, so it was possible to re-bind it to a new immutable reference `&2`

(a new immutable reference pointing to another value `2`

). But it was not possible to update the actual *referent* value `*t`

via the reference `t`

, because the reference `t`

was itself defined as immutable `&i32`

.

`let mut t: &mut T = &mut v`

)`let mut t: &mut i32 = &mut 1; let new_t: &mut i32 = &mut 2; t = new_t; //compiles OK*t = 2; //compiles OK`

A variable `t`

was declared as *mutable*, so it was possible to re-bind it to a new mutable reference `&mut 2`

(a new mutable reference pointing to another value `2`

). In this case, we had to bind `&mut 2`

to another variable `new_t`

first, because assigning it directly to `t`

would cause a compilation error `temporary value is freed at the end of this statement`

. Also it was OK to update the actual *referent* value `*t`

via the reference `t`

, because the reference `t`

was itself defined as mutable `&mut i32`

.

Sometimes I don't understand how a *modern* language could be so *outdated*, as though deliberately ignoring many good things that have happened in software development over the last 30 years. And even more unfathomable is how some interesting features in the language could coexist with other awkward counterparts. From Go wikipedia page I know that Go design was a collective effort of 3 guys. I am thinking that maybe 1 of them was sabotaging the process undermining all good in the language with his old-fart ideas? That's a joke, of course. All 3 of them are great, I love them.

I understand the purpose of building a simple, easy-to-learn, easy-to-read language but being *simple* should not be a synonym for being *dumb*. When writing code in Go I sometimes find myself in situations when I feel, well... dumb, or rather that Go designers squeezed my balls forcing me to forget all good I have learned about software development up to now. I know that I should not compare Go with other languages. Go is Go, it's opinionated, it's unique, I should not complain and I should take it the way it is, like my grandma. And so I am focking trying.

Thanks god, generics were just added... By the way, I have no idea how Go developers lived without generics for 10 or so years and didn't blow their brains out. Luckily, I joined Go after generics were finally added, so I survived. I survived but sometimes I wish I hadn't, because the support for generics is so limited that I often want to cut my hand off, so I have an excuse for not writing another workaround in my code. One example, is that generic type parameters are not supported in methods. Here is a proposal on GitHub to finally add this support to Go. And you know what's funny? What's funny is comments in the proposal like "This will add so much complexity" etc. Come on... Having this feature is such a basic thing you would expect from generics, that not having it would initially cause a shock. How the hell was it implemented in other languages then? Right, I remember, I should not compare, Go is opinionated, unique, I should not complain...

Another shocking discovery waiting new-joiners with a garden pruner around the corner is a lack of support for function/method overloading feature (I doubt if I should even call a "feature" something you are just used to take for granted). There is a special section dedicated to that in the official FAQ defending this decision: "*Experience with other languages told us that having a variety of methods with the same name but different signatures was occasionally useful but that it could also be confusing and fragile in practice*". Well, "occasionally useful" are not the words you would nervously mutter when you have to name functions like `SumInt2`

, `SumInt3`

, `SumFloat2`

, `SumFloat3`

etc. for summing a different number of arguments of different types, instead of having just 1 overloaded `Sum`

. Probably it was the idea of that sabotaging guy...

Multiple return values... This one is just a weirdo. It's a unique feature, indeed. Most other languages don't have it, indeed. And that's for a reason - because it's silly. Why not to add a native support for Tuples (maybe just as a syntax sugar around structs), so that we can return 1 Tuple value e.g. with 2 constituents like `(a, b)`

? Tuples can be constructed and deconstructed, essentially behaving like multiple return values. And on top of that, Go developers would get another useful construction to work with, helpful in many other cases. Win-win? No, the whole new syntax was hard-coded into the compiler for no reason, instead of spending this energy on fixing other basic issues in the design.

Ok, I finished smoking my virtual joint (kids, never smoke a real one unless... your mam asks you to write too much idiomatic Go code - then smoking is the only way you could survive). Probably it's time to wrap up... But I still have so much to say, I just scratched the surface... Should I make it a series? Just kidding, I love Go, love you guys. Thanks for reading! By the way, I develop a library for Go called Go4Fun, join me if you wish: https://github.com/ialekseev/go4fun

]]>Currying is a technique of converting a function that takes multiple arguments into a chain of functions that each takes a single argument.

Currying usage could look like this in Go:

`f := func(a int, b bool, c float64) string { return fmt.Sprint(a) + " " + fmt.Sprint(b) + " " + fmt.Sprint(c)}curriedF := Curry3(f)r := curriedF(1)(true)(5.5)fmt.Println(r)// Output: 1 true 5.5`

`Curry3`

is a helper function provided by Go4Fun library that takes a 3-argument function as a parameter and does *Currying*: it converts a provided function of 3 arguments into a chain of 3 functions each taking exactly 1 argument.

How it can be useful?

Imagine we have `Map`

function defined for slices that expects a 1-argument function `f`

as a parameter. `Map`

function applies function `f`

to each element of the slice and returns a new slice as a result. It could be defined like this (`Seq`

type is a type based on regular Go slices `type Seq[A any] []A`

with additional methods defined):

`func (seq Seq[A]) Map(f func(A) A) Seq[A] { r := EmptySeq[A](seq.Length()) for _, e := range seq { r = r.Append(f(e)) } return r}`

And then we define a simple function `Add`

to add 2 numbers:

`func Add(a int, b int) int { return a + b}`

And now we want to use `Map`

operation to transform each element of the slice using `Add`

function (add number 10 to each element of the slice):

`seq := Seq[int]{1, 2, 3, 4, 5, 6, 7}//Would not work! Map expects a function of 1 argument but Add has 2...r := seq.Map(Add)`

To help with that we can use currying:

`r := seq.Map(Curry2(Add)(10))fmt.Println(r)//Output: [11 12 13 14 15 16 17]`

`Curry2`

converted the provided function of 2 arguments (`Add`

) into a chain of 2 functions each having exactly 1 argument. And then we provided `10`

value for the first 1-argument function in that chain, which returned us a remaining 1-argument function expecting the second operand for `Add`

operation. This returned function had only 1 argument, thus it was compatible with `Map`

function.

How `Curry2`

function is implemented under the hood? In fact, the implementation is very straightforward:

`func Curry2[A, B, C any](f func(A, B) C) func(A) func(B) C { return func(a A) func(B) C { return func(b B) C { return f(a, b) } }}`

`Curry3`

... functions could be implemented similarly for currying functions of 3 or more arguments.

Since we are able to *Curry* something we should be able to *UnCurry* it back via **UnCurrying**.

UnCurrying is an operation opposite to Currying. It takes a chain of 1-argument functions and coverts it back to 1 function taking multiple arguments.

Usage could look like this:

`f := func(a int) func(bool) func(float64) string { return func(b bool) func(float64) string { return func(c float64) string { return fmt.Sprint(a) + " " + fmt.Sprint(b) + " " + fmt.Sprint(c) } }}unCurriedF := UnCurry3(f)r := unCurriedF(1, true, 5.5)fmt.Println(r)// Output: 1 true 5.5`

`UnCurry3`

function takes a chain of 3 1-argument functions and converts it to 1 function of 3 arguments. It's implemented trivially under the hood:

`func UnCurry3[A, B, C, D any](f func(A) func(B) func(C) D) func(A, B, C) D { return func(a A, b B, c C) D { return f(a)(b)(c) }}`

Other `UnCurry`

functions could be implemented similarly for uncurrying function chains with a different number of functions.

In this article we looked into the concept called *Currying* (and the opposite concept *UnCurrying*). It's primarily used in functional-first languages but even in more imperative languages like Go it could sometimes be of service. If you are interested to explore more practical functional programming concepts applicable in Go and/or would like to get hands-on, welcome to join me on GitHub where I develop Go4Fun library for Go: https://github.com/ialekseev/go4fun. Thanks for reading!

But before we proceed with a partial function application, what does a plain **Application** mean?

Application is a process of applying a function to its arguments to get a result.

When we say that a function is applied to its arguments, we usually mean **Full application **- when we provide *all* arguments required by the function signature.

For example, a function `Apply2`

below does a full application of a 2-argument function `f`

to 2 provided arguments `a`

and `b`

to produce a result of type `C`

:

`func Apply2[A, B, C any](f func(A, B) C, a A, b B) C { return f(a, b)}`

In contrast to Full application, *Partial application* means that we would not provide all arguments for the application, but only *some* of them (in case of a 2-argument function, only 1 of the 2 arguments).

Go doesn't support a partial function application out of the box, but it's possible to implement it by hand. For example, helper functions for a partial application of 2-argument functions could look like that:

`func Apply2Partial_1[A, B, C any](f func(A, B) C, a A) func(B) C { return func(b B) C { return f(a, b) }}func Apply2Partial_2[A, B, C any](f func(A, B) C, b B) func(A) C { return func(a A) C { return f(a, b) }}`

The first function partially applies a 2-argument function `f`

to its 1st argument of type `A`

and returns a function of 1 remaining argument of type `B`

. The second function partially applies a 2-argument function `f`

to its 2nd argument of type B and returns a function of 1 remaining argument of type A.

The reason of having these (slightly inelegant) function names is because Go doesn't support **Function/Method overloading**. In other words, it's not possible to define 2 functions with the same name in the same package, even if functions take different arguments as parameters. It's little bit unfortunate but we can live without that.

By extension, similar helper functions for a partial application could be implemented for 3-argument functions:

`func Apply3Partial_1[A, B, C, D any](f func(A, B, C) D, a A) func(B, C) D { return func(b B, c C) D { return f(a, b, c) }}//...func Apply3Partial_2[A, B, C, D any](f func(A, B, C) D, b B) func(A, C) Dfunc Apply3Partial_3[A, B, C, D any](f func(A, B, C) D, c C) func(A, B) Dfunc Apply3Partial_1_2[A, B, C, D any](f func(A, B, C) D, a A, b B) func(C) Dfunc Apply3Partial_1_3[A, B, C, D any](f func(A, B, C) D, a A, c C) func(B) Dfunc Apply3Partial_2_3[A, B, C, D any](f func(A, B, C) D, b B, c C) func(A) D`

And then we can use these helper functions to do an actual partial application of our functions, for example:

`f := func(a int, b bool, c float64) string { return fmt.Sprint(a) + " " + fmt.Sprint(b) + " " + fmt.Sprint(c)}// function `f` is applied only to the 1st and the 2nd argument.// resulting function `p` has only 1 remaining argument.p := Apply3Partial_1_2(f, 10, true)fmt.Println(p(5.5))// Output: 10 true 5.5`

In this article we explored Partial application of functions and how it could be implemented in Go. If you are interested to deep dive into the topic of functional programming, you are welcomed to join me on GitHub where I develop a library for functional programming in Go: https://github.com/ialekseev/go4fun. We can play together :) Thanks for reading!

]]>Memoization pattern is extensively used in functional programming world because it plays nicely with a concept of **pure functions**. What is a *pure function*?

A function is pure if it always returns the same output for the same input parameters and it has no side effects. In other words, it just maps input parameters to a result in a mathematical sense of it, and doesn't mutate any shared state along the way.

If we have a pure function that does an expensive computation for a provided input, it should be safe to cache a result of that computation when doing the computation for the first time for that input, and for subsequent calls just return the cached result without repeating the same computation again.

Apparently, it is trivial to achieve this caching behavior using a plain `map`

structure as a cache and some imperative construct to save and retrieve values from the map. But is there a more functional and generic way to do it?

In functional programming it could be achieved by **memoizing a function**: an expensive function is wrapped into another function with the same signature but with the caching logic inside. The memoized function would cache results of function calls and would return back a cached result for the same input, if requested again. For example, it could look like that:

`var memoF = Memo(func(a int) string { // expensive computation: time.Sleep(time.Millisecond * time.Duration(a)) return fmt.Sprint(a)})r := memoF(2) // the first call is slowr = memoF(2) // other calls are fastfmt.Println(r)// Output: 2`

But how would we implement the above `Memo`

wrapper? Unsurprisingly, the implementation is quite straightforward:

`func Memo[A comparable, B any](f func(A) B) func(A) B { m := make(map[A]B) return func(a A) B { result, ok := m[a] if ok { return result } else { evaluated := f(a) m[a] = evaluated return evaluated } }}`

As expected, this generic `Memo`

function takes a 1-argument function as a parameter and returns a new function of exactly the same signature. This new function checks if the input has already been cached in the map, and returns it from the map if it's the case. Otherwise, it evaluates the original function and saves the result in the cache for future use.

One problem remains is that our `Memo`

supports only 1-argument functions like `func(A) B`

. But could we somehow reuse it to support functions of a different arity like `func(A, B) C`

, `func(A, B, C) D`

...?

One functional way to do it is to convert a function of 2 (3, or more) arguments into a function that takes 1 **Tuple** argument instead. A `Tuple`

is a structure that contains a fixed number of elements, each with its own type, which could be defined like that:

`type Tuple2[A, B any] struct { a A b B}type Tuple3[A, B, C any] struct { a A b B c C}func Tup2[A, B any](a A, b B) Tuple2[A, B] { return Tuple2[A, B]{a, b}}func Tup3[A, B, C any](a A, b B, c C) Tuple3[A, B, C] { return Tuple3[A, B, C]{a, b, c}}`

Then functions to convert a function of 2 (3, or more) arguments into a function that takes 1 `Tuple`

argument could be defined like that:

`func Tupled2[A, B, C any](f func(A, B) C) func(Tuple2[A, B]) C { return func(t Tuple2[A, B]) C { return f(t.a, t.b) }}func Tupled3[A, B, C, D any](f func(A, B, C) D) func(Tuple3[A, B, C]) D { return func(t Tuple3[A, B, C]) D { return f(t.a, t.b, t.c) }}`

And the opposite operations to convert a function with 1 tupled argument into a regular function of 2 (3, or more) arguments:

`func UnTupled2[A, B, C any](f func(Tuple2[A, B]) C) func(A, B) C { return func(a A, b B) C { return f(Tup2(a, b)) }}func UnTupled3[A, B, C, D any](f func(Tuple3[A, B, C]) D) func(A, B, C) D { return func(a A, b B, c C) D { return f(Tup3(a, b, c)) }}`

Now using these `Tupled`

/`UnTupled`

functions we can make any function of 2 (3, or more) arguments compatible with our `Memo`

function that expects a function of 1 argument:

`func Memo2[A, B comparable, C any](f func(A, B) C) func(A, B) C { return UnTupled2(Memo(Tupled2(f)))}func Memo3[A, B, C comparable, D any](f func(A, B, C) D) func(A, B, C) D { return UnTupled3(Memo(Tupled3(f)))}`

And now we finally can memoize functions of 2 (3, or more) arguments also. For example, for a 2-argument function, usage of our `Memo`

could look like that:

`var memoF = Memo2(func(a, b int) string { // expensive computation: time.Sleep(time.Millisecond * time.Duration(a+b)) return fmt.Sprint(a) + fmt.Sprint(b)})r := memoF(2, 2) // the first call is slowr = memoF(2, 2) // other calls are fastfmt.Println(r)// Output: 22`

In this article we looked into *Memoization* technique, discussed its benefits, and a possible implementation of this pattern in Go. If you are interested to dive deeper into the functional programming topic, you are welcomed to join me on GitHub, where I develop a functional programming library for Go: https://github.com/ialekseev/go4fun. Thanks for reading!

Go, like most of other programming languages, uses the **strict evaluation** strategy.

When we do a variable assignment in Go, an expression on the right side is evaluated first, and then a resulting value of that evaluation is assigned to the variable:

`r := doSomeComputation(a, b, c)`

Or when we call a function, its arguments are evaluated before the function is actually called. In the case below, `calculate1() + calculate2()`

expression is evaluated first, and only then a result of this evaluation is passed to the function `myFunc`

:

`r := myFunc(calculate1() + calculate2())`

This strict evaluation behavior is what most developers are used to and what we want most of the times anyways in practice. And while some programming languages provide special syntax support for delayed evaluations, there is no built-in support for that in Go. However, of course, it could be simulated by wrapping expressions in function values to be evaluated later e.g.:

`func myFunc(lazy func() int) int { evaluated := lazy() //work with the evaluated value}r := myFunc(func() int { return calculate1() + calculate2()})`

This technique of delaying a computation until its result is needed is often called a Thunk.

Another aspect of laziness is a concept of **lazy transformations** when working with (possibly) large data structures. Let's consider this sequence of operations (`Seq`

is a generic type based on Go slices such as `type Seq[A any] []A`

, with additional support for common functional programming operations, provided by Go4Fun library):

`r := Seq[int]{-2, -1, 0, 1, 2, 3, 4, 5, 6}. Filter(func(a int) bool { return a > 0 }). Filter(func(a int) bool { return a%2 == 0 }). Map(func(a int) int { return a / 2 }). Reduce(func(a1, a2 int) int { return a1 + a2 })fmt.Println(r)// Output: 6`

We start with a provided `Sequence`

(slice) and then we apply multiple transformations: the first `Filter`

returns only positive elements `[1 ,2 ,3, 4, 5 ,6]`

, the second `Filter`

further narrows them to return only even numbers `[2, 4, 6]`

, then we transform each element with `Map`

operation dividing each element by 2 to get `[1, 2, 3]`

, and then we sum these elements with `Reduce`

to get the result `6`

.

The problem with this chain of operations is that this expression creates a new intermediate `Sequence`

eagerly after each operation, until the final result is produced by `Reduce`

operation. But, ideally, we instead want to just *describe* successive transformations without evaluating intermediate results, because creating intermediate results could be expensive for large data structures. We want to keep the clean structure of chained operations while avoiding the overhead of growing time and space complexity.

In order to achieve it, we want our strict `Sequence`

to become a `Lazy Sequence`

where intermediate Transformations (`Filter`

, `Map`

...) become "Lazy" operations that *describe* transformations rather than actually apply them. Something that could look like that:

`r := Seq[int]{-2, -1, 0, 1, 2, 3, 4, 5, 6}.Lazy(). Filter(func(a int) bool { return a > 0 }). Filter(func(a int) bool { return a%2 == 0 }). Map(func(a int) int { return a / 2 }). Reduce(func(a1, a2 int) int { return a1 + a2 })fmt.Println(r)// Output: 6`

The only syntax difference that we want to have is a `Lazy()`

method call that converts our *strict* `Sequence`

into a *lazy* one, while all further operations are exactly the same. But how do we achieve that?

One approach to do it is to use **Iterators**. When we call the `Lazy()`

method above, we get a `Lazy Sequence`

structure which has a simple iterator inside (`SeqIterator`

implements `Iterator`

interface):

`type Iterator[A any] interface { HasMore() bool Next() A } type SeqIterator[A any] struct { seq Seq[A] currentIndex int } func (iterator *SeqIterator[A]) HasMore() bool { return iterator.currentIndex < len(iterator.seq) } func (iterator *SeqIterator[A]) Next() A { current := iterator.seq[iterator.currentIndex] iterator.currentIndex = iterator.currentIndex + 1 return current }`

And each next transformation just transforms one input iterator into another one, which also implements `Iterator`

interface. For example, calling `Map`

transformation would return a structure with a new iterator inside (which also implements the same `Iterator`

interface):

`type MapIterator[A, B any] struct { inputIterator Iterator[A] mapF func(A) B}func (iterator *MapIterator[A, B]) HasMore() bool { return iterator.inputIterator.HasMore()} func (iterator *MapIterator[A, B]) Next() B { return iterator.mapF(iterator.inputIterator.Next())}`

In other words, no real transformation happens during the `Map`

call - we just produce a new iterator which *describes* a provided `Map`

transformation on top of the underlying input iterator. Similarly, all other "Lazy" transformations are applied until we get to the last materializing `Reduce`

call, that actually evaluates the whole chain of computations. The `Reduce`

method uses `HasMore()`

and `Next()`

methods of the iterator to traverse it *in one go*, where all provided transformations are applied along the way during the traversal.

Apart from the mentioned above `Map`

transformation, many more could be implemented for our `Lazy Sequence`

using the same technique based on iterators. We can implement it for `FlatMap`

, `Filter`

, `Zip`

and other common functional programming operations. And, similarly, each such operation would just *describe* an actual transformation we want to apply on top of another underlying iterator. When our chain of computations is fully *described*, we can actually *execute* it by calling some materializing action like `Reduce`

, `Fold`

etc. that would actually produce a meaningful result in 1 iteration, without using intermediate Sequences.

Applying some lazy evaluation techniques could sometimes be useful even when there is no direct built-in support for the laziness in the language. Using *Thunks* or *Lazy Transformations* patterns could help to apply laziness to solve some practical problems. If you are interested, you are welcomed to follow up on this topic and check out a functional programming library for Go that I develop https://github.com/ialekseev/go4fun. And join if you like, of course! Thanks for reading!

But before we proceed, let's recall what a **tail recursion** is:

Tail recursion is a recursive function in which the recursive call is the last statement that is executed by the function.

This function *is* tail recursive:

`func summation(n, current int) int { if n < 1 { return current } return summation(n-1, n + current)}`

In contrast, this function *is not* tail recursive:

`func summation(n int) int { if n < 1 { return 0 } return n + summation(n - 1) }`

In a regular non-tail recursion we perform our recursive call first, and then we take an output of the recursive call and calculate a result. This way we don't get a result of our calculation until we have returned from every nested recursive call.

In a tail recursion we perform our calculation first (`n + current`

in this case) and after that we make a recursive call, passing a result of our current step to the next recursive call.

Each recursive call (both regular and tail) requires stack space to store parameters and information associated with each call. But many programming languages could automatically optimize tail recursive calls to avoid stack frame swelling: for example, a compiler could "rewrite" such functions to replace tail recursive calls with a traditional loop. However, that is not the case in Go. On the other hand, Go has a clever implementation of the stack: instead of having a fixed amount of stack memory, the space is elastic - it's growing and shrinking based on-demand, starting with some small default. But even that has a limit - having a too deep recursion eventually would lead to Stack Overflow anyway:

`func summation(n, current uint64) uint64 { if n < 1 { return current } return summation(n-1, n+current)}summation(100000000, 0)// fatal error: stack overflow`

Since Go compiler could not help us to optimize this tail recursion automatically, is there a way to somehow do this manually? Yes, this technique is called **Trampolining**. The basic idea is simple: instead of actually calling our function recursively, we return a deferred *description* of the next call. That call returns a *description* of the next call, and so on, until we get a *description* of the final result. It could be visualized like that:

`More(func() { return More(func() { return Done(result) }) })`

Essentially, our recursive function becomes a *description* of the recursive computation which we need to *run* later on to actually produce a real result. Using Go4Fun functional programming library (which includes a simple Trampoline implementation) it could look like that:

`func summationT(n, current uint64) Trampoline[uint64] { if n < 1 { return DoneTrampolining(current) } return MoreTrampolining(func() Trampoline[uint64] { return summationT(n-1, n+current) })}summationT(100000000, 0).Run()// Output: 5000000050000000`

We created our deferred recursive computation by calling `summationT(100000000, 0)`

and then we actually ran it calling `Run()`

. No Stack Overflow happens this time. It works because, basically, this way we exchanged a stack allocation for heap: instead of having nested recursive calls blowing up the stack, we have a chain of structures allocated in the heap.

Let's see how this simple Trampoline is implemented in the library:

`type Trampoline[A any] struct { call func() Trampoline[A] done bool result A}func DoneTrampolining[A any](a A) Trampoline[A] { return Trampoline[A]{done: true, result: a}}func MoreTrampolining[A any](more func() Trampoline[A]) Trampoline[A] { return Trampoline[A]{call: more, done: false}}func (t Trampoline[A]) Run() A { next := t for { if next.done { return next.result } next = next.call() }}`

As we can see, the implementation is very straightforward: we have a simple `Trampoline`

structure to represent a next deferred `call`

OR a final `result`

. Also we have a function `MoreTrampolining`

to wrap a deferred call into this structure and `DoneTrampolining`

to wrap a final result. Plus, we have a method `Run`

which traverses our Trampoline until we unwrap it fully to get a final result.

In this article we covered a simple Trampolining technique that allows us to keep a recursive structure of the code, which is natural for some algorithms, while avoiding potential Stack Overflow issues. This Trampoline implementation is by no means comprehensive e.g. it doesn't allow us to describe recursive functions with multiple recursive calls (e.g. a canonical implementation of Fibonacci sequence calculation). This would require a more powerful Trampoline which could combine multiple Trampolines together (e.g. using `Map`

, `FlatMap`

combinators). It is something we might explore in a future article. But now, if you are interested to dive deeper into some hands-on functional programming in Go - I would be happy see you joining me on GitHub (https://github.com/ialekseev/go4fun). Thanks for reading!

In this article we will explore a functional programming pattern called *Monad* and one of its canonical implementations - `Option`

type.

As many functional programming concepts, Monads originate from Category theory. And while it's nice to know that a useful development pattern finds its roots in mathematics, we are going to look at this concept from the practical angle only.

Practically, a Monad type is a container type `M`

that wraps some value `A`

and has 2 operations defined: `Unit`

(that wraps a value of type `A`

into a Monad `M`

) and`FlatMap`

(that transforms a wrapped value `A`

to a value `B`

) :

`type M[A any] struct { value A}func Unit[A any](a A) M[A] func FlatMap[A, B any](m M[A], f func(a A) M[B]) M[B]`

It's quite likely that for a person with no prior experience with Monads, this definition would not click right away. To answer a question "but why it is useful?" we will implement a simple but handy `Option`

Monad.

`Option`

type represents some value that might exist or might not. In Go it could be defined using a simple structure like this:

`type Option[A any] struct { value A defined bool}`

Also we will define 2 functions for creating an `Option`

. These functions effectively represent `Unit`

operation from the Monad definition above (`Some`

wraps a `value`

into `Option`

, while `None`

represents a lack of value):

`func Some[A any](value A) Option[A] { return Option[A]{value, true}}func None[A any]() Option[A] { return Option[A]{defined: false}}`

One benefit of using `Option`

type in the code is that it states a developer's intentions clearly. If we see a function returning `Option[string]`

we know that a value might not exist and we are forced to process that case explicitly. We know that if a function returns `string`

then it has to have some meaningful value which we can use in our logic right away, without further checks. But having `Option[string]`

indicates that it might not be the case.

However, even though using our `Option`

type would serve this purpose, it will not be enough to convince how it's better than using another more idiomatic way to handle optional values in Go - returning multiple values like `value, ok = getSomeOptionalValue()`

. But when we complete our `Option`

type implementation by adding a `FlatMap`

operation, it should make some difference. We will define it like that:

`func FlatMap[A, B any](option Option[A], f func(A) Option[B]) Option[B] { if option.defined { return f(option.value) } else { return None[B]() }}`

Now imaging we have a chain of computations where each next computation depends on the previous one, but each computation might not return a successful result (a result is optional):

`func calculate1() Option[int] { return Some(100)}func calculate2(a int) Option[int] { return Some(a + 200)}func calculate3(a int) Option[string] { return Some(fmt.Sprint(a))}func calculate4(a string) Option[string] { return Some("abc " + fmt.Sprint(a))}`

Using our `Option`

Monad we could express the overall computation like that:

`FlatMap(FlatMap(FlatMap(calculate1(), calculate2), calculate3), calculate4)// Some(abc 300)`

And if it happens that any of these computations yields `None`

, then the overall result of the computation would become `None`

as well:

`FlatMap(FlatMap(FlatMap(None[int](), calculate2), calculate3), calculate4)// None`

Essentially, `Option`

Monad could help to chain computations with optional results, thus helping to reduce boilerplate code which is otherwise needed for dealing with non-present values.

We've touched slightly a concept of Monads from practical perspective, and implemented a simple `Option`

Monad. Of course, apart from the operations which we defined for `Option`

type, there are many more common functional programming combinators helping to make Options even more useful in practice. If you are interested to find out more or dive deeper into the topic of functional programming in Go, I would be more than happy to see you joining me on GitHub (https://github.com/ialekseev/go4fun). We can play together :) Thanks for reading!

But what is a functional programming in the first place? And how it is different from imperative programming? From Wikipedia:

Functional programming is a programming paradigm where programs are constructed by applying and composing functions. It is a declarative programming paradigm in which function definitions are trees of expressions that map values to other values, rather than a sequence of imperative statements which update the running state of the program.

Thus, one of the main prerequisites of functional programming support in a given language is treating functions as first-class citizens there: ability to assign functions to local variables, pass them around and return from other functions. And it's supported by Go, of course. That makes it possible to organize our code in a more declarative way (describing "what" we want to do), in contrast to a more imperative way ("how" we want to do it). For example:

`seq := []int{1, 2, 3, 4, 5, 6, 7}sum := 0for _, e := range seq { if e%2 == 0 { sum = sum + e*2 }}return sum// 24`

`seq := fun.Seq[int]{1, 2, 3, 4, 5, 6, 7}sum := seq.Filter(func(e int) bool { return e%2 == 0 }). Map(func(e int) int { return e * 2 }). Reduce(func(e1, e2 int) int { return e1 + e2 })return sum// 24`

Both are Go code snippets that produce the same output. The first one is a more idiomatic imperative way to do it in Go. The second one is a more functional way using common higher-order functions (functions taking other functions as arguments) provided by a functional programming library I created Go4Fun.

It's a bit unfortunate that Go doesn't support a short lambda syntax present in many other modern programming languages, that would allow the code to look like that, for example (that is not real Go code):

`seq.Filter(e => e%2 == 0). Map(e => e * 2). Reduce((e1, e2) => e1 + e2)`

There is an open Go 2 proposal to add support for a more lightweight anonymous function syntax (https://github.com/golang/go/issues/21498). At the moment, it looks like there is a standoff between "progressives" who want Go to have it and "conservatives" resisting the proposal (recommend to read, quite interesting).

Anyway, even without the short lambda syntax it's possible to write useful common generic functional programming types (`Option`

, `Future`

, `Either`

...) and higher-order functions (`Map`

, `FlatMap`

, `Filter`

, `Fold`

, `Reduce`

, `Zip`

...).

Go support for higher-order functions allows to implement by hand other functional programming concepts not provided out of the box: *currying* and *function composition*.

Currying is the technique of converting a function that takes multiple arguments into a sequence of functions that each takes a single argument.

Currying of a function with 3 arguments could be implemented in Go like that:

`func Curry3[A, B, C, D any](f func(A, B, C) D) func(A) func(B) func(C) D { return func(a A) func(B) func(C) D { return func(b B) func(C) D { return func(c C) D { return f(a, b, c) } } }}`

And then it can be used to curry a function `f`

with 3 arguments:

`f := func(a int, b bool, c string) string { return "(" + fmt.Sprint(a) + " " + fmt.Sprint(b) + " " + fmt.Sprint(c) + ")"}curriedF := Curry3(f)// Calling the curried function with all 3 arguments:curriedF(1)(true)("abc")// Using the curried function (providing 2 arguments out of 3)// with Map function (expecting 1 String argument in this case):seq := Seq[string]{"abc", "def", "ghi"}r := seq.Map(curriedF(1)(true))return r// [(1 true abc) (1 true def) (1 true ghi)]`

Currying is a useful practical concept which benefits however might not be immediately obvious. Since many common higher-order functional programming functions (like `Map`

) expect functions with 1 argument as a parameter, using Currying technique we can convert some multi-argument function into a sequence of 1-argument functions, thus making it compatible. Like in the example above: we have a function `f`

which expects 3 arguments, but we are able to use it with `Map`

by currying `f`

first and then providing 2 arguments out of 3.

Function composition is an operation that takes two functions f and g, and produces a function h such that h(x) = g(f(x)).

It could be implemented in Go like that:

`func Compose2[A, B, C any](f func(A) B, g func(B) C) func(A) C { return func(a A) C { return g(f(a)) }}`

And then we can use it like that:

`f := func(a int) bool { return a != 0 }g := func(b bool) string { return fmt.Sprint(b) }h := Compose2(f, g)return h(1) == g(f(1))// true`

Function composition allows us to build more complex functions out of simpler ones. For a trivial example like the above, the benefit would clearly not outweigh the trouble, but the more functions, acting as building blocks, you have in your pipeline - the more obvious the benefit could become.

Apart from higher-order functions, currying and function composition, there are other features often associated with functional programming: *closures*, *tail recursion optimization*, *immutable data types*...

A closure is a function value that references variables from outside its body.

Obviously, it's supported by Go, for example:

`func myfunc() func(int) int { s := 10 return func(x int) int { return s + x }}`

In this case `myfunc`

function returns a closure: a new function value "bound" to a local variable `s`

of `myfunc`

.

What is a tail recursion?

Tail recursion is a recursive function in which the recursive call is the last statement that is executed by the function.

In a traditional recursion often you perform your recursive call first, and then you take an output of the recursive call and calculate a result. This way you don't get a result of your calculation until you have returned from every recursive call.

In a tail recursion you perform your calculation first and after that you make a recursive call passing a result of your current step to the next recursive call.

For example, this function is not tail-recursive:

`func factorial(n int) int { if n < 2 { return 1 } return n * factorial(n - 1)}`

But this one is tail recursive:

`func factorial(n, current int) int { if n < 2 { return current } return factorial(n - 1, n * current)}`

In many languages the latter case could be optimized automatically by the compiler or runtime to avoid stack frame growing (which otherwise could eventually lead to stack overflow crash). Often that is done by replacing a tail recursion with a traditional loop or by reusing a stack frame when calling the function in the tail position. In that case it's possible to get the best of 2 worlds: expressiveness of recursion, natural when implementing some algorithms, plus performance of traditional loops.

At the moment, the tail-call optimization is not supported by Go. There was a proposal which was closed after a long discussion: https://github.com/golang/go/issues/22624

In object-oriented and functional programming, an immutable object is an object whose state cannot be modified after it is created.

Immutable objects provide a number of important benefits: they are easier to reason about (we know that they can't be updated unexpectedly by some other parts of the code, thus not requiring defensive copies), more thread-safe and (in some cases) could even improve runtime efficiency.

Many core built-in types in Go are naturally immutable: `bool`

, `int`

, `string`

... But when talking about a language support for immutable data types, often a more general support for immutable composite data types is implied. E.g. slices, maps or structures that would get "frozen" after they are created: you can read them but could not update "in place", only by getting a copy of the object with an updated value. It might sound as a horribly inefficient exercise, however it depends on how underlying data structures are implemented - updated immutable instances could potentially reuse existing data nodes which could be very efficient, especially when creating copies of objects.

At the moment, there is no built-in support for immutable slices, maps or structures in Go, however a similar effect could often be achieved by e.g. exporting only read-only methods from the structure, without public access to the internal fields and mutating methods. That could be a bit verbose but still could do. There is a number of open Go 2 proposals on the immutability topic that could be interesting to read, like: https://github.com/golang/go/issues/27975

In this article we've scratched the surface of some functional programming concepts and their possible application in Go: imperative vs functional code, higher-order functions, currying, function composition, closures, tail recursion, immutable data types. Go is a practical language with simplicity being one of its the most cherished assets. And still it supports a decent number of features that make it possible to apply some practical functional programming techniques. If someone is interested to explore this topic in more depth and would like to join me in this adventure, I would be happy to see you on GitHub (https://github.com/ialekseev/go4fun). We can play together :) Thanks for reading!

]]>