Les funcions es poden tractar com un tipus de dades.
Introducció
A Funció vas aprendre a declarar funcions amb la paraula clau fun.
Com que les funcions també són tipus de dades les pots desar en variables, passar com a paràmetres a altres funcions, retornar-les com a valor de retorn, etc.
Referenciar una funció
A continuació tens la funció hello:
fun
fun : String return "Hello"
}
}En lloc de cridar la funció hello directament, la pots desar en una variable perquè una funció és un objecte com pot ser un 3 o "Hello".
Et pots referir a la funció hello com un valor amb l’operador de referència de funció (::):
fun
fun : String return "Hello"
}
val hi
}La constant hi té tipus () -> String:
val hi: No inclous els parèntesis després de hello perquè vols desar la funció en una variable, no pas cridar-la.
Copia el valor de la variable hi a una nova variable hey.
Crida la “funció” hey.
Show solution
fun
fun : String return "Hello"
}
val hi
val hey
}Referenciar no és executar
A continuació tens la funció ego que entra en bucle infinit:
fun
fun while
}
}
}Si executes aquesta funció, es comporta d’una manera bastant egocèntrica, es queda el programa només per ella:
Ego is egocentric, only I 👺 !
Ego is egocentric, only I 👺 !
Ego is egocentric, only I 👺 !
...Si vols pots referenciar la funció ego: referenciar no és executar!
fun while
}
}
val self
Pots verificar que en aquest codi la funció ego no s’ha executat:
No ego!High-order functions
Una funció pot acceptar una funció com a paràmetre.
Per exemple, la funció newList accepta com a segon paràmetre una funció de tipus (Int) -> Int:
fun : val newList for val newItem newList.
}
return newList
}La funció newList tornarà una llista nova en què a tots els seus elements seran …
val list Qui sap, només ho sap toto! 🤔
Per exemple, toto podria ser aquesta funció:
fun : Int return a + 5
}
val list
O aquesta altra:
fun : Int return a * 5
}
val list
Funció anònima
Una funció anònima és aquella que no té nom.
S’utilitza quan la funció només es fa servir com a paràmetre d’una altra funció,
Per exemple, en lloc d’escriure:
fun : Int return a + 5
}
val list
Pots assignar directament la funció a una variable:
val toto return a + 5
}I executar la funció referenciada per la variable toto com qualsevol altra funció:
Ara la funció no té nom, només variables que referencien la funció:
val tata
val list Per tant, pots passar directament una funció anònima com paràmetre d’una funció:
val list ,
: Int return a + 5
}
)
Funció genèrica
Les funcions poden tenir paràmetres genèrics, que s’especifiquen amb claudàtors angulars <> abans del nom de la funció.
La funció newList funciona molt bé amb Int, però no passa per a qualsevol altre tipus de dada.
La solució és fer-la genèrica per a qualsevol mena de dada parametritzant la funció newList amb T:
fun <T> : val newList for val newItem newList.
}
return newList
}Ara ja la pots utilitzar amb String, per exemple:
val list ,
: String return name.
}
)
O un data class definit per tu:
val list ,
: Person return person.
}
)
Una altra millor és que la funció newList pugui acceptar una funció f que rep com a parametre un tipus de dada i pugui retornar un tipus de dada diferent.
Per tant, l’has de parametritzar amb dos paràmetres T i R:
fun <T, R> : val newList for val newItem newList.
}
return newList
}Ara la funció toto té llibertat per tornar un tipus de dada diferent:
val list ,
: String return person.name
}
)
Però encara queda l’última millora que pots fer … Que accepti una funció que pot tornar valors nuls:
🥳 👻 😺
fun <T, R> :
val newList for val newItem if newList.
}
}
return newList
}La signatura de la funció és una mica T i R, però a banda d’això la implementació molt comprensible.
Ara si volem un llista amb el nom de totes les persones majors de 30 anys:
val list ,
: String? return if person.name else null
}
)
I pots verificar que pot tornar una llista buida si no hi ha persones majors de 50 anys:
val list ,
: String? return if person.name else null
})
Expressió lambda
La funció toto és una funció d’un sol ús, contradient el fet que les funcions haurien de ser codi reutilitzable (que es fa servir moltes vegades).
Tampoc és un bloc de codi molt llarg que necessiti separar-se per fer el codi llegible.
Aquest tipus de funcions són molt habituals i es poden escriure de manera més concisa amb les expressions lambda.
fun
val hi
}val toto
val list
I com tota variable, aquesta es pot passar en línia si només s’utilitza una sola vegada:
val list ,
)
Sintaxis
La forma sintàctica completa de les expressions lambda és la següent:
val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }-
Una expressió lambda sempre va envoltada per claus.
-
Les declaracions de paràmetres en la forma sintàctica completa van dins de les claus i poden tenir anotacions de tipus opcionals.
-
El cos va després de
->. -
Si el tipus de retorn inferit de la lambda no és
Unit, l’última (o possiblement única) expressió dins del cos de la lambda es tracta com el valor de retorn. -
Si deixes fora totes les anotacions opcionals, el que queda té aquest aspecte:
val sum Activitat
A continuació tens una funció genérica que recòrre tots els elements de la llista i la redueix a un únic valor.
Ella no sap com ha de procedir per fer la reducció, això ho sap la funció f.
fun <T> : T? var result: T? for result }
return result
}Per exemple, si tinc aquesta llista:
val numbers Puc sumar tots els seus elements amb una funció anònima:
val sum return if b else a + b
})
O amb una funció lambda de manera més concisa:
val sum )
Si et fixes, la funció reduce és genial! 😸
Funciona amb una llista d’un sol número:
val numbers val sum )
Inclús amb una llista buida:
val numbers val sum )
😼
Passant lambdes finals
Si l’últim paràmetre d’una funció és una funció, llavors una expressió lambda passada com a argument corresponent es pot col·locar fora dels parèntesis:
val numbers
val max if b else if a else b
}
Aquesta sintaxi també es coneix com a trailing lambdal.
Calcula el valor mínim de la llista:
val numbers Show solution
Si la lambda és l’únic argument en aquesta crida, els parèntesis es poden ometre completament:
fun
}
fun
})
run ->
}
run
}
}L’última sintaxi és la més concisa, però si no saps d’on ve no pots entendre que és una funció lambda que s’està passant com argument a la funció run().
it: nom implícit d’un únic paràmetre
És molt habitual que una expressió lambda tingui només un paràmetre.
Al principi has creat la funció newList:
fun <T, R> : val newList for val newItem newList.
}
return newList
}Si el compilador pot analitzar la signatura sense cap paràmetre, no cal declarar el paràmetre i es pot ometre ->.
El paràmetre es declararà implícitament amb el nom it:
val numbers
val result
Utilitza una funció com a tipus de retorn
Una funció és un tipus de dada, així que la pots fer servir com qualsevol altre tipus de dada.
Fins i tot pots retornar funcions des d’altres funcions.
fun : return if
} else
}
}La funció welcome() retorna una funció diferent si maybe és true o false.
La funció que retorna welcome() es pot utilitzar com qualsevol altra funció:
fun
}Activitats
A continuació tens un Map que guarda un conjunt functions que apliquen un descompte en funció del tipus de client:
CustomerTier
fun val discount: ) -> Int> CustomerTier.STANDARD to , // no discount
CustomerTier.SILVER to , // 5%
CustomerTier.GOLD to , // 10%
CustomerTier.VIP to // 15% discount with a cap of $50
val percent val cap
})
}Calcula el descompte d’un client VIP amb un import de $1000:
Show solution
Tens una llista de Int amb possibles valors nuls:
val list: Amb la funció reduce que has escrit abans, suma els elements de la llista.
Show solution
val list: val sum