¿Cuál es la importancia de las colecciones en lenguajes funcionales?
En el mundo de la programación funcional, las colecciones juegan un papel crucial. Comprender cómo funcionan las listas, conjuntos y mapas en estos lenguajes, como Scala, es fundamental para aprovechar al máximo sus capacidades. A diferencia de su contraparte en lenguajes tradicionales, las estructuras de datos en lenguajes funcionales son inmutables, lo que significa que no se pueden modificar una vez creadas. Esto implica que todas las operaciones devuelven nuevas instancias, manteniendo las originales sin cambios, lo cual es esencial para garantizar la integridad y seguridad del código.
¿Qué son las listas y cómo se trabajan en Scala?
Las listas en Scala son una de las principales formas de representar secuencias de datos. A diferencia de los arrays, que son mutables y están diseñados para compatibilidad, las listas en Scala son inmutables, lo que garantiza que una vez creadas, no se puedan modificar. Esto implica que al realizar operaciones, como agregar elementos, el resultado es una nueva lista sin alterar la original. Scala utiliza funciones como map, filter y foreach para iterar y manipular datos de manera funcional, evitando los bucles tradicionales como for o while.
val a1 = Seq(1,2,3)val a2 = a1 :+4// Crea una nueva lista agregando el 4
¿Cómo se diferencian los conjuntos y las listas?
Los conjuntos en Scala, implementados por la clase Set, se parecen a las listas, pero siguen las reglas de los conjuntos matemáticos. Permiten realizar operaciones como intersección y unión de manera nativa. Los conjuntos también son inmutables, lo que significa que las operaciones como agregar un elemento crean un nuevo conjunto.
val c1 = Set(1,2,3)val c2 = c1 +4// Crea un nuevo conjunto incluyendo el 4
¿Qué papel juegan los mapas en Scala?
Los mapas, conocidos como diccionarios en otros lenguajes, permiten almacenar datos en pares clave-valor. En Scala, los mapas son inmutables y se definen mediante la clase Map. Al igual que las listas y conjuntos, los mapas en Scala no se modifican directamente. Las claves y los valores deben proporcionarse como pares, y cualquier operación que implique agregar elementos regresará un nuevo mapa.
val m1 = Map(1->"hola")val m2 = m1 +(2->"hello")
¿Cómo se manipulan las colecciones usando funciones?
El manejo de colecciones en Scala se realiza mediante funciones internas y librerías. La función map permite aplicar una función a cada elemento de una colección, transformándola según sea necesario. Las funciones lambda o anónimas son comunes en estas operaciones, lo que facilita la manipulación funcional de listas, conjuntos y mapas.
val a2Incremental = a2.map(x => x +1)// Incrementa cada valor en la lista
Reto para mejorar tus habilidades
Para fortalecer tu comprensión, te invitamos a crear un grupo de funciones que calculen la media, mediana y moda de una lista de números, y devuelvan los resultados en un mapa. Esta práctica reforzará tus conocimientos sobre la manipulación de colecciones usando funciones en Scala, un paso relevante en tu camino hacia la maestría en lenguajes funcionales. ¡Adelante y mantén la curiosidad por el aprendizaje!
object colecciones { def main(args:Array[String]):Unit={ val lista:Seq[Int]=Seq(1,3,5,7,9,9,11,17,20,20,100)medidasT(lista)}// funciones def media(args:Seq[Int]):Int={ val media =(args.sum)/(args.size) media
} def mediana(args:Seq[Int]):Int={ val indice =(args.size/2)args(indice)} def moda(args:Seq[Int]):Int={ args
.groupBy(x => x).maxBy(x => x._2.size)._1}// funcion de medidas de tendencia central def medidasT(args:Seq[Int]):Unit={ val lista = args.sorted val mapa =Map("media"->media(lista),"mediana"->mediana(lista),"moda"->moda(lista)) mapa foreach( x =>println(x._1+"->"+ x._2))}}
Solo unas correcciones:
La mediana para una lista de tamaño par es la media de dos numeros.
La media no siempre es entero.
La moda puede ser mas de un numero.
Aqui dejo una correccion del codigo :)
import scala.util.Randomobject CentralTendencyextendsApp{ def main():Unit={ val r =Random val lista:Seq[Int]=for(i <-1 to 10)yield r.nextInt(10)medidas(lista)} def mediana(args:Seq[Int]):Float={ val tamano = args.size val indice = args.size/2if(tamano%2==0){(args(indice-1).toFloat+args(indice).toFloat)/2}else{args(indice)}} def media(args:Seq[Int]):Float={ val mean = args.sum.toFloat/args.size.toFloat mean
} def moda(args:Seq[Int]):Seq[Int]={ val mapaFreq = args
.groupBy(identity).mapValues(_.size) val maxVal = mapaFreq
.valuesIterator.max val mode =(mapaFreq
.filter(_._2== maxVal).view.map{case(k,v)=> k}.toList) mode
} def medidas(args:Seq[Int]):Unit={ val lista = args.sorted val mapaCT =Map("media"->media(lista),"mediana"->mediana(lista),"moda"->moda(lista))println(lista, mapaCT)}
para que se utiliza el ._ y un número?
En la versión actual 2.12, no se dispone de la función appended.
To append or prepend one or more elements to a Vector or Seq, use these methods:
to append one item,use:+to append multiple items, use ++to prepend one item, use +:to prepend multiple items, use ++:
Para complementar tu respuesta use esta tabla que saqué de la siguiente página:
Need Method
append 1 item oldSeq :+ e
append multiple items oldSeq ++ newSeq
prepend 1 item e +: oldSeq
prepend multiple items newSeq ++: oldSeq
Hola Daniel, disculpa para que sirven los _. en el siguiente código:
val moda=(lista:List[Int])=> lista.groupBy(identity).view.mapValues(_.size).maxBy(_._2)._1
Gracias
Hola Adrian.
El _ en estas funciones, puede verse como un "comodín", es decir, es una simplificación que el compilador es capaz de inferir, y así no tener que poner el código completo.
Por ejemplo, el equivalente de mapValues(_.size) sería mapValues(x => x.size).
O en el caso de maxBy(_._2), sería maxBy(x => x._2).
Ahora, cosa distinta son _1 y el _2, estos son los nombres del primer y segundo de una tupla, ya que los elementos de las tuplas no tienen en realidad un nombre (distinto al caso de los case class que sus atributos sí tienen nombre), para acceder a los elementos se usan esos nombres.
val tupla =("primero","segundo")tupla._1=="primero"// truetupla._2=="segundo"// true
Es el tipo de dato más básico en lenguajes funcionales. A diferencia de las listas en otros lenguajes, aquí por defecto son inmutables
List, Seq, Array
Conjuntos
Es muy similar a usar una lista, pero conceptualmente es distinto. Por definición, los elementos en un conjunto no tienen orden, ni pudeden estar repetidos.
Set,
Mapas
También se les conoce como diccionarios. No es muy diferente a lo que ya conocemos, solo que también son inmutables por defecto
Map
Loops en FP
En programación funcional no usaremos un for o un while como se hace en lenguajes imperativos. Por el contrario, usaremos funciones que recorran los elementos de una lista por nosotros.
// Definimos un objeto llamado Estadisticas que agrupa nuestras funciones estadísticas
object Estadisticas {
// Calcula la media (promedio) de una lista de números
def media(nums: List[Double]): Double =
if (nums.isEmpty) 0.0 // Si la lista está vacía, regresamos 0.0
else nums.sum / nums.length // Sumamos todos los números y dividimos entre la cantidad
// Calcula la mediana de una lista de números
def mediana(nums: List[Double]): Double = {
val sorted = nums.sorted // Ordenamos la lista de menor a mayor
val n = sorted.length // Guardamos la cantidad de elementos
if (n == 0) 0.0 // Si la lista está vacía, regresamos 0.0
else if (n % 2 == 1) sorted(n / 2) // Si la cantidad es impar, la mediana es el del medio
else (sorted(n / 2 - 1) + sorted(n / 2)) / 2.0 // Si es par, es el promedio de los dos del medio
}
// Calcula la moda (el número que más se repite) de una lista de números
def moda(nums: List[Double]): Double = {
if (nums.isEmpty) 0.0 // Si la lista está vacía, regresamos 0.0
else nums.groupBy(identity) // Agrupamos los números iguales
.mapValues(_.size) // Contamos cuántas veces aparece cada número
.maxBy(_._2)._1 // Buscamos el número con más repeticiones y lo regresamos
}
// Calcula todas las estadísticas y las devuelve en un Map
def calcularEstadisticas(nums: List[Double]): Map[String, Double] = Map(
"media" -> media(nums), // Llave "media" con el resultado de la función media
"mediana" -> mediana(nums), // Llave "mediana" con el resultado de la función mediana
"moda" -> moda(nums) // Llave "moda" con el resultado de la función moda
)
}
// Este objeto es el punto de entrada para ejecutar el programa
object Main {
def main(args: Array[String]): Unit = {
val numeros = List(1.0, 2.0, 2.0, 3.0, 4.0) // Creamos una lista de números
val resultado = Estadisticas.calcularEstadisticas(numeros) // Calculamos las estadísticas
println(resultado) // Imprimimos el resultado, por ejemplo: Map(media -> 2.4, mediana -> 2.0, moda -> 2.0)
}
}
val mediana = ("Mediana:",Ejercicios.mediana(lista))
val moda = ("Moda:",Ejercicios.moda(lista))
val respuesta = Map((media),(mediana),(moda))
print(respuesta)
Se agradece la critica constructiva :D
import scala.util.Randomdef getMean(sample:Seq[Int]):(String,Double)=if(sample.isEmpty){("mean"->0.0)}else{("mean"-> sample.sum.toDouble/ sample.length)}def getMedian(sample:Seq[Int]):(String,Double)={ val sortedSample = sample.sorted val sortedLength = sample.lengthif(sortedSample.isEmpty){("median"->0.0)}elseif(sortedLength %2==1){("median"->sortedSample(sortedLength/2).toDouble)}else{ val middel1 =sortedSample((sortedLength-1)/2) val middel2 =sortedSample(sortedLength/2) val median =(middel1 + middel2).toDouble/2("median"-> median)}}def getMode(sample:Seq[Int]):(String,Double)={ val freqMap = sample.groupBy(identity).view.mapValues(_.size) val maxFreq = freqMap.values.max val mode = freqMap.filter{case(_, freq)=> freq == maxFreq }.keys.head("mode", mode.toDouble)}def getStatistics(sample:Seq[Int]):Map[String,Double]=Map(getMean(sample),getMedian(sample),getMode(sample))val randomeSample =Seq.fill(100)(Random.nextInt(1000))val statistics =getStatistics(randomeSample)println(randomeSample)println(statistics)
Reto !
val numbers =List(1,3,5,7,9,9,11,17,20,20,100) def mediaMedianaModa(numbers:List[Int]):Unit= val media =Math.round(numbers.sum.toDouble/ numbers.length) val numbersOrder = numbers.sorted val mediana =if(numbersOrder.length%2==0){ val mitadSuperior = numbersOrder.length/2 val mitadInferior = mitadSuperior -1Math.round((numbersOrder(mitadInferior).toDouble+numbersOrder(mitadSuperior).toDouble)/2)}else{Math.round(numbersOrder(numbersOrder.length/2).toDouble)} val frecuencias = numbers.groupBy(identity).view.mapValues(_.size) val maxFrecuencia = frecuencias.values.max val moda=frecuencias.filter(_._2== maxFrecuencia).keys.toListprintln(s"Media:$media, Mediana:$mediana, Moda:${if(moda.size==numbers.size) "no hay moda" else if(moda.size==1)moda(0) else moda} ")mediaMedianaModa(numbers)
object calculo {| def media(x:Seq[Int])={ val l = x.length; val s = x.sum; val result = s / l; result}| def mediana(x:Seq[Int])={val l = x.length; val r = l /2;val result =x(r.round); result}| def getData(x:Seq[Int])={val result =Map("Media: "->media(x),"Mediana: "->mediana(x)); result}|}
Resultado del reto:
package demo
object Demo{// Main Method def main(args:Array[String]){ val nums:List[Int]=List(1,2,3,2,4,4,4) val numsOrdered = nums.sorted// Media val sumaLista = numsOrdered.sum val conteoLista = numsOrdered.size val mediana = sumaLista/conteoLista
val media = conteoLista/2 val moda = numsOrdered.groupBy(identity).maxBy(_._2.size)._1println(Map(("mediana",mediana)))println(Map(("moda",moda)))println(Map(("media",media)))}}
Si queremos hacer una secuencia, lista o colección sin inicializarla pero con los tipos que contendrá definidos podemos usar la siquiente sintaxis.
val a = Seq[Int]()
En ocasiones, queremos declarar una lista vacia que contenga elementos de cierto tipo de dato.
En el siguiente ejemplo se trata de añadir un elemento a una lista vacia y falla.
Sin embargo, al utilizar la sintaxis descrita anteriormente, no tenemos ningún problema.
Este seria el equivalente a los tipos genéricos en otros lenguajes de programación donde se suele utilizar "<>".
También puede utilizarse:
var a : Seq[Int]= Seq()
Por útlimo, en Scala3 podemos utilizar la siguiente sintaxis si quisieramos hacer una colección que contenga más de un tipo de dato.
Aqui va mi reto. Aún no me acostumbro a usar solo valores y no variables. jajajaja Esto de la inmutabilidad es dificil de adoptar jajajaj
import scala.collection.mutableimport scala.util.Randomobject MeasuresextendsApp{ val random =Random val input:Seq[Int]=for(i<-1 to 13)yield random.nextInt(10)varlowest:mutable.PriorityQueue[Int]=mutable.PriorityQueue.empty[Int]varhighest: mutable.PriorityQueue[Int]=mutable.PriorityQueue.empty[Int]varmediana:Double=0varmoda:Seq[Int]=Seq.empty[Int] def media(x:Seq[Int]):Double= x.sum.toDouble/x.size def mediana(x:Int):Unit={ lowest.addOne(x) highest.addOne(-lowest.dequeue())if(highest.size>lowest.size){lowest.addOne(-highest.dequeue())}if(lowest.size>highest.size)mediana=lowest.headelse mediana=(lowest.head-highest.head).toDouble/2} def calculateModa():Seq[Int]={ val max:Int= input.groupBy(identity).maxBy(_._2.size)._2.size input.groupBy(identity).foreach{case(k, v)=>if(v.size== max){ moda=moda.appended(k)}}}println("Los datos ingresados son :") input.foreach(println(_))println("La media de los datos ingresados es"+media(input))println("La mediana es :") input.foreach(mediana(_))println(mediana)if(calculateModa().isEmpty){println("No hay moda")}else{println("La moda es:") moda.foreach(println(_))}}
La solución del reto la pueden encontrar aquí en mi GitHub: GitHub - @RianoJNicolas
scala> def promedio(lista:Seq[Int]):Unit ={
| val suma: Float= lista.sum.toFloat
| val tamanio= (lista.map(x=>1)).sum
| val promedio = suma/tamanio
| Map("Media"-> promedio)
| println(promedio)
| }
scala> def mediana(lista:Seq[Int])={
| val tamanio= (lista.map(x=>1)).sum.toFloat
| val mitad = tamanio/2
| if (mitad%2==0){
| val mediana: Float = (lista(mitad.toInt-1)+lista(mitad.toInt))/2.toFloat
| Map("Mediana"->mediana)}
| else Map("Mediana"-> lista(mitad.toInt))
| }
Duda por que cuando haces le map a una lista por ejemplo
override def main(args: Array[String]): Unit = {
val numbers = List (1,2,3,4)
println(numbers)
val numbers2 = numbers.map(x => x+1)
println(numbers2)
}
}
en la salida me recorre el valor iniciando en 2, ósea si agrega el 5 en mi lista pero me quita el primer valor