Here comes the squid game. Now let's start. Let's see what level you've passed.

Posted by TRUSTINWEB on Tue, 18 Jan 2022 14:36:33 +0100

Here comes the squid game. Now let's start. Let's see what level you've passed.

Without the aid of IDE, see whether your human flesh compiler can compile the correct results.

Scala-like functions

fun hello() = {  
    println("Hello, World")  
}  
  
hello()  

a). Does not compile

b). Prints "Hello, World"

c). Nothing

d). Something else

Tip: in the IDE, lint will prompt, Unused return value of a function with lambda expression body

Answer: C

To execute this lambda, you need to use hello()(), or hello () invoke()

This reminds me of an operation in fluent: immediately invoked function expression (IIFE), namely () {} ()

Indent trimming

val world = "multiline world"  
println(  
    """  
        Hello  
        \$world  
    """.trimIndent()  
)  

a)

Hello  
$world  

b)

Hello  
$world  

c)

Hello  
\multiline world  

d)

Doesn't compile

Answer: C

In Kotlin, raw string is defined by a three quotation mark (""), does not contain escape, but can contain newline characters and any other characters. Even if there is an escape character \, string cannot be used there. If we have to show a string, we need to use KaTeX parse error: Expected '}', got 'EOF' at end of input: {''} instead.

If-else chaining

fun printNumberSign(num: Int) {  
    if (num < 0) {  
        "negative"  
    } else if (num > 0) {  
        "positive"  
    } else {  
        "zero"  
    }.let { Log.d("xys", it) }  
}  
  
  
printNumberSign(-2)  
Log.d("xys", ",")  
printNumberSign(0)  
Log.d("xys", ",")  
printNumberSign(2)  

a) negative,zero,positive

b) negative,zero,

c) negative,positive

d) ,zero,positive

Answer: D

,  
zero  
,  
positive  

Remember, after processing by the Java compiler, the else if structure is actually processed by calling else with a "single line" if, that is, no matter how many else if, it will actually be converted into nesting in else. In Kotlin, functions are parsed before if else blocks, so Let {print (it)} applies only to the last else if. So in this case, the result of the first if statement will not be used and the function will return immediately. To avoid this, you can wrap the entire if... Else... In parentheses and then add let.

Lambda runnables

fun run() {  
    val run: () -> Unit = {  
        println("Run run run!")  
    }  
    Runnable { run() }.run()  
}  
  
Call:  
run()  
  

a) "Run run run!"

b) Doesn't compile

c) StackOverflowError

d) None of the above

Answer: A

This question is actually about the use of Kotlin local functions. The above code is actually equivalent to the following code:

val run1: () -> Unit = {  
    println("Run run run!")  
}  
fun run() {  
    Runnable { run1() }.run()  
}

Using local functions, you can hide logic inside functions.

Making open abstract

open class A {  
    open fun a() {}  
}  
  
abstract class B: A() {  
    abstract override fun a()  
}  
  
open class C: B()  

a) Compiles fine

b) Error: Class 'C' is not abstract and does not implement abstract base class member

c) Error: 'a' overrides nothing

d) Error: Function 'a' must have a body

Answer: B

We can override an open function with an abstract function, but it is still abstract. We need to override the methods to be implemented in all subclasses, and the following code can be executed.

open class A {  
    open fun a() {}  
}  
  
abstract class B: A() {  
    abstract override fun a()  
}  
  
open class C: B() {  
    override fun a() {}  
}  
  
C().a()  

**List minus list
**

val list = listOf(1, 2, 3)  
println(list - 1)  
println(list - listOf(1))  
val ones = listOf(1, 1, 1)  
println(ones - 1)  
println(ones - listOf(1))  

Options:

a) [2, 3][2, 3][1, 1][1, 1]  
b) [2, 3][2, 3][1, 1][]  
c) [1, 3][2, 3][][1, 1]  
d) [2, 3][2, 3][][]  

Answer: B

This question is actually to investigate the implementation of the minus function. In Kotlin:

List****minus T: remove the first matching element.

List****minus List: remove all elements existing in the second list from the first list.

Composition

operator fun (() -> Unit).plus(f: () -> Unit): () -> Unit = {  
    this()  
    f()  
}  
  
({ print("Hello, ") } + { print("World") })()  

a) "Hello, World"

b) Error: Expecting top-level declaration

c) Error: Expression f cannot be invoked as a function

d) Error: Unresolved reference (operator + not defined for this types)

e) Works, but prints nothing

Answer: A

The definition of operator overloaded function plus is completely correct. It returns a new function (created using a lambda expression) consisting of two functions as arguments. When we add two functions, we have another function to call. When we call it, we have one lambda expression after another called.

What am I?

val whatAmI = {}()  
println(whatAmI)  

a) "null"

b) "kotlin.Unit"

c) Doesn't print anything

d) Doesn't compile

Answer: B

This question examines the basic knowledge of lambda expression. This lambda expression does not return content, so its type is Unit.

Return return

fun f1(): Int {  
    return return 42  
}  
fun f2() {  
    throw throw Exception()  
}  

Can f1 and f2 be executed?

a) returns 42; throws exception

b) returns 42; doesn't compile

c) doesn't compile; throws exception

d) doesn't compile; doesn't compile

Answer: A

The first return in f1 is actually invalid. If it is in the IDE, Lint will prompt.

