It’s an interesting language that tries to prevent programmers from shooting themselves in the foot while trying to maintain the efficiency of the JVM. Here are some interesting and sometimes (pleasantly) surprising things Kotlin does.
Null needs to be explicity defined
if vs when
Both are expressions that can return values.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
funmain() { print("Enter the temperature:")
val input = readLine() if (input == null) return
val comment = if (input.toInt() > 37) { println("seems hot...") "Make an omlette on the pavement" } elseif (input.toInt() < 0) { "Brrr...that's cold" } else { "Nice day for a walk" } println(comment) }
1 2 3 4 5 6 7 8 9 10 11
funmain() { print("Enter the temperature:") val input = readLine() val comment = when(input?.toInt()) { in-40..5 -> "Brrr...that's cold" in6..24 -> "Mmmm...that's crisp" in25..37 -> "Getting hot in here" else -> "Make an omlette on the pavement" } println(comment) }
arrays
You can create arrays of mixed types
1 2 3 4 5 6
funmain() { var collection = arrayOf("Apple", 23.6, 'D', 77) for (e in collection) { println(e) } }
If we convert to bytecodes and look at the Java equivalent we see it’s just an array of Objects.
funmain() { outer@for (i in"This is the outer loop") { for (j in"This is the inner loop") { if (j in"aeiou") break@outer println("$i .. $j") } } }
Functions
It’s cool that functions can be defined as expressions
1
funproduct(a: Int, b: Int) = a * b
Smart Casts
Safe casts are inserted when needed.
1 2 3 4 5 6 7
val a :Any a = "45"
if (a !is String) return println("Len = ${a.length}") println("this prints too") println("and so does this")
The lines after the condition are only executed for Strings.
Properties
Classes can have properties which can be marked as read-only(val) or read-write(var). Variables be initialized to be used. If it cannot be initialized then qualify it with lateinit.
Constructors
There are primary and secondary constructors.
Primary
1 2 3 4 5 6
classDog(val name :String)
funmain() { var d = Dog("Spike") println("My name is ${d.name}") }
Secondary
1 2 3 4 5 6 7 8 9 10 11
classDog{ val name: String constructor(name: String) { this.name = name } }
funmain() { var d = Dog("Spike") println("My name is ${d.name}") }
Data classes
Inheritance
Modifiers are explicit at the function level. By default functions are marked as closed and need to be qualified with open to be able to override it.
var <propertyName>[: <PropertyType>] [= <property_initializer>] [<getter>] [<setter>]
Abstract classes
Abstract classes allow us to define specific traits similar to interfaces.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
abstract class Plane(val name: String) { abstract fun fly() }
fun main() { val p = F120("Zippy") println(p) }
class F120(myname: String): Plane(myname) { override fun fly() { println("zooooom") } override fun toString(): String { return "name --> $name" } }
Functional Programming [TODO]
One of the paradigms that can be used.
1 2
Coroutines
Basic example
1 2 3 4 5 6 7 8 9 10
import kotlinx.coroutines.*
funmain() { GlobalScope.launch { // launch new coroutine in background and continue delay(3333L) // non-blocking delay for 1 second (default time unit is ms) println("${Thread.currentThread()} World!") // print after delay } println("${Thread.currentThread()} Hello,") // main thread continues while coroutine is delayed Thread.sleep(5000L) // block main thread for 2 seconds to keep JVM alive }
funreverse(list: List<Int>): List<Int> { val result = arrayListOf<Int>() for (i in0..list.size - 1) { result.add(list[list.size - 1 - i]) } return result }
dataclassResult(val failed: Boolean, val numTries: Int)
funmain(args: Array<String>) { print("What word do you want to guess? ") val word = readLine() if (word == null) { println("No word provided...exiting") return }
for (i in1..100) println()
val letters = word.toLowerCase().toCharArray().toHashSet() val correctGuesses = mutableSetOf<Char>() var totalTries = 0
while (letters != correctGuesses) { print("Enter letter: ") val c = readLine() if (c == null) { println("Bad input") return } val (failed , numTries) = guessWord(c.first(), correctGuesses, totalTries, word) totalTries = numTries if (!failed) break } println("Number of wrong guesses = $totalTries"); }
funguessWord(char: Char, correctGuesses: MutableSet<Char>, fails: Int, word: String): Result { var failed = false var count = fails //if (correctGuesses.contains(char)) return Result(false, fails) for(c in word) { if (c == char) { print("$c ") correctGuesses.add(c) } elseif (correctGuesses.contains(c)) { print("$c ") } else { print("_ ") failed = true } } if (failed) count++ return Result(failed, count) }
Example: Get Max IP address count from file
1 2 3 4 5 6 7 8 9 10 11 12 13 14
package collections
import java.io.File
funmain(args: Array<String>) { var file = File("input.txt") val addrCount = mutableMapOf<String, Int>() var maxPair: Pair<String, Int> = Pair("", 0) file.forEachLine { addrCount.put(it, addrCount.getOrDefault(it, 0) + 1) maxPair = if (maxPair.second < addrCount.get(it)!!) Pair(it, addrCount[it]!!) else maxPair } println(maxPair) }
Example: Capitalize Words in a Sentence
Implementation
1 2 3 4 5 6
package capitalizeSentence
funcapitalizeLetter(sentence: String) : String { val s = sentence.split(" ").joinToString(separator = " ") { it.capitalize()} return s }
@Test fun `range 5-7 contains 5-7`() { assertTrue(checkSubrange(5..7, 5..7)) }
@Test fun `range 6-7 does not contain 5-7`() { assertFalse(checkSubrange(6..7, 5..7)) }
@Test fun `range 5-8 does not contain 3-5`() { assertFalse(checkSubrange(5..8, 3..5)) } }
Example: Add number up to N
Given positive integer n implement a function which calculates sum of all numbers from 1 up to (and including) number n.
Implementations
1 2 3 4 5 6 7 8 9 10
import java.lang.IllegalArgumentException
funaddUpTo(p: Int): Int { if (p < 0) throw IllegalArgumentException("Cannot solve for negatives!") var amount = 0 for (e in0..p) { amount += e } return amount }
More idiomatic solutions
1 2 3 4 5 6 7 8 9
funaddUpTo2(p: Int): Int { if (p < 0) throw IllegalArgumentException("Cannot solve for negatives!") return (1..p).sum() }
funaddUpTo3(p: Int): Int { if (p < 0) throw IllegalArgumentException("Cannot solve for negatives!") return (0..p).fold(0) {accumulated, current -> accumulated + current} }
Given a string containing just the characters ‘(‘ and ‘)’, find the length of the longest valid (well-formed) parentheses substring.
Example 1:
Input: “(()” Output: 2 Explanation: The longest valid parentheses substring is “()”
Example 2:
Input: “)()())” Output: 4 Explanation: The longest valid parentheses substring is “()()”
Requirements
string containing just the characters ‘(‘ and ‘)’
find the length of the longest valid (well-formed) parentheses substring
string can be empty? yes
Approach
We can use a stack to keep track of opening parentheses. When we see an opening parenthesis, we push index of opening parenthesis to stack. When we see closing, we pop off stack and add 2 to a running counter. After stack is empty we update the global max of counts.
1 2 3 4 5 6 7
classSolution { public: intlongestValidParentheses(string s){ int max_len = 0; int curr_len = 0; // TODO };
Given a non-empty list of words, return the k most frequent elements.
Your answer should be sorted by frequency from highest to lowest. If two words have the same frequency, then the word with the lower alphabetical order comes first.
Example 1:
Input: [“i”, “love”, “leetcode”, “i”, “love”, “coding”], k = 2 Output: [“i”, “love”] Explanation: “i” and “love” are the two most frequent words. Note that “i” comes before “love” due to a lower alphabetical order.
Example 2:
Input: [“the”, “day”, “is”, “sunny”, “the”, “the”, “the”, “sunny”, “is”, “is”], k = 4 Output: [“the”, “is”, “sunny”, “day”] Explanation: “the”, “is”, “sunny” and “day” are the four most frequent words, with the number of occurrence being 4, 3, 2 and 1 respectively.
Note:
You may assume k is always valid, 1 ≤ k ≤ number of unique elements. Input words contain only lowercase letters.
Follow up:
Try to solve it in O(n log k) time and O(n) extra space.
Requirements
return the k most frequent elements
answer should be sorted by frequency from highest to lowest
If two words have the same frequency, then the word with the lower alphabetical order comes first
words contain only lowercase letters
solve it in O(n log k) time and O(n) extra space
Approach
We can use a map for gathering the counts of all the words. This can be done in O(n) time and will use O(n) space. Once we have this, we can use a priority queue with a custom comparator.
Git is a fantastic piece of technology. It’s been a game-changer for me, both professionally as well as personally. I started using it for the first time six years ago. At first I was quite repulsed by it. Over time though, I’ve grown quite fond of it.
In this post, I will be diving deeper into the internals of git to see what makes it such a solid piece of software.
You are given the number of rows n_rows and number of columns n_cols of a 2D binary matrix where all values are initially 0. Write a function flip which chooses a 0 value uniformly at random, changes it to 1, and then returns the position [row.id, col.id] of that value. Also, write a function reset which sets all values back to 0. Try to minimize the number of calls to system’s Math.random() and optimize the time and space complexity.
Note:
1 <= n_rows, n_cols <= 10000
0 <= row.id < n_rows and 0 <= col.id < n_cols
flip will not be called when the matrix has no 0 values left.
the total number of calls to flip and reset will not exceed 1000.
The input is two lists: the subroutines called and their arguments. Solution’s constructor has two arguments, n_rows and n_cols. flip and reset have no arguments. Arguments are always wrapped with a list, even if there aren’t any.
Constraints etc
minimize calls to random function
optimize the time and space complexity -
Approaches
We can use a reservoir sampling approach.
Mapping a 2D matrix to an array.
Given an index from an array we can compute the row and column of the original matrix as long as we know the number of rows and columns.
/* pick a number between 0 and n_samples - 1 */ vector<int> flip() { int selected; int last = n_samples; // pick random number from 0 to n_samples - 1 int r = rand() % n_samples;
if (indices.find(r) != indices.end()) { // if index already selected before then pick // the next available one from map selected = indices[r]; } else { selected = r; } // store it for when we hit the same index again indices[r] = n_samples; //reduce sample size for next run n_samples--;
/** * Your Solution object will be instantiated and called as such: * Solution obj = new Solution(n_rows, n_cols); * vector<int> param_1 = obj.flip(); * obj.reset(); */
Argg 😞
I cannot do
1 2 3 4 5
vector<int> v; // row v[0] = i / n_cols; // column v[1] = i % n_cols;
We should do push_back().
There is also a division by zero problem here.
1 2
// pick random number from 0 to n_samples - 1 int r = rand() % n_samples;
/* pick a number between 0 and n_samples - 1 */ vector<int> flip() { int selected; int last = n_samples; int r = 0; if (n_samples) r = rand() % n_samples;
if (indices.find(r) != indices.end()) { // if index already selected before then pick // the next available one from map selected = indices[r]; } else { selected = r; }
//reduce sample size for next run n_samples--;
// store it for when we hit the same index again, // we want to store it after decrement so we pick the right one indices[r] = n_samples;
int n_samples, n_rows, n_cols; unordered_map<int, int> indices;
public: Solution(int n_rows, int n_cols):n_rows(n_rows), n_cols(n_cols), n_samples(n_rows*n_cols) { srand(time(0)); }
vector<int> flip() { int r = 0; if (n_samples) r = rand() % n_samples;
// if index already selected before then pick // the next available one from map int selected = indices.count(r) ? indices[r] : r;
//reduce sample size for next run n_samples--;
// store it for when we hit the same index again, // we want to store it after decrement so we pick the right one // if the last element indices[r] = indices.count(n_samples) ? indices[n_samples] : n_samples;
int n_samples, n_rows, n_cols; unordered_map<int, int> indices;
public: Solution(int n_rows, int n_cols):n_rows(n_rows), n_cols(n_cols), n_samples(n_rows*n_cols) { srand(time(0)); }
vector<int> flip() { int r = 0; if (n_samples) r = rand() % n_samples;
// if index already selected before then pick // the next available one from map int selected = indices.count(r) ? indices[r] : r;
//reduce sample size for next run n_samples--;
// store it for when we hit the same index again, // we want to store it after decrement so we pick the right one // if the tail element is the map then we pick from map // otherwise we can just store the index indices[r] = indices.count(n_samples) ? indices[n_samples] : n_samples;
voidnextPermutation(vector<int>& nums){ auto i = is_sorted_until(nums.rbegin(), nums.rend()); if (i != nums.rend()) swap(*i, *upper_bound(nums.rbegin(), i, *i)); reverse(nums.rbegin(), i); }
Let’s say you have a really cool website where your visitors always randomly see a new image. Kind of like the illustration of a person here. I suspect this uses some kind of a naive algorithm to change the image. But let’s say you had millions of images at you disposal because you are using an endless stream of images from instagram.
And you want to (almost) always pick an image uniquely. How could you do this?