精华内容
下载资源
问答
  • org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappi
  • Recursion

    2019-07-09 14:54:00
    Reference:http://web.mit.edu/6.005/www/fa16/classes/14-recursion/ Objectives After today’s class, you should: be able to decompose a recursive problem into recursive steps and base cases know w...

    Reference: http://web.mit.edu/6.005/www/fa16/classes/14-recursion/

    Objectives

    After today’s class, you should:

    • be able to decompose a recursive problem into recursive steps and base cases
    • know when and how to use helper methods in recursion
    • understand the advantages and disadvantages of recursion vs. iteration

    Recursion

    In today’s class, we’re going to talk about how to implement a method, once you already have a specification. We’ll focus on one particular technique, recursion. Recursion is not appropriate for every problem, but it’s an important tool in your software development toolbox, and one that many people scratch their heads over. We want you to be comfortable and competent with recursion, because you will encounter it over and over. (That’s a joke, but it’s also true.)

    Since you’ve taken 6.01, recursion is not completely new to you, and you have seen and written recursive functions like factorial and fibonacci before. Today’s class will delve more deeply into recursion than you may have gone before. Comfort with recursive implementations will be necessary for upcoming classes.

    A recursive function is defined in terms of base cases and recursive steps.

    • In a base case, we compute the result immediately given the inputs to the function call.
    • In a recursive step, we compute the result with the help of one or more recursive calls to this same function, but with the inputs somehow reduced in size or complexity, closer to a base case.

    Consider writing a function to compute factorial. We can define factorial in two different ways:

    ProductRecurrence relation

     (where the empty product equals
      multiplicative identity 1)

    which leads to two different implementations:

    IterativeRecursive
    public static long factorial(int n) { long fact = 1; for (int i = 1; i <= n; i++) { fact = fact * i; } return fact; }
    public static long factorial(int n) { if (n == 0) { return 1; } else { return n * factorial(n-1); } }

    In the recursive implementation on the right, the base case is n = 0, where we compute and return the result immediately: 0! is defined to be 1. The recursive step is n > 0, where we compute the result with the help of a recursive call to obtain (n-1)!, then complete the computation by multiplying by n.

    To visualize the execution of a recursive function, it is helpful to diagram the call stack of currently-executing functions as the computation proceeds.

    Let’s run the recursive implementation of factorial in a main method:

    public static void main(String[] args) { long x = factorial(3); }

    At each step, with time moving left to right:

    starts in
    main

    calls
    factorial(3)

    calls
    factorial(2)

    calls
    factorial(1)

    calls
    factorial(0)

    returns to
    factorial(1)

    returns to
    factorial(2)

    returns to
    factorial(3)

    returns to
    main

    main
     
    factorial
    n = 3
     
    main
    x
    factorial
    n = 2
     
    factorial
    n = 3
     
    main
    x
    factorial
    n = 1
     
    factorial
    n = 2
     
    factorial
    n = 3
     
    main
    x
    factorial
    n = 0
    returns 1
    factorial
    n = 1
     
    factorial
    n = 2
     
    factorial
    n = 3
     
    main
    x
    factorial
    n = 1
    returns 1
    factorial
    n = 2
     
    factorial
    n = 3
     
    main
    x
    factorial
    n = 2
    returns 2
    factorial
    n = 3
     
    main
    x
    factorial
    n = 3
    returns 6
    main
    x
    main
    x = 6

    In the diagram, we can see how the stack grows as main calls factorialand factorial then calls itself, until factorial(0) does not make a recursive call. Then the call stack unwinds, each call to factorial returning its answer to the caller, until factorial(3) returns to main.

    Here’s an interactive visualization of factorial. You can step through the computation to see the recursion in action. New stack frames grow down instead of up in this visualization.

    You’ve probably seen factorial before, because it’s a common example for recursive functions. Another common example is the Fibonacci series:

    /**
     * @param n >= 0
     * @return the nth Fibonacci number 
     */
    public static int fibonacci(int n) { if (n == 0 || n == 1) { return 1; // base cases } else { return fibonacci(n-1) + fibonacci(n-2); // recursive step } }

    Fibonacci is interesting because it has multiple base cases: n=0 and n=1. You can look at an interactive visualization of Fibonacci. Notice that where factorial’s stack steadily grows to a maximum depth and then shrinks back to the answer, Fibonacci’s stack grows and shrinks repeatedly over the course of the computation.

    READING EXERCISES

    Recursive factorial
    Recursive Fibonacci

    Choosing the Right Decomposition for a Problem

    Finding the right way to decompose a problem, such as a method implementation, is important. Good decompositions are simple, short, easy to understand, safe from bugs, and ready for change.

    Recursion is an elegant and simple decomposition for some problems. Suppose we want to implement this specification:

    /**
     * @param word consisting only of letters A-Z or a-z
     * @return all subsequences of word, separated by commas,
     * where a subsequence is a string of letters found in word 
     * in the same order that they appear in word.
     */
    public static String subsequences(String word)

    For example, subsequences("abc") might return "abc,ab,bc,ac,a,b,c,". Note the trailing comma preceding the empty subsequence, which is also a valid subsequence.

    This problem lends itself to an elegant recursive decomposition. Take the first letter of the word. We can form one set of subsequences that include that letter, and another set of subsequences that exclude that letter, and those two sets completely cover the set of possible subsequences.

     1 public static String subsequences(String word) { 2 if (word.isEmpty()) { 3 return ""; // base case 4 } else { 5 char firstLetter = word.charAt(0); 6 String restOfWord = word.substring(1); 7 8 String subsequencesOfRest = subsequences(restOfWord); 9 10 String result = ""; 11 for (String subsequence : subsequencesOfRest.split(",", -1)) { 12 result += "," + subsequence; 13 result += "," + firstLetter + subsequence; 14 } 15 result = result.substring(1); // remove extra leading comma 16 return result; 17 } 18 }

    READING EXERCISES

    subsequences("c")
    subsequences("gc")

    Structure of Recursive Implementations

    A recursive implementation always has two parts:

    • base case, which is the simplest, smallest instance of the problem, that can’t be decomposed any further. Base cases often correspond to emptiness – the empty string, the empty list, the empty set, the empty tree, zero, etc.

    • recursive step, which decomposes a larger instance of the problem into one or more simpler or smaller instances that can be solved by recursive calls, and then recombines the results of those subproblems to produce the solution to the original problem.

    It’s important for the recursive step to transform the problem instance into something smaller, otherwise the recursion may never end. If every recursive step shrinks the problem, and the base case lies at the bottom, then the recursion is guaranteed to be finite.

    A recursive implementation may have more than one base case, or more than one recursive step. For example, the Fibonacci function has two base cases, n=0 and n=1.

    READING EXERCISES

    Recursive structure

    Helper Methods

    The recursive implementation we just saw for subsequences() is one possible recursive decomposition of the problem. We took a solution to a subproblem – the subsequences of the remainder of the string after removing the first character – and used it to construct solutions to the original problem, by taking each subsequence and adding the first character or omitting it. This is in a sense a direct recursive implementation, where we are using the existing specification of the recursive method to solve the subproblems.

    In some cases, it’s useful to require a stronger (or different) specification for the recursive steps, to make the recursive decomposition simpler or more elegant. In this case, what if we built up a partial subsequence using the initial letters of the word, and used the recursive calls to complete that partial subsequence using the remaining letters of the word? For example, suppose the original word is “orange”. We’ll both select “o” to be in the partial subsequence, and recursively extend it with all subsequences of “range”; and we’ll skip “o”, use “” as the partial subsequence, and again recursively extend it with all subsequences of “range”.

    Using this approach, our code now looks much simpler:

    /**
     * Return all subsequences of word (as defined above) separated by commas,
     * with partialSubsequence prepended to each one.
     */
    private static String subsequencesAfter(String partialSubsequence, String word) { if (word.isEmpty()) { // base case return partialSubsequence; } else { // recursive step return subsequencesAfter(partialSubsequence, word.substring(1)) + "," + subsequencesAfter(partialSubsequence + word.charAt(0), word.substring(1)); } }

    This subsequencesAfter method is called a helper method. It satisfies a different spec from the original subsequences, because it has a new parameter partialSubsequence. This parameter fills a similar role that a local variable would in an iterative implementation. It holds temporary state during the evolution of the computation. The recursive calls steadily extend this partial subsequence, selecting or ignoring each letter in the word, until finally reaching the end of the word (the base case), at which point the partial subsequence is returned as the only result. Then the recursion backtracks and fills in other possible subsequences.

    To finish the implementation, we need to implement the original subsequencesspec, which gets the ball rolling by calling the helper method with an initial value for the partial subsequence parameter:

    public static String subsequences(String word) { return subsequencesAfter("", word); }

    Don’t expose the helper method to your clients. Your decision to decompose the recursion this way instead of another way is entirely implementation-specific. In particular, if you discover that you need temporary variables like partialSubsequence in your recursion, don’t change the original spec of your method, and don’t force your clients to correctly initialize those parameters. That exposes your implementation to the client and reduces your ability to change it in the future. Use a private helper function for the recursion, and have your public method call it with the correct initializations, as shown above.

    READING EXERCISES

    Unhelpful 1
    Unhelpful 2
    Unhelpful 3

    Choosing the Right Recursive Subproblem

    Let’s look at another example. Suppose we want to convert an integer to a string representation with a given base, following this spec:

    /**
     * @param n integer to convert to string
     * @param base base for the representation. Requires 2<=base<=10.
     * @return n represented as a string of digits in the specified base, with 
     *           a minus sign if n<0.
     */
    public static String stringValue(int n, int base)

    For example, stringValue(16, 10) should return "16", and stringValue(16, 2) should return "10000".

    Let’s develop a recursive implementation of this method. One recursive step here is straightforward: we can handle negative integers simply by recursively calling for the representation of the corresponding positive integer:

    if (n < 0) return "-" + stringValue(-n, base);

    This shows that the recursive subproblem can be smaller or simpler in more subtle ways than just the value of a numeric parameter or the size of a string or list parameter. We have still effectively reduced the problem by reducing it to positive integers.

    The next question is, given that we have a positive n, say n=829 in base 10, how should we decompose it into a recursive subproblem? Thinking about the number as we would write it down on paper, we could either start with 8 (the leftmost or highest-order digit), or 9 (the rightmost, lower-order digit). Starting at the left end seems natural, because that’s the direction we write, but it’s harder in this case, because we would need to first find the number of digits in the number to figure out how to extract the leftmost digit. Instead, a better way to decompose n is to take its remainder modulo base (which gives the rightmost digit) and also divide by base (which gives the subproblem, the remaining higher-order digits):

    return stringValue(n/base, base) + "0123456789".charAt(n%base);

    Think about several ways to break down the problem, and try to write the recursive steps. You want to find the one that produces the simplest, most natural recursive step.

    It remains to figure out what the base case is, and include an if statement that distinguishes the base case from this recursive step.

    READING EXERCISES

    Implementing stringValue
    Calling stringValue

    Recursive Problems vs. Recursive Data

    The examples we’ve seen so far have been cases where the problem structure lends itself naturally to a recursive definition. Factorial is easy to define in terms of smaller subproblems. Having a recursive problem like this is one cue that you should pull a recursive solution out of your toolbox.

    Another cue is when the data you are operating on is inherently recursive in structure. We’ll see many examples of recursive data a few classes from now, but for now let’s look at the recursive data found in every laptop computer: its filesystem. A filesystem consists of named files. Some files are folders, which can contain other files. So a filesystem is recursive: folders contain other folders which contain other folders, until finally at the bottom of the recursion are plain (non-folder) files.

    The Java library represents the file system using java.io.File. This is a recursive data type, in the sense that f.getParentFile() returns the parent folder of a file f, which is a File object as well, and f.listFiles() returns the files contained by f, which is an array of other File objects.

    For recursive data, it’s natural to write recursive implementations:

    /**
     * @param f a file in the filesystem
     * @return the full pathname of f from the root of the filesystem
     */
    public static String fullPathname(File f) { if (f.getParentFile() == null) { // base case: f is at the root of the filesystem return f.getName(); } else { // recursive step return fullPathname(f.getParentFile()) + "/" + f.getName(); } }

    Recent versions of Java have added a new API, java.nio.Files and java.nio.Path, which offer a cleaner separation between the filesystem and the pathnames used to name files in it. But the data structure is still fundamentally recursive.

    Reentrant Code

    Recursion – a method calling itself – is a special case of a general phenomenon in programming called reentrancy. Reentrant code can be safely re-entered, meaning that it can be called again even while a call to it is underway. Reentrant code keeps its state entirely in parameters and local variables, and doesn’t use static variables or global variables, and doesn’t share aliases to mutable objects with other parts of the program, or other calls to itself.

    Direct recursion is one way that reentrancy can happen. We’ve seen many examples of that during this reading. The factorial() method is designed so that factorial(n-1) can be called even though factorial(n) hasn’t yet finished working.

    Mutual recursion between two or more functions is another way this can happen – A calls B, which calls A again. Direct mutual recursion is virtually always intentional and designed by the programmer. But unexpected mutual recursion can lead to bugs.

    When we talk about concurrency later in the course, reentrancy will come up again, since in a concurrent program, a method may be called at the same time by different parts of the program that are running concurrently.

    It’s good to design your code to be reentrant as much as possible. Reentrant code is safer from bugs and can be used in more situations, like concurrency, callbacks, or mutual recursion.

    When to Use Recursion Rather Than Iteration

    We’ve seen two common reasons for using recursion:

    • The problem is naturally recursive (e.g. Fibonacci)
    • The data is naturally recursive (e.g. filesystem)

    Another reason to use recursion is to take more advantage of immutability. In an ideal recursive implementation, all variables are final, all data is immutable, and the recursive methods are all pure functions in the sense that they do not mutate anything. The behavior of a method can be understood simply as a relationship between its parameters and its return value, with no side effects on any other part of the program. This kind of paradigm is called functional programming, and it is far easier to reason about than imperative programmingwith loops and variables.

    In iterative implementations, by contrast, you inevitably have non-final variables or mutable objects that are modified during the course of the iteration. Reasoning about the program then requires thinking about snapshots of the program state at various points in time, rather than thinking about pure input/output behavior.

    One downside of recursion is that it may take more space than an iterative solution. Building up a stack of recursive calls consumes memory temporarily, and the stack is limited in size, which may become a limit on the size of the problem that your recursive implementation can solve.

    Common Mistakes in Recursive Implementations

    Here are two common ways that a recursive implementation can go wrong:

    • The base case is missing entirely, or the problem needs more than one base case but not all the base cases are covered.
    • The recursive step doesn’t reduce to a smaller subproblem, so the recursion doesn’t converge.

    Look for these when you’re debugging.

    On the bright side, what would be an infinite loop in an iterative implementation usually becomes a StackOverflowError in a recursive implementation. A buggy recursive program fails faster.

    READING EXERCISES

    subsequences("123456")

    Summary

    We saw these ideas:

    • recursive problems and recursive data
    • comparing alternative decompositions of a recursive problem
    • using helper methods to strengthen a recursive step
    • recursion vs. iteration

    The topics of today’s reading connect to our three key properties of good software as follows:

    • Safe from bugs. Recursive code is simpler and often uses immutable variables and immutable objects.

    • Easy to understand. Recursive implementations for naturally recursive problems and recursive data are often shorter and easier to understand than iterative solutions.

    • Ready for change. Recursive code is also naturally reentrant, which makes it safer from bugs and ready to use in more situations.

    转载于:https://www.cnblogs.com/skying555/p/11157352.html

    展开全文
  • js-recursion-源码

    2021-06-19 07:54:01
    这个 repo 充满了伴随的 JavaScript 递归类的练习。 指示 使用此存储库右侧的下载 Zip按钮。 从您下载的 zip 副本中提取文件。 按照课堂上的指示进行开放练习。 按照README.md文件提供的说明进行操作。...
  • php-sebastian-recursion-context3-3.0.0-1.el7.remi.noarch.rpm
  • recursion.js

    2019-11-05 17:41:50
    简易的三层迭代方式,放入parent,可format出三层分类。
  • Recursion Theory

    2018-03-21 15:58:21
    介绍了Rice原理及post和Kleene问题原理,分析hierarchy,自动推论等
  • left-recursion:在Haskell解析器中消除左递归的快速说明
  • 递归 按定义:递归是递归过程或定义的重复应用。 要理解我介绍了计算器的答案,我们可以举一个例子递归,你想知道什么是资格成为美国的总理,这是假设: 您必须是美国公民,以下是规则: 您是美国(或)自然出生...
  • 递归 这是我作为Hack Reactor的学生完成的一个项目。 这个项目是一对的。 该项目旨在探索递归的使用,同时重新实现以下本机javascript和DOM方法polyfill: getElementByClassName() parseJSON() ...
  • Java 阶乘递归 找到 n 数阶乘的非常简单的方法。
  • 递归可视化器 递归可视化工具是python工具,可通过动画可视化递归树,并为递归函数绘制递归树。 它适用于几乎所有类型的递归函数。 只需将递归可视化装饰器添加到您的函数中,然后让它完成其余的工作即可。...
  • EXAM_recursion-源码

    2021-06-02 09:34:14
    EXAM_recursion 从同一方法中调用方法。 有些问题通过递归比迭代更容易解决。 例如。 合并排序(?)的排序算法。 一般来说,递归方法比迭代方法占用更少的空间。 另一方面,在几乎所有情况下,由于使用堆栈,使用...
  • RuntimeError: maximum recursion depth exceeded in cmp Error: <stdin>: syntax error in line 295 near '' </stdin></module></code></pre>该提问来源于开源项目:akme/...
  • Recursion examples
  • tail recursion

    2019-02-16 11:19:02
    What is Tail Recursion? Provide an example and a simple explanation. Here we provide a simple tutorial and example of a normal non-tail recursive solution to the Factorial problem in Java, and then we...

    What is Tail Recursion? Provide an example and a simple explanation.
    Here we provide a simple tutorial and example of a normal non-tail recursive solution to the Factorial problem in Java, and then we can also go over the same problem but use a tail recursive solution in Python. Don’t worry if you don’t know Python, the code is very intuitive and easy to understand even if you come from another background.


    If you read our recursion tutorial, then you should already have a good idea of how the recursive solution for the factorial works. This is the Java code that will calculate the factorial of a number:

    public int factorial (int x)
    {
      if (x == 1)
      {
        return 1;  //base case
      }

      else  /*recursive case*/
       return factorial(x-1) * x;
    }
    Let’s say that we want to calculate the factorial of 4 using the function above. So, what exactly happens when the value of 4 is passed into the function above? Well, here is the sequence of calls that will be made – with the first to last going from top to bottom:

    factorial (4) ;
    return factorial(3) * 4;
    return factorial(2) * 3;
    return factorial(1) * 2;
    return 1;
     

     
    The thing that you should pay special attention to is the fact that in order to reach the final value of the factorial (which is 24), each and every function call must be executed to completion. You should be able to see that – in order to know the factorial of 4 we must find the factorial of 3 multiplied by 4, and in order to get the factorial of 3, we must get the factorial of 2 multiplied by 3, and in order to get the factorial of 2 we must get the factorial of 1 multiplied by 2, and finally, we know that the factorial of 1 is equal to 1 so 1 is returned because that is our base case which will actually stop the recursion. Then, a 2 will be returned (factorial(1) * 2 is equal to 2), and then a 6 will be returned by factorial(2) *3 and so on and so forth until the final value “24” is returned by the function.

    The concept that we are trying to emphasize here is that every function call must run to completion in order for us to finally get to the correct value of “24”. This is different from tail recursion, and you will see why once we go over a variation of the factorial function that uses tail recursion.

    Tail Recursion Factorial Implementation in Python
    It’s much easier to understand tail recursion with an actual example followed by an explanation of that example. With that in mind, let’s go over an example of a Factorial solution in Python that uses tail recursion instead of normal recursion. We use Python because it’s easier for the sake of illustrating our example.

    Example of Tail Recursion
    def factorial(i, current_factorial=1):
      if i == 1:
        return current_factorial
      else:
        return factorial(i - 1, current_factorial * i)
    Note that we provide a default argument of 1 for the current_factorial, but that only applies to the very first call of the function. When the factorial function is called recursively the default argument is overridden with whatever value is passed by the recursive call. We need to have that second argument there because it will hold the current factorial value which we intend on passing into the function.

    Breaking down our example of tail recursion
    Now, let’s suppose again that we want to calculate the factorial of 4. Note that the very first call to factorial(4) really is factorial(4, 1), because we have a default argument of 1 for the second parameter in factorial. So, we would end up with a series of calls that look like this:

    factorial(4, 1)
    return factorial (3,  1 * 4 ) //i == 4
    return factorial (2,  4 *3 ) //i == 3
    return factorial (1,  12 *2 ) //i == 2
    return 24  //i == 1
    So, you can see that each time the factorial function is called in our example, a new value for the current_factorial variable is passed into the factorial function. The function is basically updating the current_factorial variable with each call to the function. We are able to maintain the current factorial value because this function accepts 2 arguments/parameters – not just 1 like our normal, non-tail recursive factorial function above.

    The difference between tail recursion and normal recursion
    You can see that once we make a call to factorial (1, 12 *2 ), it will return the value of 24 – which is actually the answer that we want. The most important thing to notice about that is the fact that all of the recursive calls to factorial (like factorial (2, 4 *3 ), factorial (3, 1*4 ), etc) do not actually need to return in order to get the final value of 24 – you can see that we actually arrive at the value of 24 before any of the recursive calls actually return. So, we can say that a function is tail recursive if the final result of the recursive call – in this case 24 – is also the final result of the function itself, and that is why it’s called “tail” recursion – because the final function call (the tail-end call) actually holds the final result. If we compare that with our earlier example of “normal” recursion, you should see the difference – the “normal” recursive call is certainly not in it’s final state in the last function call, because all of the recursive calls leading up to the last function call must also return in order to actually come up with the final answer.


     
    Tail recursion and stack frames
    If you read our Recursion Tutorial, then you understand how stack frames work, and how they are used in recursion. We won’t go into detail here since you can just read that article, but basically each recursive call in a normal recursive function results in a separate stack frame as you can see in this graphic which assumes a call of Factorial(3) is being made:

    Tail call optimization explanation
    Tail call optimization is the process by which a tail recursive function call is optimized to use just one stack frame. You should definitely read our detailed explanation on tail call optimization here: Tail call optimization.

    Tail recursion versus normal recursion
    You can see that once a final call to the tail recursive factorial is made – the “factorial (2, 3 *2 )”, it will return the value of 6 – which is actually the answer that we want. The most important thing to notice about that is the fact that all of the recursive calls to factorial (like factorial (3, 1 * 3 ), factorial (2, 3 *2 ), etc) do not actually need to return in order to get the final value of 6 – you can see that we actually arrive at the value of 6 before any of the recursive calls actually return. So, we can say that a function is tail recursive if the final result of the recursive call – in this case 6 – is also the final result of the function itself, and that is why it’s called “tail” recursion – because the final function call (the tail-end call) actually holds the final result. If we compare that with our earlier example of “normal” recursion, you should see the difference – the “normal” recursive call is certainly not in it’s final state in the last function call, because all of the recursive calls leading up to the last function call must also return in order to actually come up with the final answer.

    Tail call optimization explanation
    In our tail recursive example, the recursive calls to factorial do not actually need a new stack frame for each and every recursive call that is made. This is because the calculation is made within the function parameters/arguments – and the final function call actually contains the final result, and the final result does not rely on the return value of each and every recursive call. However, an important point that you should make sure you understand is that it is up to the compiler/interpreter of the particular language to determine whether or not the recursive calls in a tail recursive function actually use an extra stack frame for each recursive call to the function.


     
    Some language’s compilers choose to optimize tail recursive functions because they know that the final result will be in the very last function call. So, the compilers will not create a new stack frame for each recursive call, and will instead just re-use the same stack frame. This saves memory overhead by using a constant amount of stack space, which is why it is called tail recursion optimization. But, keep in mind that some compilers do not perform this optimization on tail recursive functions, which means that the tail recursive function will be run like a normal recursive function and will use a new stack frame for each and every function call.

    Python does not use tail recursive optimization
    In our example of tail recursion, we used Python because it made it easy to illustrate our example. But, it’s interesting to note that Python does not actually utilize tail recursive optimization, which means that it treats tail recursive calls just as it would treat normal recursive calls. This of course means that tail recursive calls in Python will be less efficient then they would be if they were optimized, but there are valid reasons the creator of Python decided not to add this feature.

    Tail Recursion versus Iteration
    Tail recursion can be as efficient as iteration if the compiler uses what is known as tail recursion optimization. If that optimization is not used, then tail recursion is just as efficient as normal recursion.


     
    Is Tail Recursion optimization automatic?

    No, tail recursion optimization is a feature that must be built in as part of the compiler, as we mentioned before.

    Tail recursion optimization and stack overflow
    Because tail recursion optimization essentially makes your tail recursive call equivalent to an iterative function, there is no risk of having the stack overflow in an optimized tail recursive function.

    Tail Recursion optimization in Java
    Tail recursion optimization is not actually supported by Java because the Java standard does not require that tail recursion be supported, although there are some JVM implementations that do support it as an add-on. But, tail recursion itself (note that we left out the “optimization” part) is supported in Java because it is just a special case of normal recursion – so there’s really nothing extra that Java JVM has to do in order to support tail recursion versus normal recursion. Because there is no tail recursion optimization in Java, code that uses tail recursion will behave like a normal recursive call, and will use multiple stack frames.

    Tail call optimization versus tail call elimination
    Both tail call optimization and tail call elimination mean exactly the same thing and refer to the same exact process in which the same stack frame is reused by the compiler, and unnecessary memory on the stack is not allocated.

    What does TRO stand for in computer science?
    TRO stands for Tail recursion optimization.

    What does TCE stand for in computer science?
    TCE stands for Tail recursion elimination.

    展开全文
  • 【Python】Recursion 典型问题 这里是一个刚刚上手Python的小白整理的一些题型。期末考试将至,供复习之用。 部分题型及代码来源于网络,某些部分进行了适当的修改,如有侵权请尽快联系我哦~ ~~ 1. 二分法找...

    【Python】Recursion递归 典型问题

    这里是一个刚刚上手Python的小白整理的一些题型。大部分题目来自平时上课的例题及习题,在此稍作整理,以供复习之用。
    因为平时全英文,有些专有名词不知道中文名,就直接用英文代替啦~

    1. 二分法找有序数列指定值

    写一个程序binary_search(x, y), 输入一个list x (你可以假设里面的数据已经按升序排列) 然后输入想要查找的数据y,使得函数返回y的index,或者当y不属于x时返回 “Not found”.

    def binary_search(x,y):
        a = 0
        b = len(x) #如果这里打的是‘len(x)-1’,那么如果要查找最后一个数就会陷入死循环
        c = (a+b)//2
        if y not in x:
            return 'Not found'
        while x[c] != y:
            if x[c] < y:
                a = c
            elif x[c] > y:
                b = c
            c = (a+b)//2
        return c
    

    这个问题如果以问法的话就只能写loop的函数,但其实它也可以用recursion解:

    def binary_search(min, max, d, n): '''初始mid=0, max=len(d)'''
        mid = (min+max)//2
        if n not in d:
            return 'Not found'
        elif d[mid] < n:
            return binary_search(mid, max, d, n)
        elif d[mid] > n:
            return binary_search(min, mid, d, n)
        else:
            return mid
    

    2. 字符串(非重复)全排列

    def permutation(array):
        if len(array) <= 1:
            return [array]
        res = []
        for i in range(len(array)):
            s = array[:i] + array[i+1:] #每次拿出一个元素
            p = permutation(s) #将剩下元素全排列
            for x in p:
                res.append(array[i:i+1]+x) #将该元素插到剩下元素后面
    

    (或者要做有重复的也很简单,再写一个function把重复的去掉即可)

    3. 爬楼梯问题

    1)假设一共有n级台阶,每次可以跨1步或3步,求要爬完这一段台阶一共有多少种可能

    (可以看成是斐波那契数列)

    def func(n):
        if n <= 2:
            return 1
        elif n == 3:
            return 2
        elif n > 3:
            return func(n-1) + func(n-3)
    
    2)假设一共有n级台阶,每次可以跨1步或3步,列举要爬完这一段台阶的所有可能
    def func(n):
        res = []
        if n == 1:
            return '1'
        elif n == 2:
            return '11'
        elif n == 3:
            return ['111', '3']
        elif n > 3:
            for i in func(n-1):
                res.append(i + '1')
            for i in func(n-3):
                res.append(i + '3')
            return res
    

    4. Tower of Hanoi

    1)假设一共有n块圆盘,求总共需要移动的次数
    def han(n):
        k = 0
        if n == 1:
            k += 1
        elif n > 1:
            k += 2*han(n-1) + 1
        return k
    
    2)假设一共有n块圆盘,求每次移动的步骤
    def han(disc, frm, to, temp):
        if disc == 1:
            print('move disc' , 1, 'from', frm, 'to', to)
        elif disc > 1:
            han(disc-1, frm, temp, to)
            print('move disc' , disc, 'from', frm, 'to', to)
            han(disc-1, temp, to, frm)
    

    5. 列举一个字符串中每一个字母的大小写全排列

    Write a program to read a string input and then output a list of all possible case permutations of the string.(比如“Ab”,就有“ab,Ab,aB,AB”四种可能)

    def case_permutation(array):
        res = []
        if len(array) <= 1:
            res.append(array.lower())
            res.append(array.upper())
            return res
        else:
            s = array[0]
            array = array[1:]
            p = case_permutation(array)
            for x in p:
                res.append(s.lower()+x)
                res.append(s.upper()+x)
        return res
    

    6. 判断回文

    def is_pal(line):
        n = len(line)
        if n <= 1:
            return True
        elif n <= 3:
            if linr[0] == line[n-1]:
                return True
            else:
                return False
        elif n > 3:
            if line[0] == line[n-1]:
                return is_pal(line[1:n-1]) '''删去第一个和最后一个字符'''
            else:
                return False
    

    7. 寻找一个set的power set

    (这里我自动默认已经把set转换成了一个list)

    def power(s):
        res = []
        if len(s) == 1:
            res.append([s[0]])
            res.append([])
        elif len(s) > 1:
            for i in power(s[1:]):
                res.append(i)
                res.append(i+[s[0]]) 
        return res
    

    目前暂时整理了这么多,但只要把套路摸透,就会发现所有的recursion核心思路基本都一样
    祝大家考试顺利,过三爆四~~

    展开全文
  • 成功解决RecursionError: maximum recursion depth exceeded 目录 解决问题 解决思路 解决方法 解决问题 在程序pyinstaller打包的时候,遇到递归错误, RecursionError: maximum recursion ...

    成功解决RecursionError: maximum recursion depth exceeded

    目录

    解决问题

    解决思路

    解决方法


    解决问题

    在程序pyinstaller打包的时候,遇到递归错误,

    RecursionError: maximum recursion depth exceeded

    解决思路

    递归错误:超过最大递归深度

    解决方法

    直接修改默认的最大递归深度即可!

    第一步,在py对应的spec文件内添加

    展开全文
  • OBS file to use as a recursion plugin
  • 怎么解决python maximum recursion depth exceeded while calling a Python object 程序运行到for Pc in filter_pc: update_phase_and_phys(OP_1.results(Pc=Pc)) 这一步堆栈溢出,按网上的操作修改内存容量...
  • 当函数调用自身时,其称为recursion 强大的iterations替代。( for loop ) 非常适合解决某些类型的问题。 导致优雅和简单的短代码。 函数式编程( Haskell )仅使用递归。 发生在代码和现实世界的许多地方: ...
  • Data_Strucures_Recursion_Lab Raheel Khoja CS 314数据结构Novak UT EID:rrk549 作业2:递归和列表 peanoplus的一个不变属性是peanoplus的Big O(在不更改编译器的情况下)。 无论X和Y分别是多大或小,peanoplus...
  • VLSI参考资料,对高速数字电路设计进行学习,提升数字电路设计技能
  • bind allow-recursion

    2018-11-18 01:11:19
    bind allow-recursion
  • hermes_recursion_源码

    2021-09-29 09:16:33
    Recursion examples. A fractal
  • recursion.js中recursion.js您的解决方案 根据需要查看spec/part1.js和spec/part2.js的测试 保存您的工作并刷新浏览器以检查通过/失败的测试 什么是递归? 递归是指函数调用自身直到不调用为止。 --没有帮助的人 ...
  • 不久前写了一个脚本检测Linux某一个服务是否是启动状态, 因为服务启动时间较长就加了一个递归操作, 结果报错RuntimeError: maximum recursion depth exceeded, 我们来看下如何解决 / 1 / 问题分析    ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 50,321
精华内容 20,128
关键字:

recursion