The return expression has a return type and can be used as an expression. In f1, it also ends the execution of f1 with result 42. Similarly, throw declares the type - Nothing is also a return type, so both functions can be compiled, but when f2 is called, it will end with an exception.

Extensions are resolved statically

open class C  
class D : C()  
  
fun C.foo() = "c"  
fun D.foo() = "d"  
  
fun printFoo(c: C) {  
    println(c.foo())  
}  
  
Call:  
printFoo(D())  

a) Doesn't compile

b) Runtime error

c) c

d) d

Answer: C

This example examines the specific implementation principle of the extension function. Because the called extension function only depends on the declaration type of parameter C, that is, class C, it will only call the extension function foo of class C.

Extensions do not actually modify the classes they extend. By defining an extension function, you don't really insert new members into a class, but just let the new function be called with a dot on this type of variable, which is equivalent to a layer of Wrapper.

Expression or not

fun f1() {  
    var i = 0  
    val j = i = 42  
    println(j)  
}  
  
fun f2() {  
    val f = fun() = 42  
    println(f)  
}  
  
fun f3() {  
    val c = class C  
    println(c)  
}  

What are the results of f1, f2 and f3?

a)

42 () -> kotlin.Int class C

b)

42 () -> kotlin.Int doesn't compile

c)

doesn't compile () -> kotlin.Int doesn't compile

d)

doesn't compile doesn't compile doesn't compile

Answer: C

Variable initialization and class declaration are statements in Kotlin. They do not declare any return type, so we can't assign this declaration to variables, so we can't compile. In f2, we actually implement an anonymous function, so we output a function.

Eager or lazy?

val x = listOf(1, 2, 3).filter {  
    print("$it ")  
    it >= 2  
}  
  
print("before sum ")  
println(x.sum())  

a) 1 2 3 before sum 5

b) 2 3 before sum 5

c) before sum 1 2 3 5

d) order is not deterministic

Answer: A

Unlike the Stream API of Java 8, the collection extension function in Kotlin is Eager's. If you need to use Lazy mode, you can use sequenceOf or asSequence. Both sequences are initialized with inertia.

Map default

val map = mapOf<Any, Any>().withDefault { "default" }  
println(map["1"])  

a) default

b) nothing

c) null

d) will not compile*

Answer: C

Don't be fooled by the literal meaning of withDefault. withDefault can only be used in delegated attribute scenarios. Therefore, for unknown extension functions, you must go in and see the implementation. You can't guess.

val map = mutableMapOf<String, Set<String>>().withDefault { mutableSetOf() }  
  
var property: Set<String> by map // returns empty set by default  

Null empty

val s: String? = null  
if (s?.isEmpty()) println("is empty")  
if (s.isNullOrEmpty()) println("is null or empty")  

a) is empty is null or empty

b) is null or empty

c) prints nothing

d) doesn't compile

Answer: D

When s == null, s Isempty() will return null, so the return type of this expression should be Boolean?, Therefore, it cannot be compiled. You can modify it in the following ways:

val s: String? = null  
if (s?.isEmpty() == true) {  
    println("is empty")  
}  
if (s.isNullOrEmpty()) {  
    println("is null or empty")  
}  

List or not

val x = listOf(1, 2, 3)  
println(x is List<*>)  
println(x is MutableList<*>)  
println(x is java.util.List<*>)

a) true false true

b) false false true

c) true true true

d) true false false

Answer: C

In Kotlin, listOf, MutableList and Java ArrayList all return Java util. List, so their types are the same.

Everything is mutable

val readonly = listOf(1, 2, 3)  
  
if (readonly is MutableList) {  
    readonly.add(4)  
}  
  
println(readonly)

a) [1, 2, 3]

b) [1, 2, 3, 4]

c) UnsupportedOperationException

d) Will not compile

Answer: C

Similar to listOf and array Helper functions such as aslist () return Java util. Arrays $arraylis, not Java util. ArrayList, so they cannot be modified.

Fun with composition

val increment = { i: Int -> i + 1 }  
val bicrement = { i: Int -> i + 2 }  
val double = { i: Int -> i * 2 }  
val one = { 1 }  
  
private infix fun <T, R> (() -> T).then(another: (T) -> R): () -> R = { another(this()) }  
operator fun <T, R1, R2> ((T) -> R1).plus(another: (T) -> R2) = { x: T -> this(x) to another(x) }  
  
Call:  
val equilibrum = one then double then (increment + bicrement)  
println(equilibrum())

a) Nothing, it doesn't compile

b) 5

c) (1, 2)

d) (3, 4)

Answer: D

This is a classic compound function problem. The overloaded plus function returns the pair generated by one or two functions. Therefore, we all start with {1} and become 2 through the then infix operator double. In plus, it is executed as 3 and 4 respectively.

The case comes from puzzlers on KT Academy

Many people say that what is the use of these things? A lot of code can be put in the IDE to know whether it is right or wrong and what the running result is. Why do you do this?

In fact, understanding these things will be very helpful to your programming thinking and language understanding. In the IDE, it has helped us do so many things that we can't really find out the essence of the problem. With the training of these problems, we can understand how the compiler handles the code and how the code is executed, That's why we train these subjects.

so, did you live to the end of this squid game?

Don't panic, there's the next episode!

Topics: Design Pattern Interview Programmer Game Development