வெளியேறும் நிபந்தனை இல்லாமல் சுழல்நிலை குறியீட்டின் எடுத்துக்காட்டு

ஒரு சுழல்நிலை சிக்கலை இன்னொரு முறை பார்க்கலாம். உதாரணமாக, ஃபைபோனச்சி எண்களைக் கணக்கிடுவதைக் கவனியுங்கள். ஃபைபோனச்சி வரிசை என்பது ஒரு எண் வரிசையாகும், அதில் முதல் இரண்டு எண்கள் 0 மற்றும் 1 ஆகும், மேலும் ஒவ்வொரு அடுத்தடுத்த எண்ணும் முந்தைய இரண்டு எண்களின் கூட்டுத்தொகைக்கு சமமாக இருக்கும்.

இந்த எண்களைக் கணக்கிட்டுக் காட்ட குறியீட்டை எழுதுவோம்:

public class Fibonacci {
    public static void main(String[] args) {
        System.out.println(0);
        System.out.println(1);
        printFibonacci(0, 1);
    }

    private static void printFibonacci(long penultimate, long previous) {
        long current = penultimate + previous;
        System.out.println(current);
        printFibonacci(previous, current);
    }
}

சுழல்நிலை அச்சு ஃபைபோனச்சி முறையின் முதல் அழைப்புக்கு முன், வரிசையின் முதல் இரண்டு எண்களை அச்சிடவும்: பூஜ்யம் மற்றும் ஒன்று. நாம் இதைச் செய்ய வேண்டும், ஏனெனில் சுழல்நிலை முறை உள்ளீட்டு அளவுருக்களின் கூட்டுத்தொகையை மட்டுமே காட்டுகிறது, அளவுருக்கள் அல்ல.

குறியீடு சரியாகத் தெரிகிறது: நாங்கள் இரண்டு எண்களைப் பெறுகிறோம், அவற்றின் தொகையைக் கணக்கிட்டு, அதை கன்சோலில் அச்சிட்டு, மீண்டும் மீண்டும் மீண்டும் printFibonacci முறையை அழைக்கிறோம். முந்தைய எண்ணையும் (முந்தைய) தற்போதைய எண்ணையும் (தற்போதைய) வாதங்களாகக் கடந்து செல்கிறோம்.

உண்மையில், குறியீட்டில் இரண்டு பிழைகள் உள்ளன. நீங்கள் குறியீட்டை இயக்கினால் அவற்றைப் பார்க்கலாம்.

முதல் பிழை நீண்ட வகை நிரம்பி வழிகிறது. எங்கள் வரிசையில் 104 வது எண் எதிர்மறையானது, நீண்ட வகை நிரம்பி வழிகிறது என்பதைக் குறிக்கிறது.

இரண்டாவது பிழை வேறு. தோராயமாக 12,000 எண்களைக் கணக்கிட்ட பிறகு, நாம் பெறுகிறோம்:

நூல் "முக்கிய" java.lang இல் விதிவிலக்கு.StackOverflowError

ஜாவாவில் என்ன மெத்தட் கால் ஸ்டாக் உள்ளது என்பதை நினைவுகூர இது சரியான நேரம். ஜாவா இயந்திரம் அனைத்து செயல்பாடு அழைப்புகளையும் பதிவு செய்கிறது. இதைச் செய்ய, இது ஸ்டாக் எனப்படும் சிறப்பு வகை சேகரிப்பைப் பயன்படுத்துகிறது. ஒரு செயல்பாடு மற்றொன்றை அழைக்கும் போது, ​​ஜாவா இயந்திரம் ஒரு புதிய StackTraceElement ஐ ஸ்டாக் மீது தள்ளும். செயல்பாடு முடிந்ததும், இந்த உறுப்பு அடுக்கிலிருந்து அகற்றப்படும். அதன்படி, செயல்பாடு அழைப்பு அடுக்கின் தற்போதைய நிலை பற்றிய புதுப்பித்த தகவலை ஸ்டாக் எப்போதும் சேமிக்கிறது. StackTraceElement க்கான ஆவணங்கள், "ஒரு பயன்பாடு மிகவும் ஆழமாகத் திரும்புவதால், ஸ்டாக் ஓவர்ஃப்ளோ ஏற்படும் போது தூக்கி எறியப்படும்" என்று கூறுகிறது. ஒரு இயங்கும் JVM ஆனது மெத்தட் கால் ஸ்டேக்கை சேமிப்பதற்காக ஒரு சிறப்பு நினைவக பகுதியைக் கொண்டுள்ளது. இந்த நினைவகப் பகுதியின் அளவு OS மற்றும் JVM அமைப்புகளைப் பொறுத்தது. முறை கால் ஸ்டேக் கூடுதலாக, பழமையான மாறிகள் (முறை அளவுருக்களின் குறிப்பிட்ட மதிப்புகள்) மற்றும் குறிப்பு மாறிகளின் முகவரிகள் ("குவியல்" எனப்படும் நினைவக பகுதியில்) இந்த சிறப்பு நினைவக பகுதியில் சேமிக்கப்படும். அழைப்பு அடுக்குக்கான அணுகல் LIFO கொள்கையைப் பின்பற்றுகிறது.

