Gyakorlati alapok
Kis nosztalgia avagy előretekintés: az év hányadik napja?
Az egyik fejezetben a szökőév kiszámításakor felmerülő többszörös feltételmegadással bosszantottam a Tisztelt Olvasót. Ebben a fejezetben azt számítjuk ki, hogy a beírt dátum az év hányadik napjára esik. A 2 probléma természetesen összetartozik, hiszen a korrekt végeredményhez tudnunk kell, hogy a bemeneti adatként megadott év szökőév-e. További fontos szempont, hogy a bemeneti adatot most nem ellenőrizzük le, helyette csakis a problémamegoldásra koncentrálunk.
Kis elméleti előzmény: a szökőév egy olyan speciális év, amely általában 4 évente +1 napot tartalmaz (február 24.). Erre azért van szükség, mert a csillagászati év és az 1582 óta működő Gergely-, másik nevén Gregorián-naptár között folyamatosan korrigálandó, időbeli eltérések vannak. A szökőév nem tévesztendő össze a szökőmásodpercekkel, amelyeket -ugyanilyen okból-, a csillagászati naphosszhoz szintén korrigálnak.
A szökőév implementációjánál a következő többszörös feltételmegadást kell figyelembe vennünk:
nagyobb 1582-nél, 4-gyel osztható; 100-zal nem osztható, 400-zal osztható
Mindennek kiszámítása a fejezetcsomag igényei szerint külön függvénybe került:
public static int szokoev(int ev){
int szokoev = 0;
if ((ev > 1582) && (ev % 4 == 0) && (ev % 100 != 0) || (ev % 400 == 0)){
szokoev++;
}
return szokoev;
}
Ezen függvény érdekessége, hogy int típusú. A funkció valójában megoldható boolean típussal is:
public static
boolean szokoev(int ev){
boolean szokoev =
false;
if ((ev > 1582) && (ev % 4 == 0) && (ev % 100 != 0) || (ev % 400 == 0)){
szokoev == true;
}
return szokoev;
}
Továbbá ismernünk kell azt a listát, amelyik azt tartalmazza, hogy a hónapok hány napból állnak, illetve hónapváltáskor éppen az év hányadik napjában vagyunk (barna színnel jelölve):
-
január - 31 napos = 31
-
február - 28 = 59 (vagy szökőévenként 29 napos)
-
március - 31 napos = 90
-
április - 30 napos = 120
-
május - 31 napos = 151
-
június - 30 napos = 181
-
július - 31 napos = 212
-
augusztus - 31 napos = 243
-
szeptember - napos 30 = 273
-
október - 31 napos = 304
-
november - 30 napos = 334
-
december - 31 napos = 365
Szökőév esetén március 1-től felfelé az aktuális dátumhoz +1 napot kell hozzáillesztenünk.
A számolás fő vezérlési szerkezete egy switch kapcsoló (Dönteni kell! (switch) című fejezet), amely a "napsorszámot" nekünk villámgyorsan megállapítja:
switch(honap){
case 1: eredmeny = nap; break;
case 2: eredmeny = 31 + nap; break;
case 3: eredmeny = 59 + nap; break;
case 4: eredmeny = 90 + nap; break;
case 5: eredmeny = 120 + nap; break;
case 6: eredmeny = 151 + nap; break;
case 7: eredmeny = 181 + nap; break;
case 8: eredmeny = 212 + nap; break;
case 9: eredmeny = 243 + nap; break;
case 10: eredmeny = 273 + nap; break;
case 11: eredmeny = 304 + nap; break;
case 12: eredmeny = 334 + nap; break;
}
A bemeneti adat fogadásakor a legfontosabb, még nem ismert beépített metódus a substring(). Ez választja le a bemeneti String füzérről (például 2015.03.18.)...
-
az év (String year = adatString.substring(0,4);),
-
a hónap (String month = adatString.substring(5,7);)
-
és a nap (String month = adatString.substring(8,10);) számkaraktereit.
Ám amint már említésre került, nincs egyéb karakterellenőrzés.
Az utolsó if-else döntéscsomagban pedig az eredményeket írjuk ki. Ennek is van egy kis bonyodalma, hiszen...
...valójában nem pontos: a szökőnap ugyanis nem február 29. hanem február 24. Ám ez lényegtelen, mert a napszámításkor nem ez, hanem csakis az számít, hogy február 28 vagy 29 napos. Tehát a napsorszámok csak február 28. után (59. nap után) változnak meg és csakis szökőév esetén.
import java.util.Scanner;
public class Main {
public static int szokoevSzamitas(int ev){
int szokoev = 0;
if ((ev > 1582) && (ev % 4 == 0) && (ev % 100 != 0) || (ev % 400 == 0)){
szokoev++;
}
return szokoev;
}
public static void main(String[] args) {
int ev = 0;
int honap = 0;
int nap = 0;
int szokoev = 0;
int eredmeny = 0;
Scanner scanner = new Scanner (System.in);
System.out.println ("Kérem, hogy adja meg a dátumot a következő formátumban:
pl. 2015.03.18.");
String adatString = scanner.nextLine();
String year = adatString.substring(0,4);
String month = adatString.substring(5,7);
String day = adatString.substring(8,10);
ev = Integer.parseInt(year);
honap = Integer.parseInt(month);
nap = Integer.parseInt(day);
szokoev = szokoevSzamitas(ev);
switch(honap){
case 1: eredmeny = nap; break;
case 2: eredmeny = 31 + nap; break;
case 3: eredmeny = 59 + nap; break;
case 4: eredmeny = 90 + nap; break;
case 5: eredmeny = 120 + nap; break;
case 6: eredmeny = 151 + nap; break;
case 7: eredmeny = 181 + nap; break;
case 8: eredmeny = 212 + nap; break;
case 9: eredmeny = 243 + nap; break;
case 10: eredmeny = 273 + nap; break;
case 11: eredmeny = 304 + nap; break;
case 12: eredmeny = 334 + nap; break;
}
if(szokoev != 0 && eredmeny > 59){
System.out.println(adatString + ".
szökőév és a megadott dátum az év "
+ (eredmeny + szokoev) + ". napja volt/lesz.");
}
else if(szokoev != 0){
System.out.println(adatString + ".
szökőév és a megadott dátum az év "
+ eredmeny + ". napja volt/lesz.");
}
else
System.out.println(adatString + ".
NEM szökőév és a megadott dátum az év "
+ eredmeny + ". napja volt/lesz.");
}
}
A program valójában 3 metódusból áll, amelyhez +1 lehetőségként szintén külön eljárásban hozzácsaphatjuk az eredménykiírást:
-
szimpla adatbekérés, vele adattípus-átalakítás,
-
szökőévszámítás,
-
napsorszám-számítás,
-
eredménykiírás.
A fenti program ebből csak 1 metódust implementált külön függvényben (szokoevSzamitas(int ev), kézenfekvő tehát a feladat, miszerint mindegyiket valósítsuk meg külön függvényben!
import java.util.Scanner;
public class Main {
static int szokoevSzamitas(int ev){
int szokoev = 0;
if ((ev >
1582) && (ev % 4 == 0) && (ev % 100 != 0) || (ev % 400 == 0)){
szokoev++;
}
return szokoev;
}
static int[] getBemenetiAdat(String adatBekeres){
int[] eredmenyTomb = new int[3];
Scanner scanner = new Scanner (System.in);
System.out.println(adatBekeres);
String adatString = scanner.nextLine();
String year =
adatString.substring(0,4);
String month =
adatString.substring(5,7);
String day =
adatString.substring(8,10);
eredmenyTomb[0] =
Integer.parseInt(year);
eredmenyTomb[1] =
Integer.parseInt(month);
eredmenyTomb[2] =
Integer.parseInt(day);
return eredmenyTomb;
}
static int napSorszamSzamitas(int honap, int nap){
int napSorszam = 0;
switch(honap){
case 1:
napSorszam = nap; break;
case 2:
napSorszam = 31 + nap; break;
case 3:
napSorszam = 59 + nap; break;
case 4:
napSorszam = 90 + nap; break;
case 5:
napSorszam = 120 + nap; break;
case 6:
napSorszam = 151 + nap; break;
case 7:
napSorszam = 181 + nap; break;
case 8:
napSorszam = 212 + nap; break;
case 9:
napSorszam = 243 + nap; break;
case 10:
napSorszam = 273 + nap; break;
case 11:
napSorszam = 304 + nap; break;
case 12:
napSorszam = 334 + nap; break;
}
return napSorszam;
}
static void eredmenyKiiras(int ev, int szokoev, int
napSorszam){
if(szokoev != 0 && napSorszam > 59){
System.out.println(ev + ". szökőév és a megadott dátum az év "
+ (napSorszam + szokoev) + ". napja volt/lesz.");
}
else if(szokoev != 0){
System.out.println(ev + ". szökőév és a megadott dátum az év "
+ napSorszam + ". napja volt/lesz.");
}
else
System.out.println(ev +
". NEM szökőév és a megadott dátum az év "
+ napSorszam + ". napja volt/lesz.");
}
public static void main(String[] args) {
int[] eredmenyTomb =
getBemenetiAdat("Kérem, hogy adja meg a dátumot
a következő formátumban: pl. 2015.03.18.");
int szokoev =
szokoevSzamitas(eredmenyTomb[0]);
int napSorszam =
napSorszamSzamitas(eredmenyTomb[1], eredmenyTomb[2]);
eredmenyKiiras(eredmenyTomb[0],
szokoev, napSorszam);
}
}
Végeredmény (például):
Kérem, hogy adja meg a dátumot a következő formátumban:
pl. 2015.03.18.
2015.01.10.
2015. NEM szökőév és a megadott dátum az év 10. napja volt/lesz.
Ilyen módon a Java-kódban több érdekes változás is történik. A legszembetűnőbb jelenség, hogy a főprogram milyen rövid lett, hiszen a funkcionális kódok "kívülre" kerültek, a main főprogram csak hivatkozik rájuk:
public static void
main(String[] args) {
int[] eredmenyTomb =
getBemenetiAdat("Kérem, hogy adja meg a dátumot
a következő formátumban: pl. 2015.03.18.");
int szokoev =
szokoevSzamitas(eredmenyTomb[0]);
int napSorszam =
napSorszamSzamitas(eredmenyTomb[1], eredmenyTomb[2]);
eredmenyKiiras(eredmenyTomb[0],
szokoev, napSorszam);
}
A külső eljárások és függvények használatával viszont új nehézségekbe ütköztünk: fokozottan kell figyelnünk a függvények közti adatátadáskor fellépő típusegyezéseket.