Вы находитесь на странице: 1из 49

COMPUTACION II

OBJETIVOS
Aprender las tcnicas bsicas del clculo
numrico para su empleo en la resolucin
de las ecuaciones que aparecen en
Fsica.
Aprender los elementos bsicos de al
menos un lenguaje de programacin.

Direcciones electrnicas de inters:


http://www.cplusplus.com/
Libros de programacin
-Programacin en C++. Serie Schaum
Luis Joyanes Aguilar (Cdigos fuente en www.mh.es/joyanes)
-C++ Programming Language. Bjarne Stroustup. Addison Wesley.
-Programming with C++ . John Hubbard. Schaum.
Libros de consulta de la asignatura
-Anlisis Numrico con Aplicaciones. C.F. GERALD y P.O. WHEATLEY.
Editorial Addison-Wesley.
- Anlisis Numrico. R.L. BURDEN, J. DOUGLAS FAIRES. Editorial
International Thomson Editores.
Clculo Numrico. B. CARNAHAN, H.A. LUTHER y J.O. WILKES. Editorial
Rueda.
-Elementary Numerical Analysis. KENDALL ATKINSON. John Wiley and Sons.

TEMA I: INTRODUCCIN
Los idiomas ms utilizados en la computacin cientfica
(especialmente en la fsica) son FORTRAN y C / C + +.
Tradicionalmente FORTRAN fue el idioma de eleccin, y todava
hoy hay una gran cantidad de programas disponibles en las
bibliotecas FORTRAN.
Sin embargo, durante la ltima dcada, C / C + + se ha hecho cada
vez ms importante en la fsica, por lo que este curso se centra en
C + + y se aleja del FORTRAN.
Seguimos convencidos de que el lenguaje FORTRAN es mejor para
la fsica, pero para obtener un compilador FORTRAN 90/95, uno
tiene que comprar un paquete comercial, mientras que el
compilador C + + est disponible sin costo alguno tanto para Linux
como para Windows.

Algoritmos y programas
Los programas son conjuntos de instrucciones escritas en un
lenguaje de programacin (cdigo fuente) que ser interpretado
por un ordenador y traducido a lenguaje mquina (cdigo objeto)
para poder ser ejecutado (programa ejecutable).
El lenguaje de programacin permite especificar a la
computadora las acciones que debe ejecutar, escritas en un
lenguaje relativamente prximo al lenguaje humano.
El algoritmo es un procedimiento que se puede implementar en
un ordenador. Consiste en una secuencia ordenada de pasos
que conduce a la resolucin de un problema determinado en un
nmero finito de pasos.

Estructura de un programa en C++


lnea de comentario

// mi primer programa en C++


# include <iostream>
using namespace std;
int main ()
{
cout << "Hola!";
system(pause);
return 0;
}

el signo # es una instruccin


para el compilador: incorpora
una librera de entrada-salida
namespace: lugar donde se
ubicarn las sentencias.
punto de comienzo de la ejecucin
seguido de ( ) ya que es una funcin
cuerpo de la funcin encerrado
entre llaves { }
cout representa la salida standard
en C++
la sentencia return hace que la
funcin main acabe (cdigo 0).

Permite detener el programa para


visualizar lo escrito en pantalla.

El programa anterior ha sido estructurado en diferentes lneas con


objeto de ser mas comprensible, pero en C++ no existen reglas
estrictas sobre como separar instrucciones en diferentes lneas. Por
ejemplo, en lugar de:
int main ()
{
cout << " Hola!";
return 0;
}
podramos haber escrito:

int main () { cout << "Hola!"; return 0; }

Todo en una sola lnea y tendra el mismo significado que el cdigo previo.

INTRODUCCIN AL C++
1.- Variables. Tipos de datos
2.- Operadores
3.- Operaciones bsicas de Entrada/Salida: operadores cout y cin
4.- Operadores de control:
a) estructura condicional: if; if else
b) bucles: while; do while; for;
5.- Arrays
6.- Funciones. Parmetros de una funcin. Argumentos pasados como
referencia. Recursividad.
7.- Punteros. Reserva dinmica de memoria.

1.- Variables
Un identificador vlido es una secuencia de una o mas letras, dgitos o
guion bajo ( _ ).
Ejemplos:
a=5;
lambda=0.300;
OJO CON PALABRAS RESERVADAS!!!
Muy importante: El C++ es un lenguaje que distingue entre
minsculas y maysculas.