வெளியேறும் நிபந்தனையுடன் சரி செய்யப்பட்ட உதாரணம்

குறியீட்டில் உள்ள இரண்டாவது சிக்கலை சரிசெய்வதன் மூலம் ஆரம்பிக்கலாம்.

சிக்கலைத் தீர்க்க முயற்சிப்போம்: அடுக்கு மிகவும் சிறியதாக இருந்தால், அதை பெரிதாக்குவோம். இதைச் செய்ய, "-Xss" கொடியுடன் JVM ஐத் தொடங்கி, அடுக்கிற்கு எவ்வளவு நினைவகத்தை ஒதுக்க வேண்டும் என்பதைக் குறிப்பிடவும். 5 மெகாபைட்களை ஒதுக்க முயற்சிப்போம். ஐடியாவில் இது எப்படி இருக்கும்:

வெளியீட்டின் நீளத்தை அதிகரிக்க முடிந்தது. இப்போது 12,000 எண்களுக்கு மட்டுப்படுத்தப்பட்டதை விட, வரிசையின் 49,000 க்கும் மேற்பட்ட எண்களைக் கணக்கிட முடியும். ஆனால் ஒரு கட்டத்தில், நாம் இன்னும் StackOverflowError ஐப் பெறுகிறோம் .

நீங்கள் அடுக்கின் அளவை அதிகரிக்க முயற்சி செய்யலாம், ஆனால் அது அடிப்படை சிக்கலை சரிசெய்யாது. எனவே, தர்க்கத்தில் ஒரு சிக்கலைப் பார்ப்போம். மறுநிகழ்வு நிறுத்தப்படும் போது ஒரு புள்ளி இருக்க வேண்டும். வேறு வார்த்தைகளில் கூறுவதானால், சுழல்நிலை முறை இனி எப்போது அழைக்கப்படாது என்பதை தீர்மானிக்கும் சில நிபந்தனைகள் இருக்க வேண்டும், இது அழைப்பு அடுக்கை அவிழ்க்க அனுமதிக்கிறது. அத்தகைய நிபந்தனையை வரையறுக்க, நமது புறநிலையை வெளிப்படையாக்குவோம்: Fibonacci தொடரின் எண்கள் முழு எண்ணை விட குறைவாக இருக்கும் வரை அதைக் காண்பிக்கவும். MAX_VALUE .

இந்த நிபந்தனையை கணக்கில் எடுத்துக் கொள்ளும் புதிய printFibonacciWithCondition முறையை எழுதுவோம் . மேலும் புதிய திருத்தப்பட்ட முறையை பிரதான முறையில் அழைப்போம்.

public class Fibonacci {
    public static void main(String[] args) {
        System.out.println(0);
        System.out.println(1);
//        printFibonacci(0, 1);
        printFibonacciWithCondition(0, 1);
    }

    private static void printFibonacci(long penultimate, long previous) {
        long current = penultimate + previous;
        System.out.println(current);
        printFibonacci(previous, current);
    }

    private static void printFibonacciWithCondition(long penultimate, long previous) {
        long current = penultimate + previous;
        if (current > Integer.MAX_VALUE) {
            return;
        }
        System.out.println(current);
        printFibonacciWithCondition(previous, current);
    }
}

