W poprzednim wpisie omawialiśmy zmienne oraz ich typy, dziś zaś powiemy sobie czym są wyrażenia algebraiczne.
Czym zatem są te wyrażenia algebraiczne?
Otóż, wyrażenia algebraiczne są niczym innym jak wyrażeniami matematycznymi, złożonymi z min. jednego symbolu algebraicznego (stała/zmienna), połączonego ze sobą (w przypadku min. 2) znakiem działania (plus, minus, znak mnożenia, znak dzielenia, pierwiastek, potęga) oraz zawierającymi niekiedy nawiasy – użyte zgodnie z notacją matematyczną.
Jak zatem wyglądają wyrażenia algebraiczne w C#.
1. Działania bez konwersji typów
Posługując się niemal w niezmienionej postaci kodem napisanym we wpisie Zmienne i ich typy, proste wyrażenie algebraiczne bez konwersji typów jesteśmy w stanie zapisać w postaci:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace wyrazenia_algebraiczne { class Program { static void Main(string[] args) { //przykladowe wyrazenie: y=2ab-4 int a = 2, b = 3, y; y = 2 * a * b - 4; Console.WriteLine(y); Console.ReadKey(); } } } |
Jak wiemy z przeszłości, w matematyce możemy opuścić znak mnożenia pomiędzy zmiennymi bądź zmienną i liczbą, i zapisać powyższy przykład w postaci y=2ab-4 – jest on dla każdego czytelny i zrozumiały. W programowaniu jednak nie możemy w ten sposób zapisywać iloczynu, ponieważ zmienne mogą być wieloznakowe i brak operatorów mnożenia rodziłby niejednoznaczność.
W powyższym przykładzie użyliśmy tylko dwóch operatorów, a mianowicie znaku mnożenia (*) oraz znaku odejmowania (–), które są operatorami arytmetycznymi. W języku C# operatorów jest o wiele więcej. Wszystkie z nich znajdziemy wraz z ich opisem działania w tablicach informatycznych bądź w dokumentacji pod tym linkiem.
2. Modyfikacja typów dla literałów oraz sufiksy
W tym punkcie omówimy sufiksy umożliwiające modyfikację typów literałów oraz powiemy czym są literały.
Czym jest literał?
Zarówno w matematyce jak i w programowaniu stałe, które znamy są wielkościami, które jak sama nazwa mówi nie mogą się zmieniać.
Wśród wielkości stałych możemy wyróżnić dwa główne ich rodzaje:
- stałe mające swoją nazwę (identyfikator)
- literały
Każdy literał ma domyślny typ; tak jak każda liczba całkowita ma domyślny typ integer (int).
1 |
int x = 2 |
Powyżej widzimy zmienną x typu integer, dla której przypisana jest wartość 2, która jest literałem.
Podobnie jest w poniższym przypadku, gdzie dla zmiennej y typu double , literałem jest 2.5
1 |
double y = 2.5 |
W przeciwieństwie do naszych ustawień regionalnych, w programowaniu część dziesiętną liczby, jak widzimy powyżej zapisujemy nie z przecinkiem a kropką.
W programowaniu w C# można zmienić domyślny typ literału dodając specjalny sufiks, czyli literałowy modyfikator typu.
W dokumentacji na stronie producenta (link) znajdziemy pełny wykaz modyfikatorów typów dla literałów, ja zaś poniżej przedstawię 4 podstawowe.
- F lub f
Modyfikacja typu literału liczbowego na typ float, np. 3.5f to literał 3.5 typu float. Analogicznie 5.7f to literał 5.7 typu float.
Deklaracja float x = 34.5; spowoduje błąd, ponieważ literał 34.5 ma domyślny typ double. Dopiero dopisanie modyfikatora dla typu float pozwoli poprawnie wykonać deklarację z inicjalizacją literału z separatorem dziesiętnym do zmienne typu float: float x = 34.5f lub float x = 34.5F
- D lub d
Modyfikacja typu literału liczbowego na typ double, np. 3d to jest literał 3.0 typu double.
- M lub m
Modyfikacja typu literału liczbowego na typ decimal, np. 3m to jest literał 3.0 typu decimal.
- L lub l
Modyfikacja typu literału liczbowego na typ long, np. 123456L to jest literał 123456 typu long.
Przykład 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace wyrazenia_algebraiczne { class Program { static void Main(string[] args) { double y = 1 / 2; Console.WriteLine(y); Console.ReadKey(); } } } |
Dla zmiennej y typu double przypisano wynik dzielenia 1 przez 2. Wynikiem jest 0. Czemu? Otóż dlatego, że każde działanie jest wykonywane według ‘najwyższego’ typu argumentu. W powyższym przypadku oba argumenty operatora / (tzn. literały 1 i 2) mają ten sam typ int, w związku z czym całe działanie jest wykonane w typie int. Obliczona wartość jest umieszczana w zmiennej y typu double, ale część ułamkowa została utracona już w czasie wykonywania działania.
Przykład 2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace wyrazenia_algebraiczne { class Program { static void Main(string[] args) { double y = 1d / 2; Console.WriteLine(y); Console.ReadKey(); } } } |
Wynikiem w tym przykładzie jest 0,5. Stało się to przez dopisanie do literału 1 sufiksu d, który zmienił literał 1 na typ double.
Przykład 3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace wyrazenia_algebraiczne { class Program { static void Main(string[] args) { double F, C; Console.WriteLine("Podaj temperaturę w stopniach Fahrenheita"); F = double.Parse(Console.ReadLine()); C = 5d / 9 * (F - 32); Console.WriteLine(C); Console.ReadKey(); } } } |
Powyższy przykład jest już nieco bardziej złożony, a mianowicie wykorzystuje wzór pozwalający na przeliczenie temperatury podanej w stopniach Fahrenheita na stopnie w skali Celsjusza, tj. F=5/9*(F-32).
Jak widzimy użyliśmy tu modyfikatora d (double) dla literału 5 ponieważ bez niego zmienna C domyślnie przyjęłaby typ integer.
Podobny rezultat dla powyższego przykładu osiągnęlibyśmy zapisem:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace wyrazenia_algebraiczne { class Program { static void Main(string[] args) { double F, C; Console.WriteLine("Podaj temperaturę w stopniach Fahrenheita"); F = double.Parse(Console.ReadLine()); C = 5.0 / 9 * (F - 32); Console.WriteLine(C); Console.ReadKey(); } } } |
gdzie nie używamy modyfikatora, a dla zmiennej C przypisujemy wartość 5.0 co z góry określa nam ten literał na typ double i modyfikacja typu przez sufiks nie jest konieczna. Podobny rezultat osiągnęlibyśmy poprzez zapis tej lini kodu jako:
1 |
C = 5 / 9.0 * (F - 32); |
3. Konwersja typów
Poznaliśmy już jedną z dwóch wielkości stałych w programowaniu, tj. literał. Zajmijmy się teraz zatem stałą oraz konwersją typu dla zmiennej i stałej.
Poniższy przykład przedstawia program liczący średnią, miesięczną sprzedaż samochodów.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace wyrazenia_algebraiczne { class Program { static void Main(string[] args) { const int liczbaMiesiecy = 12; int liczbaSamochodow; double sredniaSprzedazSamochodow; Console.WriteLine("Podaj roczną sprzedaż samochodów:"); liczbaSamochodow = Convert.ToInt32(Console.ReadLine()); sredniaSprzedazSamochodow = liczbaSamochodow / (double)liczbaMiesiecy; Console.WriteLine(sredniaSprzedazSamochodow); Console.ReadKey(); } } } |
Jak widzimy w powyższym kodzie dokonano deklaracji stałej o nazwie liczbaMiesiecy – jest to stała typu integer i deklaracja tej stałej odbyła się poprzez dodanie słowa kluczowego const. W przypadku deklaracji stałej musimy pamietać o wspomnianym wcześniej const (constans), typie dla tej stałej (w typ przypadku int) oraz aby stała była stałą musimy pamietać o przypisaniu dla niej wartości (w tym przypadku 12). Dla stałych przypisanie wartości inicjalizacyjnej musi się odbyć przy deklaracji, nie później.
Dla podanego przykładu w linii 18 kodu zastosowano ciekawy zabieg o którym należy wspomnieć, a mianowicie rzutowanie.
1 |
sredniaSprzedazSamochodow = liczbaSamochodow / (double)liczbaMiesiecy; |
Określenie w tym miejscu typu double daje naszemu programowi możliwość podania dokładnej średniej liczby samochód sprzedanych w miesiącu z dokładnością do części dziesiętnych.
Miejsce na deklarację typu naszej zmiennej w tym przypadku (dzielenie) jest bez znaczenia; podobny rezultat osiągniemy przy określeniu typu przy zmiennej liczbaSamochodów, a także dla obu jednocześnie.
1 |
sredniaSprzedazSamochodow = (double)liczbaSamochodow / (double)liczbaMiesiecy; |
Zastosowane wyżej rzutowanie typów jest przykładem tzw. jawnej konwersji typów. istnieje bowiem jeszcze niejawna o której wspomnę w kolejnych wpisach.
Jawna konwersja typów informuje kompilator o tym, że świadomie chcemy dokonać konwersji typów i jesteśmy również świadomi tego, że może dojść do utraty danych.
W naszym przykładzie, przy konwersji typów z int na double nie ma takiego ryzyka, ponieważ typ double jest bardziej pojemny.
W przypadku zaś konwertowania np. zmiennej x typu double równej 1,2 na typ int część ułamkowa zostanie utracona.
Podczas rzutowania zmiennej typu double do float dojść może do zmniejszenia precyzji czego raczej chcielibyśmy uniknąć pisząc jakikolwiek program.
W naszym przykładzie zastosowano również konwersję typów o której wspominałem na początku. Dokonano tego w linii 17 za pomocą metody Convert.ToInt32
1 |
liczbaSamochodow = Convert.ToInt32(Console.ReadLine()); |
Podobny rezultat mogliśmy uzyskać poprzez użycie metody int.Parse i zapis:
1 |
liczbaSamochodow = int.Parse(Console.ReadLine()); |
4. Funkcje matematyczne
W wyrażeniach algebraicznych mogą pojawiać się funkcje matematyczne, takie jak np. sin, log, etc.
W przypadku występowania funkcji matematycznych w C#, możemy posłużyć statyczną klasą Math.
Czym jest klasa statyczna? Klasa statyczna to taka klasa dla której nie tworzymy obiektów oraz używamy jej metod poprzedzając ich nazwę kropką i nazwą klasy – to tak w skrócie. W tym wpisie używaliśmy już jednej z takich klas, a mianowicie Convert.
Przykład 1
Napiszmy kod, który obliczy nam poniższe wyrażenie:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace wyrazenie { class Program { static void Main(string[] args) { double x, y; Console.WriteLine("Podaj wartość x (większą od 0):"); x = Convert.ToDouble(Console.ReadLine()); y = Math.PI * Math.Sqrt(Math.Abs(Math.Sin(x)) * Math.Log(x, 2.0)); Console.WriteLine(y); Console.ReadKey(); } } } |
Aby czuć się pewniej i w pełni móc wykorzystać metody z klasy Math zachęcam do zapoznania się ze specyfikacją, którą znajdziesz tutaj.
My w naszym kodzie użyliśmy Abs(Double), Log(Double, Double), Sin(Double), Sqrt(Double) oraz PI.
5. Operatory inkrementacji i dekrementacji
W poniższym przykładzie przedstawię operatory inkrementacji oraz dekrementacji, które stanowią skrócony zapis instrukcji powodującej zwiększenie i zmniejszenie wartości zmiennych.
Przykład
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace przyklad1 { class Program { static void Main(string[] args) { int x = 0; x = x + 1; Console.WriteLine(x); Console.ReadKey(); } } } |
W linii 13 widzimy deklarację zmiennej x przyjmującej wartość 0, zaś w linii 14 widzimy instrukcję zwiększającą wartość x o 1.
W programowaniu tak często pojawia się konieczność użycia operacji zmniejszającej/zwiększającej o 1, że stworzono specjalne operatory do tego celu, które mają za zadanie skrócenie zapisu.
Powyższy przykład wymagający zwiększenia wartości dla zadeklarowanej zmiennej wykorzystując operator inkrementacji wygląda tak:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace przyklad1 { class Program { static void Main(string[] args) { int x = 0; x++; //x = x + 1; Console.WriteLine(x); Console.ReadKey(); } } } |
Wykorzystano w nim operator inkrementacji ++. Poprzedni zaś zapis wykomentowano.
Działaniem odwrotnym dla inkrementacji jest dekrementacja, której operatorem jest —, a zapis dekrementacji przedstawia się następująco:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace przyklad1 { class Program { static void Main(string[] args) { int x = 0; //x++; //x = x + 1; x--; //x = x - 1; Console.WriteLine(x); Console.ReadKey(); } } } |
W tego typu przykładach miejsce użycia operatorów inkrementacji oraz dekrementacji nie ma znaczenia; możemy ich użyć po nazwie zmiennej (jak w naszym przykładzie wyżej), ale możemy również ich użyć przed nazwą zmiennej:
1 |
--x; //x = x - 1; |
Znaczenie umiejscowienia operatorów inkrementacji oraz dekrementacji pojawia się już w poniższym zapisie:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace przyklad1 { class Program { static void Main(string[] args) { int x = 0, wynik; wynik = ++x * 3; Console.WriteLine(x); Console.WriteLine(wynik); Console.ReadKey(); } } } |
Użycie operatora inkrementacji po zmiennej x sprawiłoby, że kompilator najpierw wykonałby mnożenie, a później zwiększył wartość zmiennej o 1.
Jeśli podczas pisania kodu zaistnieje konieczność zwiększania o inne wartości niż 1 do wykorzystania mamy również inne operatory, tzw. operatory przypisania (dotychczas używaliśmy takiego operatora przypisania jak ‘=’, którego nigdy nie należy mylić ze znakiem równości) i tymi operatorami są ‘+=’ (przypisanie ze zwiększeniem) oraz ‘-=’ (przypisanie ze zmniejszeniem).
Uffff… dobrnęliśmy do końca, choć tak naprawdę powiedzieliśmy sobie podstawy wyrażeń algebraicznych w języku C#, są to niewątpliwie kluczowe dla niego informacje z tego zakresu.
W następnym wpisie powiemy sobie o wyrażeniach logicznych.