Tipos de datos
Resumen de los fundamentales tipos bsicos en C++
Nombre

Descripcin

Tamao

Rango

char

Tipo carcter

1 byte

int

Enteros (con signo o sin signo)

4 bytes

-2147483648 a
2147483647

float

Nmeros reales en coma flotante

4 bytes

+/- 3.4 e+/- 38

double

Nmeros reales en doble precisin 8 bytes


en coma flotante

+/- 1.7 e+/- 308

Todas las variables que queramos usar en el programa deben haber


sido declaradas con su tipo al principio del programa.
// Ejemplo:
#include <iostream>
using namespace std;
int main ()
{
// declaramos variables:
int a, b;
int result;
a = 5;
b = 2;
a = a + 1;
result = a - b;
// mostramos valores por pantalla:
cout << "a="<<a<<" b="<<b<<endl;
// mostramos el resultado por pantalla:
cout << " a-b="<<result<<endl;
// terminamos el programa:
system ("pause");
return 0;
}

Una variable puede ser global o local: Una variable global es aquella que se
declara en el cuerpo principal del programa fuente, fuera de las funciones,
mientras que una variable local es la que se declara en el cuerpo de una
funcin o en un bloque.
//Ejemplo:
#include <iostream>
using namespace std;
int
Numero_entero;
char nombre;
char apellido [20];
int main ()
{
int edad;
float A;
cout << Teclea tu edad:
cin>> edad;
.
}

Variables globales

Variables locales

Constantes definidas por el usuario: #define


Se puede usar un nombre elegido por nosotros para referirnos a una constante
que se vaya a usar frecuentemente, simplemente usando la instruccin #define
// defined constants: calculate circumference
#include <iostream>
using namespace std;
#define PI 3.14159
#define NEWLINE '\n'
int main ()
{
double r=5.0; // radius
double circle;
circle = 2 * PI * r;
cout << circle;
cout << NEWLINE;
system ("pause");
return 0;
}
RESULTADO: 31.4159

Constantes declaradas (const)


Con el prefijo const podemos declarar constantes con un tipo
especfico de la misma forma que hacemos con una variable:
const int ancho = 100;
const char tabulador = '\t';

Aqu, ancho y tabulador son dos constantes. Son tratadas como variables
normales, salvo que sus valores no pueden ser modificados despus de su
definicin.

2.- Operadores
2.1 Operadores aritmticos
Las cinco operaciones aritmticas implementadas en C++ son:
+

Suma (addition)

Resta (subtraction)

Multiplicacin (multiplication)

Divisin (division)

Mdulo (module)

Modulo es la operacin que da el resto de


una divisin de dos valores. Por ejemplo, si
escribimos:
a = 11 % 3;
la variable a contendr el valor 2, puesto
que 2 es el resto de dividir 11 entre 3

2.2 Operador asignacin (=)


// assignment operator
#include <iostream>
using namespace std;
int main ()
{
int a, b; // a:?, b:?
a = 10; // a:10, b:?
b = 4; // a:10, b:4
a = b; // a:4, b:4
b = 7; // a:4, b:7
cout << "a:";
cout << a;
cout << " b:";
cout << b<<endl;
system("pause");
return 0;
}

Resultados:
a:4; b:7

2.3 Operadores de asignacin

expresin

es equivalente a

a += b;

a = a + b;

a -= 5;

a = a - 5;

a /= b;

a = a / b;

a *= b;

a = a * b;

Operador Incremento y decremento (++, --)


Los operadores incremento (++) y decremento (--) incrementan o
reducen en uno el valor almacenado en una variable. Son equivalentes
a +=1 y a -=1, respectivamente.
Por tanto:
a++;
a+=1;
a=a+1;
son todas ellas formas equivalentes: las tres incrementan en
una unidad el valor de a.
Ojo:
x=y++;
el resultado es:
x=y; y=y+1;
en cambio, si:
x=++y;
el resultado es:
y=y+1; x=y;

3- Operadores de Entrada/Salida
cout: operador de salida por pantalla (cout<< expresin;).

Con objeto de introducir una nueva lnea, debemos insertar explcitamente un