குறியீட்டை இயக்கிய பிறகு, வெளியீடு 1836311903 என்ற எண்ணுடன் முடிவடைவதைக் காண்கிறோம். இதற்கு முந்தைய எண் 1134903170. இந்த எண்களின் கூட்டுத்தொகை 2_971_215_073 ஆகும், இது முழு எண்ணை விட அதிகம்.MAX_VALUE (2_164_78 ) .

இந்த மாற்றம் தானாகவே நீண்ட வழிதல் பிழையை சரிசெய்தது. நீங்கள் தொடரில் அதிகமானவற்றைக் கணக்கிட வேண்டும் என்றால், BigInteger போன்ற பிற தரவு வகைகளைப் பயன்படுத்த வேண்டும் .

சுழல்நிலை இறங்குதல் மற்றும் அவிழ்த்தல்

எங்கள் குறியீடு எவ்வாறு செயல்படுத்தப்படுகிறது என்பதை படிப்படியாக பகுப்பாய்வு செய்வோம். இதைச் செய்ய, நாங்கள் ஒரு எதிரொலி முறையைச் சேர்த்து, printFibonacciWithCondition முறையின் சுழல்நிலை அழைப்பிற்கு முன்னும் பின்னும் அதை அழைப்போம் .

public class Fibonacci {
    public static void main(String[] args) {
        System.out.println(0);
        System.out.println(1);
        printFibonacciWithCondition(0, 1);
    }

    private static void printFibonacciWithCondition(long penultimate, long previous) {
        long current = penultimate + previous;
        if (current > Integer.MAX_VALUE) {
            return;
        }
        echo(true, penultimate, previous);
        System.out.println(current);
        printFibonacciWithCondition(previous, current);
        echo(false, penultimate, previous);
    }

    private static void echo(boolean isBeforeRecursiveCall, long penultimate, long previous) {
        if (isBeforeRecursiveCall) {
            System.out.printf("Before method call with args: %d, %d. Current number = ", penultimate, previous);
        } else {
            System.out.printf("After method call with args: %d, %d\n", penultimate, previous);
        }
    }
}

நிரல் இந்த வெளியீட்டை நமக்கு வழங்குகிறது:

0
1
args உடன் முறை அழைப்புக்கு முன்: 0, 1. தற்போதைய எண் = 1
args உடன் முறை அழைப்புக்கு முன்: 1, 1. தற்போதைய எண் = 2
args உடன் முறை அழைப்புக்கு முன்: 1, 2. தற்போதைய எண் = 3
args உடன் முறை அழைப்புக்கு முன்: 2
.
_
_
args உடன் முறை அழைப்புக்கு முன்: 13, 21. தற்போதைய எண் = 34
args உடன் முறை அழைப்புக்கு முன்: 21, 34. தற்போதைய எண் = 55
args உடன் முறை அழைப்புக்கு முன்: 34, 55. தற்போதைய எண் = 89 args
உடன் முறை அழைப்புக்கு முன்: 55, 89. தற்போதைய எண் = 144
args உடன் முறை அழைப்புக்கு முன்: 89, 144. தற்போதைய எண் = 233
args உடன் முறை அழைப்புக்கு முன்: 144, 233. தற்போதைய எண் = 377
args உடன் முறை அழைப்புக்கு முன்: 233, 377. தற்போதைய எண் = 610
args உடன் முறை அழைப்புக்கு முன்: 377 610. தற்போதைய எண் = 987
args உடன் முறை அழைப்புக்கு முன்: 610, 987. தற்போதைய எண் = 1597
args உடன் முறை அழைப்புக்கு முன்: 987, 1597. தற்போதைய எண் = 2584
args உடன் முறை அழைப்புக்கு முன்: 1597, 2581 முறை
முன் = 418 args உடன் அழைப்பு: 2584, 4181. தற்போதைய எண் = 6765
args உடன் முறை அழைப்புக்கு முன்: 4181, 6765. தற்போதைய எண் = 10946
args உடன் முறை அழைப்புக்கு முன்: 6765, 10946. தற்போதைய எண் = 17711 7
முறை அழைப்பிற்கு முன், 17109 தற்போதைய எண் = 28657
args உடன் முறை அழைப்புக்கு முன்: 17711, 28657. தற்போதைய எண் = 46368 args உடன் முறை அழைப்புக்கு முன்
: 28657, 46368. தற்போதைய எண் = 75025 args உடன் முறை அழைப்புக்கு முன்: 46368, 75025. 1213 முறை 9 க்கு முன் 5. 121393. தற்போதைய எண் = 196418 args உடன் முறை அழைப்புக்கு முன்: 121393, 196418. தற்போதைய எண் = 317811 args உடன் முறை அழைப்புக்கு முன்: 196418, 317811. தற்போதைய எண் = 514229 514229 முறையுடன் 5 எண் = 832040 முறைக்கு முன் args உடன் அழைக்கவும்: 514229, 832040. தற்போதைய எண் = 1346269 args உடன் முறைக்கு முன் அழைக்கவும்: 832040, 1346269. தற்போதைய எண் = 2178309 args உடன் முறை அழைப்புக்கு முன்: 13462830 = 932 Current எண் 7.5








