Calculando la nota final usando funciones Funciones en C++ Programación Orientada a Objeto Ing. Civil en Telecomunicaciones Calculando la mediana usando funciones • Se desea convertir el cálculo de la mediana en una función double mediana(vector<double> v) { typedef vector<double>::size_type vec_size; vec_size size = v.size(); if (size == 0) throw domain_error(“Mediana de un vector vacio”); sort(v.begin(), v.end()); vec_size mid = size/2; return (size%2 == 0) ? (v[mid] + v[mid – 1])/2 : v[mid]; • Se desea convertir el cálculo de la nota final en una función double nota(double ex1, double ex2, double tareas) { return 0.2*ex1 + 0.4*ex2 + 0.4*tareas; } • La función se invoca como cout << "Su nota final es " << nota(examen1, examen2, suma/contador) << endl; Calculando la mediana usando funciones • Función mediana() recibe una copia del vector dado como argumento Copia del vector original es modificado por la función sort() • Variables size y mid son locales a la función Variable mediana ya no es necesaria • Si el vector está vacío, la función genera una excepción de tipo domain_error } Excepción recibe un string como argumento Calculando la mediana usando funciones Calculando la nota final usando funciones • Función nota() anterior supone que se conoce la suma de las tareas • Cómo hacer una función nota() que reciba las tareas y calcule la mediana? No sabemos el número de tareas a priori • Se desea convertir el cálculo de la nota final en una función double nota(double ex1, double ex2, const vector<double>& vec_tareas){ if (vec_tareas.size() == 0) throw domain_error(“Estudiante no tiene notas de tareas”); Pasar un vector con las notas Llamar a la función mediana() anterior © 2015 Mario Medina C. return nota(ex1, ex2, mediana(vec_tareas)); } 1 Sobrecarga de funciones Verificación del tamaño del vector • Hemos definido dos funciones nota() • Función mediana() ya verifica si el tamaño del vector es 0 double nota(double ex1, double ex2, double tareas) double nota(double ex1, double ex2, const vector<double>& vec_tareas) • Cómo sabe el compilador cuál función usar? Por el número y tipo de los argumentos Imprime mensaje “Mediana de un vector vacío” • Función nota() verifica el tamaño del vector de nuevo Imprime mensaje “Estudiante no tiene notas de tareas” Mensaje es más útil al usuario Referencias a un vector Referencias const a un vector • Función tiene argumento const vector<double>& vec_tareas • Sea vector<double> v1; Esta es una referencia a un vector const de tipo double • Referencia es un alias para un objeto vector<double> notas; vector<double>& n = notas; • No es un puntero! • n es un sinónimo ó un alias para notas Leyendo las tareas usando funciones • Queremos leer las notas de las tareas desde el flujo de entrada en una función Función debe recibir el flujo de entrada como argumento Función debe retornar el flujo de entrada istream& leeNotas(istream& in, vector<double>& notas) { // . . . . . return in; } © 2015 Mario Medina C. const vector<double>& v2 = v1 es una referencia const a v1 No es posible cambiar v1 a través de v2 const vector<double>& v3 = v2 también es una referencia const a v1 Las referencias no se anidan vector<double>& v4 = v3 es un error No es posible crear referencia no-const a un objeto o referencia const Función de lectura de notas • Argumento in es referencia no-const Indica que flujo de entrada in será modificado • Argumento notas es referencia no-const Indica que vector notas será modificado • Función debe leer notas desde el flujo y agregarlas al vector Lectura termina con error o End-Of-File • Función retorna flujo “limpio” Limpia el posible estado de error 2 Calcula mediana con funciones (I) Función de lectura de notas #include #include #include #include #include #include • Retorna las notas en vector notas istream& leeNotas(istream& in, vector<double>& notas) { if (in) { notas.clear(); // Limpia el vector double temp; while (in >> temp) { notas.push_back(temp); } // Limpia posible estado de error in.clear(); } return in; } using namespace std; Calcula mediana con funciones (III) // Funcion que calcula nota usando dos enteros y un vector double nota(double ex1, double ex2, const vector<double>& vec_tareas) { if (vec_tareas.size() == 0) throw domain_error("Estudiante no tiene notas de tareas"); return nota(ex1, ex2, mediana(vec_tareas)); } istream& leeNotas(istream& in, vector<double>& notas) { if (in) { notas.clear(); // Limpia el vector double temp; while (in >> temp) notas.push_back(temp); in.clear(); // Limpia posible estado de error } return in; } Calcula mediana con funciones (IV) int main() { // Solicita el nombre del alumno cout << "Ingrese su nombre: "; string nombre; cin >> nombre; cout << "Hola, " << nombre << "!" << endl; Calcula mediana con funciones (IV) // Lee las notas desde la entrada estandar leeNotas(cin, notas); // Calcula la nota final try { double nota_final = nota(examen1, examen2, notas); streamsize prec = cout.precision(); cout << "Su nota final es " << fixed << setprecision(3) << nota_final << setprecision(prec) << endl; } catch(domain_error) { cout << "Ingrese sus notas. Intente de nuevo" << endl; return 1; } return 0; // Solicita las notas de los examenes cout << "Ingrese las notas de sus examenes: "; double examen1, examen2; cin >> examen1 >> examen2; // Solicita las notas de las tareas cout << "Ingrese las notas de sus tareas, terminando con ^D: "; // Vector de notas vector<double> notas; © 2015 Mario Medina C. // Usamos biblioteca estándar // Funcion notas que recibe 3 doubles double nota(double ex1, double ex2, double tareas) { return 0.2*ex1 + 0.4*ex2 + 0.4*tareas; } Calcula mediana con funciones (II) // Funcion que calcula mediana de un vector double mediana(vector<double> v) { typedef vector<double>::size_type vec_size; vec_size size = v.size(); if (size == 0) { throw domain_error("Mediana de un vector vacio"); } sort(v.begin(), v.end()); vec_size mid = size/2; return (size%2 == 0) ? (v[mid] + v[mid - 1])/2 : v[mid]; } <algorithm> <iomanip> <iostream> <stdexcept> <string> <vector> } 3 Cálculo de notas desde archivo Estructura de datos por alumno • Supongamos ahora que queremos calcular las notas finales de un grupo de estudiantes e imprimir un listado ordenado por nombre • Datos para cada estudiante ingresados por teclado según el siguiente formato • Almacenaremos la información de cada alumno en una estructura de datos Bonini 4.5 5.6 3.2 3.9 6.7 4.5 Vergara 6.6 4.5 7.0 7.0 3.0 6.7 Solabarrieta 3.4 2.6 4.6 6.1 5.2 5.0 Pasos a seguir • Para resolver el problema, los pasos a seguir son Leer los datos de los alumnos a estructuras InfoAlumno Calcular la nota de cada alumno en base a la información de la estructura Generar una lista ordenada a partir de un vector de estructuras InfoAlumno Cálculo de la nota • Invoca a función notas() anterior const a una estructura InfoAlumno como argumento Evita la copia de la estructura Recibe una referencia double nota(const InfoAlumno& s) { return nota(s.examen1, s.examen2, s.notas); } Esto es igual a lenguaje C struct InfoAlumno { string nombre; double examen1, examen2; vector<double> notas; }; Lectura de los datos de los alumnos • Similar a función leeNotas() anterior Recibe un flujo de entrada de argumento y lo retorna después de extraer los datos istream& leeAlumnos(istream& in, InfoAlumno& s) { in >> s.nombre >> s.examen1 >> s.examen2; leeNotas(in, s.notas); return in; } Ordenando un vector de estructuras InfoAlumno • Usar función sort() de biblioteca de algoritmos de C++ Si datos de alumnos se almacenan en vector<InfoAlumnos> alumnos, usar sort(alumnos.begin(), alumnos.end()); • Error! Función sort no sabe cómo comparar estructuras InfoAlumno © 2015 Mario Medina C. 4 Ordenando un vector de estructuras InfoAlumno • Necesario definir una función de comparación para estructuras InfoAlumno Esta función debe recibir dos estructuras InfoAlumno y retornar una variable booleana Usa comparación alfabética de objetos string bool compara(const InfoAlumno& x, const InfoAlumno& y){ return x.nombre < y.nombre; } Ordenar usando Compilación separada • Archivo fuente ha crecido bastante Compilador C++ soporta compilación separada de archivos fuente • Dividiremos el archivo fuente en varios archivos Archivos de encabezado (.h) Archivos de código (.cpp) sort(alumnos.begin(), alumnos.end(), compara); • Comencemos con la mediana Archivo mediana.cpp Archivo mediana.h #include <algorithm> #include <stdexcept> #include <vector> // Necesario para sort() // Necesario para domain_error // Necesario para vector using namespace std; // Funcion que calcula mediana de un vector double mediana(vector<double> v) { typedef vector<double>::size_type vec_size; vec_size size = v.size(); if (size == 0) { throw domain_error("Mediana de un vector vacio"); } sort(v.begin(), v.end()); vec_size mid = size/2; return (size%2 == 0) ? (v[mid] + v[mid - 1])/2 : v[mid]; } Archivos .cpp y .h • Archivos de código contienen las definiciones de las funciones Contienen directivas #include y using • Archivos de encabezado contienen las declaraciones de las funciones No deben incluir directivas using Incluir directivas del preprocesador para evitar inclusiones múltiples Directivas #ifndef, #define, #endif © 2015 Mario Medina C. • Primeras líneas y última línea impiden inclusión multiple de archivo de encabezado • No es necesario incluir nombres de variables en las declaraciones de funciones #ifndef _mediana_h_ #define _mediana_h_ #include <vector> double mediana(std::vector<double>); #endif Archivo infoAlumno.h #ifndef _info_Alumno_h_ #define _info_Alumno_h_ #include <iostream> #include <string> #include <vector> // Estructura de datos por alumno struct InfoAlumno { std::string nombre; double examen1, examen2; std::vector<double> notas; }; std::istream& leeAlumnos(std::istream&, InfoAlumno&); bool compara(const InfoAlumno&, const InfoAlumno&); std::istream& leeNotas(std::istream& in, std::vector<double>& notas); #endif 5 Archivo infoAlumno.cpp #include "infoAlumno.h" using std::istream; using std::vector; // Funcion que lee las notas del istream in istream& leeNotas(istream& in, vector<double>& notas) { // Definicion de la funcion } // Funcion que lee los datos de los alumnos istream& leeAlumnos(istream& in, InfoAlumno& s) { // Definicion de la funcion } // Funcion que compara dos estructuras Info_Alumno bool compara(const InfoAlumno& x, const InfoAlumno& y) { // Definicion de la funcion } Archivo nota.h #ifndef _notas_h_ #define _notas_h_ #include <vector> #include "infoAlumno.h" double nota(double, double, double); double nota(double, double, const std::vector<double>&); double nota(const InfoAlumno&); #endif Archivo nota.cpp // Archivos de encabezado del sistema #include <stdexcept> #include <vector> // Archivos de encabezado del proyecto #include "notas.h" #include "mediana.h" #include "infoAlumno.h" using std::domain_error; using std::vector; // Aqui va la definicion de las funciones ya vistas // en clases anteriores . . . © 2015 Mario Medina C. 6