carcter especfico:
cout << Primera sentencia.\n;
cout << Segunda sentencia.\n;
Una forma alternativa consiste en usar el manipulador endl:
cout << Primera sentencia.<<endl;
cout << Segunda sentencia.<<endl;

cin: operador de entrada por teclado (cin>> valor;).


El dispositivo standard de entrada es el teclado. Para ello
aplicaremos el operador extraccin (>>) seguido de la variable que
almacena el dato que quiere ser extrado.
Por ejemplo:
// cin operator
#include <iostream>
using namespace std;
int main ()
{
int i;
cout<<"introduce un valor entero: ";
cin >>i;
cout<<"Has introducido el numero "<<i<<endl;
system("pause");
return 0;
}

4. Operadores de Control
4.1 Estructura condicional: if
La estructura if se usa para ejecutar una sentencia o un bloque de sentencias
solamente si se cumple una determinada condicin. Por ejemplo:
if (x == 100)
cout << "x is 100";
sacar por pantalla x is 100 solamente si el valor almacenado en la
variable x es 100.
Si quisiramos que se ejecutase ms de una sentencia en el caso de que se
cumpla la condicin, podemos especificar un bloque mediante el usos de
llaves { }:
if (x == 100)
{
cout << "x is ";
cout << x;
}

4.2 Estructura condicional: if else


Podemos especificar adicionalmente lo que queremos que ocurra si no se
cumple la condicin usando la instruccin if..else.
Por ejemplo:
//condicional: if else
#include <iostream>
using namespace std;
int main ()
{
int x;
cin>>x;
cout<<endl;
if (x > 0)
cout << "x is positive";
else if (x < 0)
cout << "x is negative";
else
cout << "x is 0";
cout<<endl;
system("PAUSE");
return 0;
}

4.3 Estructuras iterativas (bucles o loops)


a) Instruccin while
//instruccin while
#include <iostream>
using namespace std;
int main ()
{
int n;
cout << "Introduce el valor de partida ";
cin >> n;
while (n>0) {
cout << n << ", ";
-- n;
}
cout << "Terminado!\n";
system("PAUSE");
return 0;
}

RESULTADO:
Introduce el valor de partida > 8
8, 7, 6, 5, 4, 3, 2, 1, Terminado!

b) Instruccin do-while
//instruccin do-while
#include <iostream>
using namespace std;
int main ()
{
int n;
do {
cout << "Enter your
number: ";
cin >> n;
cout << "You entered: "
<< n << "\n";
} while (n != 0);
cout<<"By by ..."<<endl;
system("pause");
return 0;
}

Enter number: 12345


You entered: 12345
Enter number: 160277
You entered: 160277
Enter number: 0
You entered: 0
By by

c) Instruccin for
//instruccin for
#include <iostream>
using namespace std;
int main ()
{
for (int n=10; n>0; n--) {
cout << n << ", ";
}
cout << "Terminado!\n";
system("pause");
return 0;
}
Resultado:
10, 9, 8, 7, 6, 5, 4, 3, 2, 1, Terminado!

5.- ARRAYS
Un array es una serie de elementos del mismo tipo situados en
posiciones de memoria consecutivas.
Por ejemplo, un array que contiene 5 valores enteros de tipo int
llamado a se representara de la siguiente forma:
0

a
Para declarar un array como el de ms arriba, haremos:
int a[5];
Y para asignar valores al array, haremos, por ejemplo:
int a[5]= {1,2,3,4,5}
o bien
int a[ ]= {1,2,3,4,5}

// ejemplo de arrays
#include <iostream>
using namespace
std;
int a[ ]={1,2,3,4,5};
int n, result=0;
int main ()
{
for (n=0; n<5; n++)
{
result +=a[n];
}
cout<<result<<endl;
system("pause");
return 0;
}

Resultado:
15

Sealemos que los ndices de un array comienzan en cero.

Arrays multidimensionales:
Ejemplo:int A[3] [5];
#include <iostream>
using namespace std;
#define Filas 3
0
#define Columnas 5
1
int A [Filas] [Columnas];
int n,m;
2
int main()
{
for (n=0; n<Filas; n++)
for (m=0; m<Columnas; m++)
{
A [n] [m] = (n+1)*(m+1);
cout<<"A("<<n<<","<<m<<")=
"<<A [n] [m] <<endl;
}
system ("pause");
return 0;
}