args உடன் முறை அழைப்புக்கு முன்: 2178309, 3524578. தற்போதைய எண் = 5702887
args உடன் முறைக்கு முன் அழைப்பு: 3524578, 5702887. தற்போதைய எண் = 9227465 args உடன் முறை
அழைப்புக்கு
முன்: 5602287 args உடன் முறை அழைப்பு: 9227465, 14930352. தற்போதைய எண் = 24157817
args உடன் முறை அழைப்புக்கு முன்: 14930352, 24157817. தற்போதைய எண் = 39088169
args உடன் முறை அழைப்புக்கு முன்: 24157817, 39088169 உடன் முறை
: 39088169 088169, 63245986. தற்போதைய எண் = 102334155
முறைக்கு முன் args உடன் அழைக்கவும்: 63245986, 102334155. தற்போதைய எண் = 165580141
முறைக்கு முன் args உடன் அழைக்கவும்: 102334155, 165580141. தற்போதைய எண் = 267914296
args உடன் முறை அழைப்புக்கு முன்: 165580141, 267914296. தற்போதைய எண் = 433494437
args உடன் முறை அழைப்புக்கு முன்: 267914296, 433494437. தற்போதைய எண் = 701408733 முறை 430 உடன் 430, 430 தற்போதைய
எண் = 1134903170
முறைக்கு முன் args உடன் அழைக்கவும்: 701408733, 1134903170. தற்போதைய எண் = 1836311903
ஆப்டர் மெத்தட் கால் வித் args: 701408733, 113490317
ஆப்டர் மெத்தட் கால் வித் ஆர்க்ஸ்: 433494437, 701408733 ஆப்டர் மெத்தட் கால் வித் 67908733 ஆப்டர் மெத்தட் கால் வித் 67908733 args
:
165580141, 267914296
முறைக்கு பிறகு args உடன் அழைக்கவும்: 102334155, 165580141
ஆப்டர் மெத்தட் கால் வித் ஆர்க்ஸ்: 63245986, 102334155
ஆப்டர் மெத்தட் கால் வித் ஆர்க்ஸுடன்: 39088169, 63245986
args உடன் முறை அழைப்பு: 24157817, 39088169
args உடன் முறை அழைப்பு: 14930352,
24157817 பிறகு args உடன் முறை அழைப்பு: 9227465, 14930352 பிறகு args உடன் முறை அழைப்பு
: 742 95 4578
, 5702887
args உடன் முறை அழைப்புக்குப் பிறகு : 2178309, 3524578
After method call with args: 1346269, 2178309
After method call with args: 832040, 1346269 After method call with args: 514229, 832040 After method call with args: 514229, 832040 After method call with args: 514229, 832040
After method call with31 args: 196418, 317811 பிறகு args உடன் முறை அழைப்பு: 121393, 196418 args உடன் முறை அழைப்பு: 75025, 121393 args உடன் முறை அழைப்பு: 46368, 75025





