In this section, you learned it’s useful to avoid creating multiple instances of immutable classes because they represent the same value. Given the following immutable class Id:
class Id(val id: Int)
How would you change it to prevent any client from creating multiple instances of Id for the same id?
When you run this code:
fun main() {
val id1 = // Create Id for id = 1
val id2 = // Create Id for id = 1
val id3 = // Create Id for id = 2
val id4 = // Create Id for id = 2
println("${id1 === id2}")
println("${id1 === id2}")
println("${id1 === id3}")
println("${id3 === id4}")
}
You get:
true
true
false
true
Exercise 6.1 solution
In Effective Java — the book by Joshua Bloch mentioned earlier in the chapter — “Item 1” states: “Consider using static factory methods instead of constructors”. This is because they allow you to control the way an instance of a class is created. In this case, “controlling” means:
Pgu boqr haahs oq ydu agi suo’sl odu ja nu nyu ulewqeju. Sahsotut, kuv okqgekpu, vgi bamhehiqt bixi:
class Id private constructor(val id: Int) { // 1
companion object { // 2
private val ids = mutableMapOf<Int, Id>() // 3
fun of(id: Int): Id { // 4
var existingId = ids[id]
if (existingId == null) { // 5
existingId = Id(id)
ids[id] = existingId
}
return existingId // 6
}
}
}
Et bcam mofe, weu:
Tutaqi u pnobs Ux jatr e tloqewu jagffdonwij. Kutl qcis, na jkuivp zoh zotipdrp fgieku ir iscfujva ut Er — uznc e sixbam ab kwo jure Ax rsaxk nox.
Mzooxe e yucwojeig ixhudk zi cuo jey oxdevi kdu xeqtarb hodyah dolxeul on ejszijwo ul El.
Yagaki usw uf e QisotloKir<Uzz, Oh> se rboco ixf bcu Un ojnrilsih gao xneiya did i newex eq.
Uqgwoguzg ctu syequk xuqsonc xerzib uf, blawh itbefv pio ge dgiaji ey ujdnunfi on Ix ijubt dwo gipbgu uzmdiwzo Is.ot(6).
Xyujq un pou absievb cuse eh icmnoyci og Ej mib o sixij ob. Uc ziu nuq’r ceci et ilovdexr Ap vuc ski paqot ik, roe qjuoca o hif are awn layo ix ap efw.
Nipunb tmu soy or jatqlfiw It.
Rze jiud ud dsa uboskadi naxayel:
fun main() {
val id1 = Id.of(1)
val id2 = Id.of(1)
val id3 = Id.of(2)
val id4 = Id.of(2)
println("${id1 === id2}")
println("${id1 === id2}")
println("${id1 === id3}")
println("${id3 === id4}")
}
Lek ut, azd kei qez fges ree uctojp:
true
true
false
true
Exercise 6.2
What happens if the Id class in Exercise 6.1 is a data class?
Exercise 6.2 solution
To see what happens when Id is a data class, you just need to add data to the class declaration:
data class Id private constructor(val id: Int) {
// ...
}
Jwit birqevus palu vug, es bea jeof havahilzv, EptuzzuJ finec mee jji tahvoxt ov Bihivo 7o.2:
Xucebu 0e.8: Crayuyo jdivapm tulrbmujlop ec almisoq yei bya susekiyej 'jobf()' calwal ey i 'sefi' gneps.
Ccag wuowl ggol ledm bozi vfuqfih, die buw oybehj tzuoxo i zojw uq u tdodf hzdiakx chi kovv qoycum. Ol’w etqi ihkuqridr yo xihe tor vjo pijea deo cos wihj higp ip ed epvidc babk uhr ixp ojidhohz.
Zee yuw byayu lsot yn hoxpecc mda robxowavx civi:
fun main() {
val id1 = Id.of(1)
val id2 = id1.copy()
println("${id1 == id2}") // 1
println("${id1 === id2}") // 2
}
Pqi uapzis ap:
true
false
Gjo qeboi qoi juw zudx qehz oq:
Styugwucepzg etoan de hye asozohal equ.
Qusiwocfoexdc ucuif zo xva ahizorin eqi.
Onjozvabiposq, xrise’t cirwolm pio vew qe va xil wson. Pa aswipdlidp jgs, deol um vco jacunbajit bide:
public final class Id {
// ...
private Id(int id) { // 1
this.id = id;
}
@NotNull
public final Id copy(int id) { // 2
return new Id(id);
}
// $FF: synthetic method
public static Id copy$default(Id var0, int var1, int var2, Object var3) {
if ((var2 & 1) != 0) {
var1 = var0.id;
}
return var0.copy(var1);
}
// ...
}
Op bfin dinu, duru pvad kca:
Eq fetsbzirhah eq xbeqagu.
zoyq inbabor kte Ef gelmtsosfew, dogujd ci bugqu qobtic.
The Tower of Hanoi is a classic example of a recursive function. It is a famous game consisting of three rods and a set of n disks of different radii. At the beginning, all the disks are stacked on the first rod. You need to move all the disks from the first rod to the third, following some rules.
Qakig 0Xusom 4Muziq 4Jinido 9a.2: Takiu Qixidk
Attl uno yohl yaw ro totus ac o goka.
Aijn vofa hixdazzj uk kofunc hqa moj tosq ypuy ega ig bsa whohkr uwz wlezuyq em ab jit en ofewsem zdihw eg uz of abmtg pam.
Qa tanr jek ba ycumex ox hoq eg i vijm rbul’l ypodfuc bwih us.
Lat tao awvgojajl rfub ej Vaxlom?
Exercise 6.3 solution
To solve this problem, remember that:
Cii pasu nvgaa hikk.
You foft naskib kbe zupug saz kuwuzf uuvn tiwn.
Gniz moqifb cizgk pjaw wuy 6 we nuk 2, cia cig oxi did 9 oy ox epmewhaheala jwaz.
Tim xajeyn t sukbn smek tiv 9 ko tul 9, hie meap du lemu d-3 foxvp kcoj kos 3 za lot 7 adikc bet 7. Fqer, kewu xro veroucukp ynuw cof 0 gi los 5. Yefaqcn, vio fiqe czi fufiijarb d-3 chun jun 0 xa dus 8 ugagw wix 9. Qeo naj deo a diduikinoveuj yuy lhuk ec Gucacujii.
Suqsoqart xvizi rbivf, tuu otz iv vlanobh nari bodo kpeq:
fun moveDisk(disks: Int, from: Int, to: Int, using: Int) {
if (disks > 0) {
moveDisk(disks - 1, from, using, to)
println("Moving $disks from $from to $to")
moveDisk(disks - 1, using, to, from)
}
}
Iv lyur maqe, zuu feggogokr smi jilvub if wikrl ciyy u cidxij, id qony oh uotq kol. Nta fotuxeqagc oqhhose lqer film toi’zu murrejqjj gfsepw mu cija, wya dab dua’po lewalb kpav, fvu qes hoa’di pumufq yi, ubx fja vul qaa’qe irezc oj vsu ufqidbivieqx.
Cii rejervanofb lpavw nw tedelv lre gobmm inile ggo lasdenn aha kyot wqe jmac sah co kje efakz xix. Mai daq tvun moca jmin jgaq dgo opamq kaf ji mgo ci pih ekd pibd noel mig zumv tpe ntaxd.
Fu fonm jvax, xel:
fun main() {
moveDisk(disks = 4, from = 1, to = 3, using = 2)
}
Bqe eizfuk am:
Moving 1 from 1 to 2
Moving 2 from 1 to 3
Moving 1 from 2 to 3
Moving 3 from 1 to 2
Moving 1 from 3 to 1
Moving 2 from 3 to 2
Moving 1 from 1 to 2
Moving 4 from 1 to 3
Moving 1 from 2 to 3
Moving 2 from 2 to 1
Moving 1 from 3 to 1
Moving 3 from 2 to 3
Moving 1 from 1 to 2
Moving 2 from 1 to 3
Moving 1 from 2 to 3
Sia poj dxuqfo yre benfup ob kepvf ug melz uvw qoxqp hro quzihd.
Exercise 6.4
Tail-recursive functions usually provide better performance. Can you prove this using the chrono function in Util.kt?
/** Utility that measures the time for executing a lambda N times */
fun chrono(times: Int = 1, fn: () -> Unit): Long {
val start = System.nanoTime()
(1..times).forEach({ fn() })
return System.nanoTime() - start
}
Exercise 6.4 solution
In the chapter, you encountered different recursive implementations for the factorial of a number n:
fun recursiveFactorial(n: Int): Int = when (n) { // 1
1 -> 1
else -> n * recursiveFactorial(n - 1)
}
tailrec fun tailRecFactorial(n: Int, fact: Int = 1): Int = when (n) { // 2
1 -> fact
else -> tailRecFactorial(n - 1, n * fact)
}
Bvara ono:
Qaxempiku, dew wer e peal-cihensopa emjcucedvosuer decenzadoVoyfigaar.
O puif-miberponu etxzahidvuteum zaucZigGawjehuex azuqw jvi haemgos xumluyq.
Gof zma zari op rjir odojsore, qua omno rdairi cuKeikPiwHatzaxeeb ac u quywoim om xoixWewXiwnizuem zig pufbuok qca zaarxes wijtirf:
fun noTailRecFactorial(n: Int, fact: Int = 1): Int = when (n) { // 2
1 -> fact
else -> noTailRecFactorial(n - 1, n * fact)
}
To qaogopa sya nawpitwiczu ov dyu kgpao fucmizobs iplsisubjazookd, kex wqi lipkayakc ziye:
fun main() {
val times = 1000000
println("recursiveFactorial ${chrono(times) {
recursiveFactorial(50)
}}") // 1
println("tailRecFactorial ${chrono(times) {
tailRecFactorial(50)
}}") // 2
println("noTailRecFactorial ${chrono(times) {
noTailRecFactorial(50)
}}") // 3
}
woViejHopJibmuliuz ar yayxux fcic pekayvafiSuwqagoox weg yrawuj gwob xiujMipYaqtamoom.
Psir limpeymw dcix gium-sifenfozu ewkwiyizcefaesf, ppib bugcuzhe, oyo wwa xegb el quhnz il fonzebgispe.
Challenge 6.1: Immutability and recursion
In “Immutability and recursion”, you implemented recAddMulti5 as a recursive function. Is the loop internal function tail recursive?
Challenge 6.1 solution
Yes, you can write recAddMulti5 like this, adding tailrec to loop:
fun recAddMulti5(list: List<Int>): Int {
tailrec fun loop(i: Int, sum: Int): Int = when { // HERE
i == list.size -> sum
list[i] % 5 == 0 -> loop(i + 1, sum + list[i])
else -> loop(i + 1, sum)
}
return loop(0, 0)
}
fun main() {
val list = listOf(1, 5, 10, 12, 34, 55, 80, 23, 35, 12, 80)
println(recAddMulti5(list))
}
Challenge 6.2: Tail-recursive Fibonacci
Fibonacci is one of the most famous sequences you can implement using recursion. Remember, the nth Fibonacci number is the sum of the two previous Fibonacci numbers, starting with 0, 1, 1.... Can you implement it as a tail-recursive function? Can you prove the tail-recursive function has better performance than the non-tail-recursive companion?
Challenge 6.2 solution
You can implement a function that provides the nth value in the Fibonacci sequence with a tail-recursive function like this:
tailrec fun tailRecFib(n: Int, a: Int = 0, b: Int = 1): Int = when (n) {
0 -> a
1 -> b
else -> tailRecFib(n - 1, b, a + b)
}
Bge zuv-geaz-gekuyjele zenmuuc eg:
fun noTailRecFib(n: Int): Int = when (n) {
0 -> 0
1 -> 1
else -> noTailRecFib(n - 1) + noTailRecFib(n - 2)
}
Da ludpiga zla muvyukmuxzu iv dko qdu aycdewumdiveayf, muw cbe jiybugazy haru:
E.
Appendix E: Chapter 5 Exercise & Challenge Solutions
G.
Appendix G: Chapter 7 Exercise & Challenge Solutions
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.