Resultado
0

10

12

15

A(0,0)= 1
A(0,1)= 2
A(0,2)= 3
0
A(0,3)= 4
A(0,4)= 5
1
0
A(1,0)= 2
A(1,1)= 4
1
2
A(1,2)= 6
2
3
A(1,3)= 8
A(1,4)= 10
A(2,0)= 3
A(2,1)= 6
A(2,2)= 9
A(2,3)= 12
A(2,4)= 15
Presione una tecla para continuar . . .

Resultado
1

10

12

15

6.- Funciones
Una funcin es un grupo de sentencias que se ejecutan cuando son llamadas
desde algn punto del programa. Su formato es el siguiente:
tipo nombre (lista de parmetros)
La llamada a una funcin se realiza desde el programa principal invocando
su nombre seguido de los argumentos separados por comas y encerrados
entre parntesis.
Las funciones se deben declarar antes de invocarlas

// ejemplo n 1 de funciones:
#include <iostream>
using namespace std;
int suma (int a, int b){
int r; r = a+b; return (r); }
int main ( ) {
int z; z=suma(5,3); cout<<"Resultado: "<<z<<endl;
system("pause");
Resultado: 8
return 0;}

En el siguiente ejemplo vemos que se pueden hacer llamadas a la


funcin de distintas formas

// ejemplo n 2 de funciones:
primer resultado: 5
segundo resultado: 5
#include <iostream>
tercer resultado: 2
using namespace std;
cuarto resultado: 6
int resta (int a, int b){
Presione una tecla para continuar . . .
int r; r = a-b; return (r); }
int main ( ) {
int x=5, y=3, z;
z = resta (7,2);
cout << primer resultado: " << z << endl;
cout << "segundo resultado: " << resta (7,2) << endl;
cout << tercer resultado: " << resta (x,y) << endl;
z= 4 + resta (x,y);
cout << cuarto resultado: " << z << endl;
system("pause");
return 0;}

Funciones sin tipo: el uso de la instruccin void


En ciertas ocasiones no es necesario que la funcin devuelva un valor
al programa principal. En este caso especial usaremos el especificador
void para indicar la ausencia de tipo de la funcin:
// ejemplo n 3 de funciones:
#include <iostream>
using namespace std;
void funcionerror()
{
cout<<"He terminado el contador"<<endl;
}
int main ( )
{
int i,n=10;
for (i=0; i<n; i++)
{if (i==n-1)
funcionerror();
}
system("pause");
return 0;
}

Arrays como parmetros de una funcin


En ciertas ocasiones, podemos necesitar pasar un array a una
funcin como parmetro. Para ello, cuando se declare la funcin,
especificaremos en sus argumentos un identificador y un par de
corchetes vacos [ ]. Por ejemplo:
// ejemplo n 4 de funciones: arrays como parmetros
#include <iostream>
using namespace std;
void print_vector (int x [ ], int dim)
{
for (int n=0; n<dim; n++)
{cout<<x[n]<<" ";}
}
int main ( ) {
int x[ ]= {5, 10, 15};
int y[ ]= {2,4,6,8,10};
print_vector (x, 3);
cout<<endl;
print_vector (y, 5);
5 10 15
cout<<endl;
2 4 6 8 10
system("pause");
Presione una tecla para continuar . . .
return 0; }

Argumentos de una funcin pasados por referencia


En la llamada a una funcin normalmente se pasan copias de los valores de las
variables del programa principal y stos quedan inalterados despus de la
llamada a la funcin. Sin embargo, a veces es necesario que dichos valores
cambien, como en el ejemplo siguiente, donde se pasan los valores por
referencia.

//function swap
#include <iostream>
using namespace std;
int swap(int *a, int *b){
cout<<endl<<"estoy en la funcion"<<endl;
cout<<"dir a= "<<a<<", "<<"dir b= "<<b<<endl;
cout<<"*a= "<<*a<<", "<<"*b= "<<*b<<endl;
cout<<"hago el swap!"<<endl;
int temp; temp=*a;*a=*b;*b=temp;
cout<<"*a= "<<*a<<", "<<"*b= "<<*b<<endl;
cout<<"me voy de la funcion"<<endl<<endl;
}
int main ( ){
int a=5,b=3;
cout<<"a= "<<a<<", "<<"b= "<<b<<endl;
swap(&a,&b);
cout<<"a= "<<a<<", "<<"b= "<<b<<endl;
cout<<"& a= "<<&a<<", "<<"& b= "<<&b<<endl;
cout << "Programa terminado" << endl;
system ("PAUSE") ;
return 0;}