args உடன் மெத்தட் கால்: 28657, 46368
After method call with args: 17711, 28657
After method call with args: 10946, 17711
After method call with args: 6765, 10946
After method call with 6181, 6181
argsக்குப் பிறகு : 2584, 4181
After method call with args: 1597, 2584 After method call with args
: 987, 1597 After method call with args: 610, 987 After method call with args: 377, 610 After method call with args, 333 args க்குப் பிறகு : args உடன் முறை அழைப்பு: 144, 233 args உடன் முறை அழைப்பு: 89, 144 args உடன் முறை அழைப்பு: 55, 89 args உடன் முறை அழைப்பு: 34, 55 args உடன் முறை அழைப்பு: 21, 34








args உடன் மெத்தட் கால்: 13, 21
After method call with args: 8, 13
After method call with args: 5, 8
After method call with args: 3, 5
after method call with args: 2, 3
After method call with args : 1, 2
args உடன் முறை அழைப்பு: 1, 1
args உடன் முறை அழைப்புக்குப் பிறகு: 0, 1

என்ன நடக்கிறது என்பதற்கான காட்சிப்படுத்தல் இங்கே.

மீண்டும் சொல்கிறேன்: printFibonacciWithCondition முறை அழைக்கப்படுகிறது. இது தற்போதைய எண்ணைக் கணக்கிடுகிறது. இது நமக்குப் பொருத்தமாக இருந்தால், நாங்கள் அதைக் காண்பிப்போம் மற்றும் புதிய வாதங்களுடன் மீண்டும் printFibonacciWithCondition முறையை அழைக்கிறோம்.

சுழல்நிலை முறை என்று அழைக்கப்படும் வரை, இது "சுழற்சி வம்சாவளி" என்று அழைக்கப்படுகிறது. ரிகர்சிவ் டெர்மினேட்ஸ் மற்றும் முறை அழைப்புகள் திரும்பும் போது, ​​அழைப்பு அடுக்கு "அன்வைண்டிங்" என்று கூறுகிறோம்.

நிரலாக்கத்தில் மறுநிகழ்வு ஒரு சுவாரஸ்யமான தலைப்பு. பொருளில் சிறந்த கைப்பிடியைப் பெற, எங்கள் பணியை சிறிது மாற்றுவோம். Fibonacci தொடரின் முழு எண்களை தாண்டாத அனைத்து எண்களையும் இறங்கு வரிசையில் வெளியிடுவதே புதிய பணியாகும் . இந்த பணிக்கு தேவையான அனைத்து குறியீடுகளையும் ஏற்கனவே எழுதிவிட்டோம். தற்போதைய எண்ணைக் காண்பிக்கும் மற்றும் சுழல்நிலை முறையை அழைப்பதற்கான வரிசையை மாற்றுவது மட்டுமே மீதமுள்ளது. அதாவது, முதல் எடுத்துக்காட்டில், கணக்கிடப்பட்ட எண் "இறங்கும்" போது காட்டப்பட்டது, ஆனால் இப்போது நாம் "மிகக் கீழே இறங்க வேண்டும்" பின்னர் "மீண்டும் செல்லும் வழியில்" எண்களைக் காட்ட வேண்டும். நிச்சயமாக, முக்கிய முறையில், வரிசையின் இரண்டு ஆரம்ப எண்களை (பூஜ்ஜியம் மற்றும் ஒன்று) நாம் சுழல்நிலை முறையை அழைத்த பிறகு காட்சிப்படுத்திய பிறகு மாற்றுவோம். வாசிப்புத்திறனுக்காக,முறை.

public class Fibonacci {
    public static void main(String[] args) {
        printFibonacciWithCondition(0, 1);
        System.out.println(1);
        System.out.println(0);
    }

    private static void printFibonacciWithCondition(long penultimate, long previous) {
        long current = penultimate + previous;
        if (current > Integer.MAX_VALUE) {
            return;
        }
        printFibonacciWithCondition(previous, current);
        System.out.println(current);
    }
}

வெளியீடு இருக்கும்:

1836311903
1134903170
701408733
433494437
267914296
165580141
102334155
63245986
39088169
24157817
14930352
9227465
5702887
3524578
2178309
1346269
832040
514229
317811
196418
121393
75025
46368
28657
17711
10946
6765
4181
2584
1597
987
610
377
233
144
89
55
34
21
13
8
5
3
2
1
1
0