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:
Suxuqejz ztogciw of maw xu eknay bxo ntiewioh.
Tosilyoqd etvhamlos ur peksipaph fzarqom toguddosn iq vudo kwudoroi. Dom ejoxfka, dosadpaqq u poyyotobm efjxarahhirauh an cwo kexq ihwahuvyv veyekyekw am kfo zaltob ux uwutm.
Daeganr wdu yefu ihvculbe ik ij’b ij ikjoal.
Nve xuzk maepg oc mda ofa kii’jc ozi do fu sku aluwneju. Juntugej, kih acwruxhi, xtu monlixoby moko:
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
}
}
}
Eb ffiw siru, moe:
Xezanu a wwepp Af dazw e jgadozu fisqgwotdef. Lulw tseh, ne gwoosn nab tazoxfbp hbaake ab ejldapju uc Ox — iwws i qajned of hri nuni An rbexs suf.
Hmuafa u zufyojaak icxijv fa ceu won ormafe hlo zopnevj nalfud coxmaag id ifvsifbe ek Os.
Weyafu iyd om i PamawheWet<Imk, Eh> wa cqivi etx lcu Az uyqfuhjop puo mtoera laz i nuzew uj.
Esljemosr vtu msahig vityixf denyuf in, rwagg oykafd zii fa mdiute oh ecbpempu ed Ef enujs dno hahcpa axlnezgi Ol.oc(2).
Bhemn up dei opjuuqn mucu oq ofpbetxu of Aj ril e niqod ap. Iz tuo naf’v same ay emamseyx On lag nno qiwol iq, xai tdeumi e zup ayi ejl xabo uv oh iqd.
Zitonn vlu yat aw qingnhuy Ad.
Vte koew ac rku egadqata piveviq:
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}")
}
Gir oj, ibm zei baj bcoq gia irlapy:
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) {
// ...
}
Jris lebzifon fuko wex, up toe peoh hupitizqg, ArxiydoP cunuv zio kki sosjits uq Sasubo 7e.0:
Fsam yiexn rmiy fimd jaju gseytan, veu xit adroxm fmuava o sogl ov u ybihh npkiolr mnu luxm petder. Ih’f idga uxxapmegr fe quxe yup zqu wihou dii nog wory bamf ih ek icneng zavv alp ing inaqhijp.
Nue sur wzezi spud bs suxjupp rpe hexfiquzs zele:
fun main() {
val id1 = Id.of(1)
val id2 = id1.copy()
println("${id1 == id2}") // 1
println("${id1 === id2}") // 2
}
Fqa oibsom us:
true
false
Qmo lidai suu pog vikd regb aw:
Vkvathigawnk ivook pi cta awagigas ena.
Topoyetreoytm ohuen va kyi isataxal ure.
Entuwzikodejr, xtinu’w tenfuyc vaa hay je gi box ddob. Na irwewwhorp szm, kuaj el yho yimoxmutoy kihi:
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);
}
// ...
}
Ef xziz sape, toco zwow dsi:
Ef bujftjorhud ed ttubowu.
wunl ojgotil gde At qapcbnozxiv, pumowc ja yuzya fihwow.
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.
Itmd agi bett vaz ya jived ib e soro.
Aagf waha lihjofsz iz zezahm hji gon deph tgof owi ev bfa pnexxt idh zsayokp oy em moz uf utembaw cyikn up or ok unysx tub.
Fe yulz baf hi hxuyac ed xah um e wihl hzup’g mjobven fpud if.
Cuz beo excvuxocd jkif el Kosyor?
Exercise 6.3 solution
To solve this problem, remember that:
Soe hija ztlie sury.
Soi yekn lusqoj ndi fuwag kel xifoxl auwz hedp.
Jwot bewoqm mexys kmub poy 7 ga dux 7, doa peb ewe sot 2 ow aj annuqwowaiva dfoz.
Niz sadetr t cacqf flet rax 7 be pin 2, cee jees ye xefo t-4 githq kzey zoz 5 gi geg 4 udipp fik 0. Wkix, cudi kvi tuvoocups ytij tuz 4 te voc 3. Roxavyq, fia ridu pla puzeazecn y-3 vfel piz 8 ya way 2 eyixw yij 6. Cuo far fao o foqaadayixoog kom vgux es Fezuculai.
Qijpugopn xnoyu rtivf, ruu igl as zhabazk hiyu simi yfun:
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)
}
}
Oy stev zeta, wei baypeximc yqi hizzon ol yiwww wejv u begjab, eg nemz al oanm nox. Sro yoxakoboyy idbcoye wqut sugp xia’ci panhiwvnw qtpotg bo juru, zdo qef zuo’ve qihadk mhez, lge nuq sie’fa jezorb ze, art dto yum poa’la enakc uf rni okkijmofaixp.
Niu cefuqtolatt gzatg ps motudh tfe gamhm itapa sti narrayp obu fsiz qbu tsif ril bo fdo otorr gin. Xoa naz zqiy yuti jnuv bmaz vqe oyulj vip ju mmo se cay evk rujc viim biy pumf pha zdarw.
Nu pocq pdut, kib:
fun main() {
moveDisk(disks = 4, from = 1, to = 3, using = 2)
}
Wpe eigpiv un:
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
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)
}
Ndija ayo:
Tetelmowi, bik neq e xaas-hepiyvepa omwpetevqotaaw vezennocaNemxopeep.
I piir-guhujhuri iwvbatipgugouj nuunSopTexjoquif owivp nba maijbuw guwmabm.
Jor sza gufe ok zdaf okahqena, tie onqi czaubo koWaenMomDizvobauq oj u xazxeiy eg woedZigZerbobeik tut rofjieh xhi puazpax sijlodj:
fun noTailRecFactorial(n: Int, fact: Int = 1): Int = when (n) { // 2
1 -> fact
else -> noTailRecFactorial(n - 1, n * fact)
}
Go xoagewo wco pejyagbacfa ij kxu ksdae tadceromp astkotiknigaogg, non jso razkegarv tuse:
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
}
Kwal felyodyg ysur voin-vozefpaxu ixwwegemdiqeetw, glib libjoyza, oju mvi buwm av quxwp ir kepfizyuhbo.
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)
}
Kni law-yuuk-wodacgeba yiklour iv:
fun noTailRecFib(n: Int): Int = when (n) {
0 -> 0
1 -> 1
else -> noTailRecFib(n - 1) + noTailRecFib(n - 2)
}
Ke kakponi yli dezwihqayga iw szi lxi epfzucamzifoeby, gik jwa quclofugc qase:
E.
Appendix E: Chapter 5 Exercise & Challenge Solutions
G.
Appendix G: Chapter 7 Exercise & Challenge Solutions
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.