a= 5, b= 3
estoy en la funcion
dir a= 0x22ff74, dir b= 0x22ff70
*a= 5, *b= 3
hago el swap!
*a= 3, *b= 5
me voy de la funcion
a= 3, b= 5
& a= 0x22ff74, & b= 0x22ff70
Programa terminado
Presione una tecla para continuar . . .

Es decir:
&= operador direccin, permite hallar la direccin de la variable
a la que se aplica.
*= operador indireccin, permite acceder al valor almacenado
en la zona de memoria donde resida la variable a la que se
aplica el operador.
a

0x22ff74

0x22ff70

direcciones

nombre de las variables

valores

Recursividad: una funcin se puede llamar a s misma. Por ejemplo,


puede ser til en el caso del clculo del factorial de un nmero:
//ejemplo n 6 de funciones: recursividad
#include <iostream>
using namespace std;
// factorial calculator
#include <iostream>
using namespace std;
long factorial (long a){
if (a > 1) return (a * factorial (a-1));
else return (1); }
int main () {
long number;
cout << "Please type a number: ";
cin >> number;
cout << number << "! = " << factorial
Please type a number: 9
(number)<<endl;
9! = 362880
system ("PAUSE");
Presione una tecla para continuar . . .
return 0; }

Declaracin de funciones.
Hasta ahora, hemos definido todas las funciones antes de que se realice la
primera llamada desde el programa principal. Si no fuese as, dara error al
compilar.
Sin embargo, hay una alternativa para escribir el cdigo fuente de las
funciones despus del programa principal. Se puede hacer mediante la
declaracin de un prototipo de funcin antes de ser usada, en lugar de la
funcin completa. Esta declaracin es mas corta que la definicin completa,
pero suficiente como para que el compilador conozca su tipo y el tipo de sus
argumentos.

//ejemplo n 7 de funciones: declaracin de funciones


#include <iostream>
using namespace std;
void odd (int a);
void even (int a);
int main (){
int i;
do {cout << "Tecle un numero (0 para salir): ";
cin >> i;
odd (i);} while (i!=0);
system ("PAUSE");
return 0;}
void odd (int a)
{
if ((a%2)!=0) cout << "Numero impar"<<endl;
else even (a);
}
void even (int a)
{
if ((a%2)==0) cout << "Numero par"<<endl;
else odd (a);
}

Tecle un numero (0 para salir): 10


Numero par
Tecle un numero (0 para salir): 21
Numero impar
Tecle un numero (0 para salir): 11
Numero impar
Tecle un numero (0 para salir): 4566668
Numero par
Tecle un numero (0 para salir): 0
Numero par
Presione una tecla para continuar . . .

El tipo void es un tipo especial que indica la ausencia de


tipo. Se usa para indicar el tipo del valor de retorno en
funciones que no devuelven ningn valor, y tambin para
indicar la ausencia de parmetros en funciones que no los
requieren.

7.- Punteros Reserva dinmica de memoria.


Ya vimos que podemos acceder a la direccin de memoria que ocupa una
variable mediante el operador ampersand (&). Por ejemplo,
int a= 5, &a= 0x22ff74
Podemos adems usar una variable para que almacene la direccin de
memoria donde se encuentra el dato al cual hace referencia. Dicha variable
se llama puntero. Se puede declarar, por ejemplo, de la siguiente forma:
int * mipuntero; // variable tipo puntero
con la propiedad de que puede modificar el contenido de la variable hacia la
que apunta. Por ejemplo:
mipuntero=&a; *mipuntero=10; =>a=10
En este ltimo caso hemos hecho uso del operador indirecin (*)

Ejemplo 1:

primervalor es 10
segundovalor es 20

Programa terminado
//puntero 1
Presione una tecla para continuar . . .
#include <iostream>
using namespace std;
int main ( )
{
int primervalor=5, segundovalor=7;
int * mipuntero; // variable tipo puntero
mipuntero=&primervalor; //el puntero apunta a la variable primervalor
*mipuntero=10;
cout<<"primervalor es " << primervalor<<endl;
mipuntero=&segundovalor;//el puntero apunta a la variable
segundovalor
*mipuntero=20;
cout<<"segundovalor es " << segundovalor<<endl;
cout << endl<< "Programa terminado" << endl;
system ("PAUSE") ;
return 0;
}

