Treinamento - Kotlin
Publié le 12 May 2022
mapa mental

Programação funcional
Os exercícios são tirados do livro:
Programação funcional em Kotlin por tutoriais
Escrito por Massimo Carli
repositório: https://github.com/kodecocodes/fpk-materials
sintaxe
FirstProgramTest
noções: exibição de tela, função
FirstProgramTest:https://github.com/cheroliv/cheroliv.com/blob/master/codes/src/test/kotlin/programming/FirstProgramTest.kt[source]+
ConcatFunctionTest
noções: memória, variável, valor, objeto, extensão de função
ExampleUnitTest: fonte+
BirthdayMessageTestOutput
noções: conjuntos, laços
Introdução ao Kotlin
BirthdayMessageTestOutput:https://github.com/cheroliv/cheroliv.com/blob/master/codes/src/test/kotlin/programming/BirthdayMessageTestOutput.kt[fonte]
Curso de kotlin
Básicos funcionais
Abordagem declarativa X imperativa
package functional
import kotlin.test.Test
import kotlin.test.assertEquals
class DeclarativeTests {
val input = listOf(
"123", "abc", "1ds", "987", "abdf", "1d3", "de1", "88", "101"
)
fun imperativeSum(list: List<String>): Int {
var sum = 0
for (item in list) {
try {
sum += item.toInt()
} catch (_: NumberFormatException) {
}
}
return sum
}
@Test
fun `test imperative approach`() {
imperativeSum(input).run {
println("Sum $this")
assertEquals(1299, this)
}
}
fun isValidNumber(s: String) = try {
s.toInt()
true
} catch (_: NumberFormatException) {
false
}
fun declarativeSum(list: List<String>) = list
.filter(::isValidNumber)
.map(String::toInt)
.sum()
@Test
fun `test declarative approach`() {
assertEquals(1299, declarativeSum(input).apply {
println("Sum $this")
})
}
}
Exercício 1.1
Implementar a função sumInRange, que soma os valores em
uma List<String> dentro de um intervalo dado. A assinatura é:
fun sumInRange(input: List<String>, range: IntRange): Int
@Test
fun `Exercise 1_1`() {
assertEquals(4, sumInRange(
listOf("1", "10", "a", "7", "ad2", "3"),
1..5
).apply { println("sumInRange 1..5: $this") }
)
}
Experimente e verifique sua resposta com ahttps://github.com/cheroliv/cheroliv.com/blob/master/codes/src/test/kotlin/functional/DeclarativeTests.kt[solution, windows="_blank].
Funções de ordem superior
package functional
import java.io.ByteArrayOutputStream
import java.io.PrintStream
import java.lang.System.*
import java.lang.Thread.sleep
import kotlin.math.sign
import kotlin.test.Test
import kotlin.test.assertEquals
class BasicsHOFTests {
val ONE_SECOND = 1000L
@Test
fun `high order function`() {
//capture de la sortie standard
val standardOut: PrintStream? = out
val outputStreamCaptor = ByteArrayOutputStream()
setOut(PrintStream(outputStreamCaptor))
3.times { println("Hello") }
assertEquals(
buildString {
repeat(3) { append("Hello\n") }
deleteAt(length - 1)
}, outputStreamCaptor
.toString()
.trim()
)
//libération de la sortie standard
setOut(standardOut)
}
fun Int.times1(fn: () -> Unit) {
for (i in 1..this) {
fn()
}
}
fun Int.times2(fn: () -> Unit) {
for (i in 1..this) fn()
}
fun Int.times3(fn: () -> Unit) =
(1..this).forEach { fn() }
fun Int.times4(fn: () -> Unit) =
repeat((1..this).count()) { fn() }
fun Int.times(fn: () -> Unit) =
(1..this).forEach { _ -> fn() }
}
Exercício 1.2
Implementar chrono, que aceita uma função do tipo`() →`
Unidade na entrada e devolve o tempo gasto para executá-lo. A assinatura é :
fun chrono(fn : () -> Unité) : Long
@Test
fun `Exercise 1_2`() {
val waitOneSec = { sleep(ONE_SECOND) }
chrono(waitOneSec).apply {
println("chrono: $this")
assertEquals(1, sign)
}
}
Experimente-o e verifique sua resposta com ahttps://github.com/cheroliv/cheroliv.com/blob/master/codes/src/test/kotlin/functional/BasicsHOFTests.kt[solução, windows="_blank].
Composição
package functional
import kotlin.test.Test
import kotlin.test.assertEquals
fun double(x: Int): Int = 2 * x
fun square(x: Int): Int = x * x
fun squareAndDouble1(x: Int) = double(square(x))
infix fun <A, B, C> ((A) -> B).compose(g: (B) -> C)
: (A) -> C = { a -> g(this(a)) }
class CompositionTests {
@Test
fun composition_impure() {
assertEquals(200, double(square(10)))
assertEquals(200, squareAndDouble1(10))
}
@Test
fun composition_pure() {
val squareAndDouble = ::square compose ::double
assertEquals(200, squareAndDouble(10))
}
}
Funções puras e testabilidade
package functional
import java.io.ByteArrayOutputStream
import java.io.PrintStream
import java.lang.System.out
import java.lang.System.setOut
import kotlin.test.Test
import kotlin.test.assertEquals
var count = 0
//impure car une variable global subit un effet de bord
fun impure(value: Int): Int {
count++
return value + count
}
//impure car utilisation de la sortie standard qui fait muter le system
fun addOneAndLog(x: Int): Int {
val result = x + 1
println("New Value is $result")
return result
}
//pure
fun addOne(x: Int) = (x + 1).run {
Pair(this, "New Value is $this")
}
class PureTests {
@Test
fun `impure fonction`() {
assertEquals(3, impure(2))
val standardOut = out
val outputStreamCaptor = ByteArrayOutputStream()
setOut(PrintStream(outputStreamCaptor))
addOneAndLog(3)
assertEquals(
"New Value is 4",
outputStreamCaptor
.toString()
.trim()
)
setOut(standardOut)
}
@Test
fun `pure fonction`() {
addOne(3).run {
assertEquals(4, first)
assertEquals("New Value is 4", second)
}
}
}
Tratamento de exceções
package functional
import org.junit.jupiter.api.assertThrows
import kotlin.Result.Companion.failure
import kotlin.Result.Companion.success
import kotlin.test.Test
import kotlin.test.assertEquals
//NumberFormatException est un effet de bord qui rend la fonction impure
fun strToInt(str: String) = str.toInt()
//pure
fun strToIntOrNull(str: String) = try {
str.toInt()
} catch (nfe: NumberFormatException) {
null
}
//pure avec gestion de l'exception plus élégante
fun strToIntResult(str: String): Result<Int> =
try {
success(str.toInt())
} catch (nfe: NumberFormatException) {
failure(nfe)
}
class ExceptionHandlingTests {
@Test
fun impure() {
assertThrows<NumberFormatException> { strToInt("foo") }
assertEquals(1, strToInt("1"))
}
@Test
fun pure() {
assertEquals(null, strToIntOrNull("foo"))
assertEquals(1, strToIntOrNull("1"))
}
@Test
fun `pure avec result`() {
assertEquals(1, strToIntResult("1").getOrNull())
assertEquals(
"For input string: \"foo\"",
strToIntResult("foo")
.exceptionOrNull()
?.message
)
}
}
Pontos-chave
-
Enquanto a programação orientada a objetos significa programar com objetos,
programação funcional significa programar com funções. Você decompõe um problema em vários subproblemas, que você modela com as funções
-
As funções de ordem superior aceitam outras funções como entrada ou retornam outras
funcionam como valores de retorno. A teoria das categorias é a teoria da composição, e você a usa para entender como compor suas funções em um programa de trabalho. O valor de saída de uma função pura depende apenas dos seus parâmetros de entrada, e ela não tem efeitos colaterais
-
Um efeito colateral é algo que uma função faz no mundo exterior. Este
pode ser um log na saída padrão ou a modificação do valor de uma variável global. A programação funcional funciona para as funções puras, mas ela fornece também os ferramentas para transformar funções impuras em funções puras. Você pode tornar uma função impura pura movendo os efeitos para torná-los parte do valor de retorno.
-
Programação funcional é uma questão de composição.
Gerenciamento de erros é um caso típico de efeitos colaterais, e Kotlin lhe fornece as ferramentas para gerenciá-los de forma funcional
Aprofundar java8: lambdas expressões e interface funcionais
Teoria das categorias
A teoria matemática das categorias:
Fundamentos das funções
Exercício 2.1
Pode escrever um exemplo de função mapeando valores distintos?
cujo domínio assume valores não distintos no intervalo, como f(b) e f(c) na figura abaixo ?
Experimente, depois verifique o projeto do desafio para uma solução para ver como você fez.
Você encontrará conselhos e uma explicação seguindo o link para a solução.
Exercício 2.2
Você pode escrever a função inversa de duas vezes ?
Quais são o domínio e a imagem da função inversa?
Confira o projeto do desafio e o Apêndice B para a solução.
fun chrono(fn : () -> Unité) : Long
@Test
fun `Exercise 1_2`() {
val waitOneSec = { sleep(ONE_SECOND) }
chrono(waitOneSec).apply {
println("chrono: $this")
assertEquals(1, sign)
}
}
Experimente-o e verifique sua resposta com ahttps://github.com/cheroliv/cheroliv.com/blob/master/codes/src/test/kotlin/functional/BasicsHOFTests.kt[solução, windows="_blank].