Ejemplo 2:

firstvalue is 10
secondvalue is 20

//puntero2
Programa terminado
#include <iostream>
Presione una tecla para continuar . . .
using namespace std;
int main (){
int firstvalue = 5, secondvalue = 15;
int * p1, * p2;
p1 = &firstvalue; // p1 = address of firstvalue
p2 = &secondvalue; // p2 = address of secondvalue
*p1 = 10; // value pointed by p1 = 10
*p2 = *p1; // value pointed by p2 = value pointed by p1
p1 = p2; // p1 = p2 (value of pointer is copied)
*p1 = 20; // value pointed by p1 = 20
cout << "firstvalue is " << firstvalue << endl;
cout << "secondvalue is " << secondvalue << endl;
cout << endl<< "Programa terminado" << endl;
system ("PAUSE") ;
return 0;}

Punteros y arrays
El concepto de array est muy vinculado al de puntero.
De hecho, el identificador de un array es equivalente a la
direccin de su primer elemento, es decir, es un puntero
constante.
Supongamos las dos declaraciones siguientes:
int A [20];
int *p;
Si ahora hacemos la asignacin:
p= A;
A partir de ese momento p y A son equivalentes.

Ejemplo 3:
//punteros3: punteros y arrays
#include <iostream>
using namespace std;
int main ( ){
int A[5];
int * p; // variable tipo puntero
for (int n=0;n<5;n++) {
p=&A[n]; // el puntero apunta a la variable A (array)
*p=(n+1)*10;
cout<<A[n] << ", ";}
cout << endl<< "Programa terminado" << endl;
system ("PAUSE") ;
return 0;}
10, 20, 30, 40, 50,
Programa terminado
Presione una tecla para continuar . . .

Punteros a funciones
Se usa para pasar una funcin como argumento a otra funcin.
Veamos el siguiente ejemplo:
// pointer to functions
12
#include <iostream>
8
using namespace std;
int addition (int a, int b)
Programa terminado
{ return (a+b); }
Presione una tecla para continuar . . .
int subtraction (int a, int b)
{ return (a-b); }
int operation (int x, int y, int
(*functocall)(int,int)){int g; g = (*functocall)(x,y); return (g);}
int main (){
int m,n;
int (*minus)(int,int) = subtraction;
m = operation (7, 5, addition);
cout <<m<<endl;
n = operation (20, m, minus);
cout <<n<<endl;
cout << endl<< "Programa terminado" << endl;
system ("PAUSE") ; return 0;}

7.- Reserva dinmica de memoria


Hasta ahora, en todos nuestros programas, slo hemos tenido la memoria
disponible que hemos declarado para nuestras variablesantes de la ejecucin del
programa. Pero existe un problema: qu hacemos si necesitamos una cantidad
variable de memoria que slo se puede determinar en tiempo de ejecucin?
..Reserva de Memoria Dinmica
int * bobby;
bobby = new int [5];
Operadores delete and delete[ ]
Puesto que la necesidad de memoria dinmica se limita generalmente a
momentos especficos dentro de un programa, una vez que ya no sea necesaria
debe ser liberada para que la memoria vuelva a estar disponible para otras
solicitudes de memoria dinmica. Este es el propsito de la eliminacin del
operador, cuyo formato es:
delete pointer;
delete [ ] pointer;
La primera expresin se usa para borrar la memoria asignada para un solo
elemento y la segunda para borrar la memoria asignada para matrices de
elementos.

// reserva de memoria dinmica


#include <iostream>
#include <new>
using namespace std;
int main (){
int i,n; int * p;
cout << "How many numbers would you like to type? ";
cin >> i;
p= new (nothrow) int[i];
if (p == 0)
cout << "Error: memory could not be allocated";
else
{
for (n=0; n<i; n++)
{cout << "Enter number: "; cin >> p[n]; }
cout << "You have entered: ";
for (n=0; n<i; n++)
cout << p[n] << ", ";
delete[ ] p;
}
return 0;
}

Вам также может понравиться