Recursion and miracle

Time:2021-7-30

<!-- Recursion and miracles -- >
< # header from mengniang encyclopedia, address: https://zh.moegirl.org.cn/File:Bg_ chara_ 201912.png #>

preface

I call some exciting things miracles

There are currently three parts:

  • Part I-From recursion to tail recursion: sort out someCase of non tail recursion transcribed to tail recursion。 At first, it was just because the author of non tail recursion was tired of looking at it.And think about it. If general recursion can be changed into tail recursion, there will be hope for performance optimization! Of course, in fact, not every recursion can do this.
  • Part II-Genius and explosion! ❄: found a solution that can be used to easily create stack overflow (or similar) phenomenon. This idea is to implement modular arithmetic by recursive subtraction. By the way, this part also plays tricks. Oh, by the way,There are examples of the application of this scheme in various languagesIn fact, this part is just my own excitement, or the foreshadowing for greater excitement( So what is the greater excitement?)
  • Part III-Endless y-zygotes: I accidentally found some wonderful things when I added the second part; A simple study opens the door to a new world, which is a very general way to manually implement functional tail recursion, which may be the essence of functional tail recursion; And this part is toUse the scheme in the previous part to introduce this wonderful thing foundWho wants to say((λ (s) (s s)) (λ (s) (s s)))It’s not a miracle, I’ll blow his head, no matter what!!

🐚🐚🦀

I’m talking about functional tail recursion, in which “functional” is just an emphasis. All values are functions, and ordinary values are only special functions that return their own parameters without parameters. In this way, tail recursion will not generate stack pressing, but only return the originally called function at the return as a value first, and then call. Therefore, in effect, tail recursion is like a more intelligent method at this timeGOTOSimilarly, you don’t need to indicate which line to jump back to, you just need to indicate which defined function to re execute with what new parameters – that’s the effect.

🐙🐙🐍

More about tail recursion:

  • This article is well written. At the beginning, it directly talks about the key points:On tail recursion – segmentfault
  • You can also read this article calledLearn You Some Erlang (LYSE)My book. yesWeb version, it introducesTail recursion
    It is an introductionErlangQuick start)Yes, so I also learn a language by the way. It’s easy to master, especially if you don’t know anything. For the expression of ideas, it is extremely concise, rigorous and practical (this is my personal view).

    • You can also bring it alongLYAHI read it, too(Web directory)。 I think this book style is very interesting and worth learning.

The above two books are recommended to read the web version. One advantage of the web page is that it is easy to translate into your mother tongue ̀ ⌄• ́)

Why tail recursion?

Compared with non tail recursion, the first is the well-known reason:

  • Tail recursion saves more memory (that is, less space complexity)

There is a more important reason for me than non tail recursion:

  • If you want the human brain to pass values to the function and follow it again, I don’t need to remember all the previous calls
    (in fact, it’s still a matter of spatial complexity, but this time it’s for your brain.).

This so-calledoptimizationIt is conditional. Generally speaking, it is more difficult to call yourself multiple times at the return (it is like a tree with multiple forks)( Of course, if all of them are returned as values, there is no need to optimize, because there is no stack)

In addition,Multi forkRecursion can be easily optimized by the compiler for concurrent scheduling calculations. Moreover, if the function has no side effects, it can be easily scheduled for distributed computing. Of course, the maximum reachable concurrency is not fixed, and the space complexity will not be reduced.

Relative to the circular structure:

  • If tail recursion is used instead of loop, the brain does not need to remember how many levels have been indented. It just needs to point the arrow to where it should point like a design flow chart (this is my experience of using Erlang). As for whether this is a “circular structure” or notDon’t care at allYes.
  • If tail recursion is used instead of loop, it will lower the threshold for beginners. Even for skilled programmers, it will help to save some useless work that can be saved in theory, so as to liberate the brain. Because, in this way, only these two things need to be remembered: understanding what function definitions and function calls are doing, and related syntax.
    Outside grammar? keyword? In theory, there is no need to use it,Naturally, you don’t need to devote part of your energy to matching specific keywords when reading code。 Because,No matter whether you are used to the job or not, there are ways to avoid this part of labor. After all, the final result is exactly the same. So many parts of labor are actually useless
    In that case, why not avoid it? Because the language you are using requires you to remember these keywords and focus on recognizing them at all times. So even if there is no need to concentrate more, you have to concentrate more. After reading the code, you know whether it is necessary, and then you may think there should be a comment here. However, what if there are notes? There is no machine to restrict the standardization of annotations. So, do you want to expect it to be done?
  • In addition, if tail recursion is used instead of loop, what had to be written in the comment in the previous loop can be written to the function name. What’s the use? You can write everything you want in the notes! Do you think if you ask people to write notes, they will write what you think it is necessary to write here? What’s more, do you think it’s better to describe the business logic in the notes in a language without standard specifications, or to describe the business logic in concise and clear code that can be interpreted by the computer?
  • The main thing is the friendliness of the code itself…

Why use Erlang?

Most of the logic in this article will be implemented using Erlang code. The reasons are as follows:

  • The boundary is clear and the symbols are not redundant.

    In Erlang, line breaks can be removed, which means that the logic of compressing code will be very simple. At the same time, it does not use too many redundant symbols.

  • The symbols are concise and reasonable.

    From Prolog, ; . The design of terminator makes this language easily and clearly express different levels of content; The template structure of function definition has also reached a level of simplicity and expressiveness, and whether any necessary part has been lost( Some languages will lose the terminator and cause their indentation to have semantics – of course, whether this is a bad thing depends on the specific situation)

  • Just defining and calling functions can express almost any logical process.

From recursion to tail recursion

This section will give the recursive example code of factorial, Fibonacci sequence, filtering and lazy value from the set respectively. Each example has two versions of tail recursion and non tail recursion, which are used to compare heuristics.

In the following, the part of [tail] may indicate that this isTail recursionAnd [tree] as long as it is not otherwise specifiedSingle forkTree recursion.

(oh, by the way, there are similar examples in lyse mentioned earlier, and there are more than mine… It is suggested that you also take a look at this, which is definitely worth collecting!:recursive.erl

thinking

It’s actually a skill:

  • Try to include everything in the parameter list!

Perceptually, I don’t think a multitree can be optimized into tail recursion. And sometimes this optimization is not necessarily good. But I’ll think about it and try again.

case

Factorial

This is based on Erlangguide (Translated version)Because the head can’t turn, its original recursive writing method thinks that it can write tail recursion, so it becomes tail recursion for the convenience of thinking.

The following code will be written in Erlang. They can all be compiled, but now you don’t have to care about syntax.
If you know, a function must have such parts:

  • Function name
  • Function parameter list
  • Function body (the return should be clearly defined based on parameters)
  • Start and end marks of each part above

Then you can complete the following grammarImplication。 If you can’t, just look at the guide (or translated version) above, or the book lyse mentioned earlier.

Erlang – Tree

- module (recursion_tree) .
- export ([fac/1]) .

fac (0) -> 1 ;
fac (N) -> N * fac (N - 1) .

use:

c(recursion_tree). recursion_tree:fac(7). % ret: 5040

Erlang – Tail

- module (recursion_tail) .
- export ([fac/1]) .

fac (N) -> fac(N, 1) .

fac (0, FacRes) -> FacRes ;
fac (NumNeed, FacResPart) -> fac(NumNeed - 1, FacResPart * NumNeed) .

use:

c(recursion_tail). recursion_tail:fac(7). % ret: 5040

%%%%

This is perhaps the simplest example of the conversion from ordinary recursion to tail recursion.

Tail recursive code seems to define more information.

The front is [why tail recursion?] The “more brain saving” mentioned in that part can also be tested here to compare which of the two logics takes more brain space.

In the tail recursive code, you don’t have to remember the things before the call after the call. Just forget it. It’s like a new call,Every time is the first time
Even if you really want to run the code completely in your head, it is not impossible, even if the number of substitutions is very large. Even if you want to write every step on paper, you should be able to experience the difference between the two.

Fibonacci sequence

Scheme

This was written in scheme when watching SiCp.

Function onlyChez (cisco/chezscheme)Tested.

#| some def |#
(define (=? a b) (= a b) )
(define (or? a b) (or a b) )

;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;


#| tree invoke fib |#
(define (fib n)
        (if (or? (=? n 0) (=? n 1)) n
            (+ (fib (- n 1)) (fib (- n 2)))
        )
)

;; (fib 4) need:
;; |___(fib 3) need:
;; |   |___(fib 2) need:
;; |   |   |___(fib 1) = 1 <-:
;; |   |   |___(fib 0) = 0 <-:
;; |   |   = 1
;; |   |___(fib 1) = 1 <-:
;; |   = 2
;; |___(fib 2) need:
;;     |___(fib 1) = 1 <-:
;;     |___(fib 0) = 0 <-:
;;     = 1
;; = 3 ->:
;; 
;; tree rec


#| tail invoke fib |#
(define (fib n)
        (define (fib-iter next this step)
                (if (=? 0 step) this 
                    (fib-iter (+ next this) next (- step 1))
                )
        )
        (fib-iter 1 0 n)
)

;; (fib 4) same:
;; (fib-iter 1 0 4) same:
;; (fib-iter 1 1 3) same:
;; (fib-iter 2 1 2) same:
;; (fib-iter 3 2 1) same:
;; (fib-iter 5 3 0) = 3 <-: ->:
;; 
;; no-need-to-tree rec


#| desc desc |#

;; need: means, need to got the func value to get(=) the res.
;; same: means, same as. just while tail invoke can same as!
;; <-: get(=) the res without need: .
;; ->: whole func res.



#|-------------|#
;; end of file ;;

Try writing Erlang again

Erlang – Tree

- module (recursion_tree) .
- export ([fib/1]) .
fib (0) -> 0 ;
fib (1) -> 1 ;
fib (NumOfIndex) -> fib(NumOfIndex - 1) + fib(NumOfIndex - 2) .

use:

c(recursion_tree). 
recursion_tree:fib(7). % ret: 13
recursion_tree:fib(13). % ret: 233

Erlang – Tail

- module (recursion_tail) .
- export ([fib/1]) .
fib (NumOfIndex) -> fib(0, 1, NumOfIndex) .

fib (ThisValue, _, 0) -> ThisValue ; % or better: fib (_, NextValue, 1) -> NextValue ;
fib (ThisValue, NextValue, RestStep) -> 
    fib(NextValue, NextValue + ThisValue, RestStep - 1) .

use:

c(recursion_tail). 
recursion_tail:fib(7). % ret: 13
recursion_tail:fib(13). % ret: 233

%%%%

The sequence here is as follows:[0 1 1 2 3 ...]
Where index is from0Start.

This example of Fibonacci sequence looks like turning binary tree recursion into tail recursion.
You can’t say that completely. At least for me, this is the case:I re used another set of ideas

In other words, this, I think, can only explain,The effect of tail recursion can be achieved by tree recursion, that’s all…

——However, it may become a good example of inspiration: although it is not guaranteed that all tree recursion can be rewritten into tail recursion, it should provide a perhaps good inspiration for the work of “changing some tree recursion, even if it is not single fork, into tail recursion”( At least for the author, it would be better if this could be summarized into a general method.)

Filter list by criteria

Now there’s a series[2,1,3,0, 9,1,7,1, 1,7,1,2, 9,1,8,4]
I hope to take out the first few in this order(2One/3One/99(PCs.)oddNumber.

Scala

If you use Scala(2.12.13)To achieve the best ideographic expression:

List(2,1,3,0, 9,1,7,1, 1,7,1,2, 9,1,8,4).toStream.filter(_%2!=0).take(2).toList

But if you want to verify this, you need tofilterThe parameters of the operator are written as a well-defined function

def oddfilter (x: Int): Boolean = { println("filt: "+x) ; x%2!=0 } ;
List(2,1,3,0, 9,1,7,1, 1,7,1,2, 9,1,8,4).toStream.filter(oddfilter).take(2).toList

You can see on repl:

Welcome to Scala 2.12.13 (OpenJDK 64-Bit Server VM, Java 11.0.10).
Type in expressions for evaluation. Or try :help.

scala> def oddfilter (x: Int): Boolean = { println("filt: "+x) ; x%2!=0 } ;
oddfilter: (x: Int)Boolean

scala> List(2,1,3,0, 9,1,7,1, 1,7,1,2, 9,1,8,4).toStream.filter(oddfilter).take(2).toList
filt: 2
filt: 1
filt: 3
res0: List[Int] = List(1, 3)

scala> List(2,1,3,0, 9,1,7,1, 1,7,1,2, 9,1,8,4).toStream.filter(oddfilter).take(3).toList
filt: 2
filt: 1
filt: 3
filt: 0
filt: 9
res1: List[Int] = List(1, 3, 9)

scala> 

As for the effect of not using stream, you can try it yourself~

Just delete.toStreamRe execution

The above implementation sources:

But the author, you don’t seem to reflectTail recursionCome on!

Yes, so I’ll try writing in Erlang (just what I’ve learned now).

Why use Erlang? Because I think writing simple code is the best language I’ve ever experienced.

Erlang – Tree

- module (recursion_tree) .
- export ([take_odds/2]) .

take_odds (_, 0) -> [] ; % like a `break` in loop code lang, but here is ret a val !!

take_odds ([ElementThis| RestElems], NeedTakeCount) 
    when ElementThis rem 2 =/= 0 
-> 
    [ElementThis| take_odds(RestElems, NeedTakeCount - 1)] ;

take_odds ([ElementThis| RestElems], NeedTakeCount) 
    when ElementThis rem 2 =:= 0 
-> 
    take_odds(RestElems, NeedTakeCount) ;

take_odds ([], _) -> [] .

use:

c(recursion_tree). 
recursion_tree:take_odds([2,1,3,0, 9,1,7,1, 1,7,1,2, 9,1,8,4],77). % ret: [1,3,9,1,7,1,1,7,1,9,1]
recursion_tree:take_odds([2,1,3,0, 9,1,7,1, 1,7,1,2, 9,1,8,4],3). % ret: [1,3,9]

Erlang – Tail

- module (recursion_tail) .
- export ([take_odds/2]) .

take_odds (List, HowManyNeedToTake) -> 
    take_odds(List, [], HowManyNeedToTake) .

take_odds (_, TakenRes, 0) -> TakenRes ;

take_odds ([ElementThis| OldListRest], NewList, NeedToTake) 
    when ElementThis rem 2 =/= 0 
-> 
    take_odds(OldListRest, [ElementThis| NewList], NeedToTake - 1) ;
take_odds ([ElementThis| OldListRest], NewList, NeedToTake) 
    when ElementThis rem 2 =:= 0 
-> 
    take_odds(OldListRest, NewList, NeedToTake) ;

take_odds ([], TakenResFew, _) -> TakenResFew .

use:

c(recursion_tail). 
recursion_tail:take_odds([2,1,3,0, 9,1,7,1, 1,7,1,2, 9,1,8,4],77). % ret: [1,9,1,7,1,1,7,1,9,3,1]
recursion_tail:take_odds([2,1,3,0, 9,1,7,1, 1,7,1,2, 9,1,8,4],3). % ret: [9,3,1]

%%%%

In other words, recursion is more logical.

However, more information can be put in the form of tail recursion, so it is still the latter.

I followed Erlang guide in this tail recursive partMore About ListsPart(This is the translation)YesreverseDefinition, so the results are placed in reverse order…

And it’s not clear how to make the result positive while keeping it natural.
In addition, non tail recursion is written in the same chapterconvert_list_to_cDefinition is only used for other requirements.

Also, remember this little thing:

If you want to take the remainder (modulus) in Erlang,
Do not use a percent sign%Instead of REMrem

........

Percent sign%It’s a comment.
Use rem in Erlangrem

Genius and explosion! ❄

This section will provide a better way to create stack explosion than factorial. The result of factorial is too big!

What was that day? It’s just a lovely stem. Don’t care!

(in fact, it was gradually found that this idea was really just paving the way. To play pop stack,((λ (s) (s s)) (λ (s) (s s)))This structure is obviously the most aesthetic: it is completely abstract and completely not abstract… Abstract and concrete reach dialectical unity here)

way

Is to make one that can be completedmodelAn arithmetic function.

This practice is actually found when I play bash return code114514Will be made into other numbers, dichotomy(exit 2221);echo $?After such a template touched the bottom line of change and invariance, it found that the law had a feeling of recursion. It realized the function that an input number would give me a bash return code. As a result, it was found that I could flexibly input a large number of decimals to detect the allowable tail return depth of Java, so I had such a method. But it’s a long time ago.

Interestingly, I didn’t react until recently. This is the mold method!!
I reacted when I saw this:

iex> <<1>> === <<257>>
true

It comes fromThe official Manual of this language is here

The following scheme code can show my idea.
(if you can’t understand it, you can skip it and continue reading)

(define (remb num rem)
        (if (< num rem) num (remb (- num rem) rem) )
)

Logic is:

  1. There’s a man namedrembFunction, enter two parametersnumandremIf so:
  2. If the former is small, the function returns the value of the former. If not, subtract the latter from the former as a new valuenumThe latter continues to do its workrem Then call the function again with the new set of parameters

Take the mold like this,Of course, it has very low performance。 So you shouldn’t really do any computing tasks with it.
Its value is only that its definition is simple and can be easily defined by using different parametersIndirectly specify the number of times tail recursion occurs

And this test will lead to a great idea:

Yes, on a runtime that does not support tail recursion:

  • Just take advantage of the various functions provided by the runtime itself
  • andMaintain the tail recursive expression on the style of the code

To:

  • Let the runtime that does not support tail recursion support tail recursion

What about this?

The answer iscan

The following is a sample code for running this detection logic in several languages (runtime),And the correspondingDisappoint supportersCode of(at present, only the techniques supporting tail recursion on bash are given).

Ice snow smart genius calculator ❄❄❄

What is this? This is not actually the code example mentioned above.

You can skip(although it’s cruel to say so, this part can really be skipped)But if you want to relax, you can finish this part.

Next, pleasecirno The teacher spoke.

Hello and welcome to chiluno’s arithmetic class.

The above implementation must have been seen by everyone: there is no considerationnumIs a negative number.

Because there’s no need

Of course I know. Do you think I’m a fool! Hum! ❄
In short, this is not perfect! ❄。

Qiluno is here to bring you an enhanced version, which can be calledGenius calculatorTools!

OK, now let’s look at the great works of chiluno:

⑨❄❄❄

- module (playfuns) . 
- export ([remb/2, bakacal/1]) .

%% remb/2 define:
remb (Num, RemNum) 
    when 0 =< Num 
    andalso Num < RemNum 
-> 
    Num ;
remb (NumReming, RemNum) 
    when RemNum =< NumReming 
-> 
    remb(NumReming - RemNum, RemNum) ;
remb (NumReming, RemNum) 
    when NumReming < 0
-> 
    remb(NumReming + RemNum, RemNum) .

%% bakacal/1 define:
bakacal (N) -> remb(N - 1, 9) + 1 .
% bakacal (N + 1) -> remb(N, 9) + 1 . % error, illegal pattern

abovebakacalnamelyGenius calculatorYes. hinder/1It means that only one parameter is enough.

Is it very simple? (• ̀ ⌄• ́)

Next, if you want to use genius arithmetic, just do this on eshell:

1>c(playfuns).
{ok,playfuns}
2> playfuns:bakacal(10).
1
3> playfuns:bakacal(11).
2
4> playfuns:bakacal(9). 
9
5> playfuns:bakacal(8).
8
6> playfuns:bakacal(0).
9
7> playfuns:bakacal(-1). 
8
8> playfuns:bakacal(-2).
7
9> 

Look! With so many examples, she can calculate the appropriate results!!

And the internal logic is beautifully unified!

Be good at summarizing the laws of unity, which is called wisdom!!!! ❄❄❄❄

This poor calculator is obviously a genius, but it can’t be used anywhere. It has to be ridiculed “ha ha, can you only count to nine”. However, who knows the exquisite and beautiful wisdom behind others?
as one can imagine! How sad this calculator is when you are ridiculed by a fool and can only count to ⑨!!!! Obviously, if you want it to count only a few, it can only count a few….

In short, it is the strongest!

Examples of languages for checking tail recursion

Get down to business and check tail recursion.

Again,Using it as a mold has low performance

Here is an example.

Scheme – Chez - 9.5.4

(define (rb num rem)
        (if (< num rem) 
            num 
            (rb (- num rem) rem) 
        )
)
;; (rb 3333333333 2) ;; ret: 1

There was a mistake in the middle at the beginning. It should be(< num rem)I wrote it(num < rem) 。。。

Chez is really fast… The speed difference can be clearly compared with racket.
The main time-consuming here is estimated to be the execution of addition there.

Python :: 3.9.2, GCC 10.2.1

def rb (num, rem):
    if (num < rem): return num
    else: return rb(num - rem, rem)

### rb(3,2) # ret: 1
### rb(3333333,2) # err: RecursionError: maximum recursion depth exceeded in comparison

When Python defines a function on repl, it should type a few more line breaks at the end. after allVernier caliper languageThe biggest problem is actually the wooden end mark…

HY test

There is a library on Python calledhy, and it is said to be a lisp.

Generally speaking, hy is defined in this way(reference resources):

(defn remb [num rem]
  (if (< num rem) num (remb (- num rem) rem) )
)

Try to see:

=> (remb 4 2)
0
=> (remb 5 2)
1
=> (remb 3333333 2)
Traceback (most recent call last):
  File "stdin-c205eccd9236cc55bd83a0f3cdcf9af3deb02b56", line 1, in <module>
    (remb 3333333 2)
  File "stdin-97ee98165e108d7d2747d4e423030117a0891754", line 2, in remb
    (if (< num rem) num (remb (- num rem) rem) )
  File "stdin-97ee98165e108d7d2747d4e423030117a0891754", line 2, in remb
    (if (< num rem) num (remb (- num rem) rem) )
  File "stdin-97ee98165e108d7d2747d4e423030117a0891754", line 2, in remb
    (if (< num rem) num (remb (- num rem) rem) )
  [Previous line repeated 986 more times]
RecursionError: maximum recursion depth exceeded in comparison

Ah?! Doesn’t it mean tail recursion support? I remember wrong?

I found it, I found itthis: the general meaning is that they are implemented with macros and use these things like keywords. There is an example of factorial below, which I won’t quote. I imitated while making mistakes and wrote an implementation of self-made module:

(defn rbloop [num_in rem_in]
  (loop [[num num_in] [rem rem_in]]
    (if (< num rem) num (recur (- num rem) rem) )
  )
)
;; (rbloop 33333 2) ;; ret: 1

thisloopIn the following brackets, there are two brackets. In each bracket, the one on the left isloopThe variable name that will be used in. On the right is the value passed for its first call.
That is, write in hyTail recursionIf so, we mustThe tail recursive structure is defined inside the function

。。。 I think this is actually a sugar cycle…

If the speed was fast, I tried. The long number really didn’t come out for a long time, so I cancelled it.

Py test

I don’t know python. At present, there are two methods:

For both:

  • The previous one is done by using the language characteristics of python, a thing called decorator;
  • The latter one is solved by a way called y combiner. This method should be applicable to all languages that can transfer functions as values. According to the description in the second link, it is roughly: pass out the function as a value,So as to complete the call to the outer function without starting the inner function callSo as to manually avoid stack pressing when calling a function within a function, and then call the outgoing function, and so on.

In addition, this Python article is really everywhere… The second link here was found when I found the following PowerShell tail recursion scheme….

Scala :: 2.12.13, OpenJDK Java 11.0.10

def rb (num: Long, rem: Long)
: Long = 
{
    if (num < rem) num 
    else rb(num - rem, rem) 
} ;

// rb(3,2) // ret: res1: Long = 1
// rb(3333333333L,2) // res2: Long = 1

Although Scala is actually infinite tail recursion and does not have stack overflow, it is still that the calling function will press the stack (tree recursion will overflow the stack) (this version).

In this regard, it may not be consistent with the standard of general (even impure) functional language.
The general functional standard should be that there is no (or always equivalent to no) stack pressing, so that even tree recursion will not have the so-called stack overflow( It should only be about whether the allocated memory will be exhausted

But Scala’s long numbers are as fast as Chez’s.

Moreover, Scala would be better with a larger number.

I use it in scala replrb(9333333333L,2), and Chez interpreter(rb 9333333333 2), to make a comparison.

I still use this Scala in feodra’s WSL (1)dnfDirectly. It uses openjdk, not graalvm. It is said that the latter is faster than the former.

The speed of the JVM is really pretty good. Jpg (2021-06-12)

Java :: Jshell - 11.0.11

Long rb (Long num, Long rem)
{
    if (num < rem) return num ;
    else return rb(num - rem, rem) ;
}

// rb(3L,2L) // ret: $1 ==> 1
// rb(33333L,2L) // ret: $2 ==> 1
// rb(333333L,2L) // err: java.lang.StackOverflowError

This is in the JVM shell.

If the function defined directly here hasstaticModifier, you will get such a warning:

  • Modifier 'static' is not allowed in top-level declarations, ignored

However, the function can still be created successfully.

Of course, even Java 11, even graalvm, and even new things like jshell will overflow the stack.

It should be designed like this. There may be a switch somewhere. I didn’t turn it on.

However, Java is said to have a set of ways to make it similar to tail recursion. If you are interested, you can see for yourself:

Erlang :: Erlang/OTP 24, Eshell V12.0, Windows-Version

rb (Num, Rem) when Num < Rem -> 
    Num ;
rb (Num, Rem) -> 
    rb (Num - Rem, Rem) .

%% c(xxx). xxx:rb(3,2). % ret: 1
%% c(xxx). xxx:rb(3333333333,2). % ret: 1

Please supplement the module head by yourself.

A lot less code than the gifted arithmetic instrument above?
Because there is no negative number here, and the variable name here is not too long, that’s all.

Long numbers here are a little slower than Chez and scala.

IfjustOn the Erlang shell, anonymous functions are written. One more parameter is required.

Rb = fun (N, R) -> 
    RbIter = fun 
        (N, R, _) when N < R -> N ; 
        (N, R, F) -> F(N - R, R, F) end , 
    RbIter(N, R, RbIter) end . 

%% Rb(3,2). % ret: 1
%% Rb(3333333333,2). % ret: 1

At the beginning, the syntax of anonymous function was wrong. We measured it on eshell. According to the prompt information, it was found that the code handwritten by mobile phone was even more pseudo code than pseudo code, and there was a lot of confusion here and there….

In terms of speed, the long number has been calculated for a long time by using anonymous functions in eshell…

Bash :: 5.0.17, x86_64-redhat-linux-gnu

Bash Function

rb ()
{
    num="$1" rem="$2" &&
    ((num < rem)) && 
        { echo "$num" ; } ||
        { rb "$((num - rem))" "$rem" ; } ;
} ;
## rb 3 2 # out: 1
## rb 33333 2 # exit... no out no err, just exit after few sec. ...

In order to avoid many problems, it has become my principle to write bash explicitly as much as possible.

Here, the standard output is regarded as a means to penetrate the data, rather than using return.

。。 If it’s bash crisp or crisp, it’s over in a few. There’s nothing. I just wait for a second or two and back out…

Bash Script File

I’ll use it directly hereexec

#! /bin/bash

num="$1" rem="$2" &&

((num < rem)) && 
{
    echo "$num" ;
} ||
{
    exec /bin/bash "$0" "$((num - rem))" "$rem" ;
} ;

## bash trc.sh 3 2 # out: 1
## bash trc.sh 33333 2 # out: 1

Here you can check by yourself. YesexecAnd NoexecThe difference.It is suggested to use less important machines for inspection, otherwise don’t blame me for anything

The formatting style in the script is different from the previous one. There’s no special reason. It’s good to actually use any one. I’m just for a change( To tell you the truth, the following branch in the script is still written in this way…)

Slow or slow, of course. It is estimated that this is the main reason((xx+xx))Slow. Although I can count at last, it’s really slow.

cdThe miracle of script file!

In fact, if Bash is only testedexecFor the difference, you can use the following:

#! /bin/bash

dir0="${1:-0}" &&

cd $dir0 &&
{ pwd ; } ||
{
    exec /bin/bash "$0" "$((dir0 + 1))" ;
} ;

## bash cdtr.sh # err: cdtr.sh: line 5: cd: (some num will be here): No such file or directory

If it is executed, it will always report errors and cannot find several folders. You can use this to judge how many times it has been recursive:

  • Put thatexecIf it is deleted, the script will appear before it can go farSome problems
  • yesexecIf so, you will see that the number has been rising endlessly, unless you create a folder called larger number before it reaches that many numbers, then it will go to that folder when it tries and makes mistakespwd

Using such a recursive script on bash, you can intuitively do some trial and error work: execute a command, retry if it is wrong, and do not retry if it is right( Or vice versa?)

cdFunction of miracle!

It’s actually usedexecOne feature of:It discards the commands above and below its text, and the commands after it will not be executed anyway.(of course, this is limited here.)exec(usage of)

Based on this,There are other messy ideas, I finally found:A method to achieve tail recursion without generating files !!

Praise!!!!

Simple example:

tailcd ()
{
    count=${1:-0} &&
    cd $count ;
    exec bash -c "$(declare -f tailcd)"' ; tailcd '"$((count+1))" ;
} ;
## run:
bash -c "$(declare -f tailcd)"' ; tailcd'

Or it’s better to do something like this:

tailcd ()
{
    count=${1:-0} &&
    cd $count ;
    exec bash -c 'tailcd '"$((count+1))" ;
} &&
export -f tailcd ;
## you can run,
`# use this:`  bash -c tailcd
`# or this:`  (tailcd)

The above code supports simple compression logic: you can just replace newlines with spaces and delete consecutive spaces into one. After that, the code can still be executed.

If it is implemented, it is recommended not to implement it directlytailcdCall the function like this.
The simplest way to write is to have that pair of parentheses:(tailcd)

Direct executiontailcdIt doesn’t mean no, you can try, but you may feel uncomfortable after it stops running.

What’s wrong? Just try it yourself. Maybe you can get more experience!:D
(it’s actually a process tree thing. Whether bash – C tailcd or (tailcd) is to open child processes. Why open a child process? This requires you to study it yourselfexec(see you later!)

In this way, those who failed in the front can not write files!!

Bash Function

Why am I so obsessed with BashfunctionInstead of a script file?

A very important reason is that if I define a function, I can safely determine these two things:

  • It won’t be changed. It’s what I defined, and I can easily overwrite it with the new definition without interacting with the disk.
  • Its life cycle is clear and its impact scope is limited. I don’t want a part of the software to suddenly appear in one place. If I don’t care, it won’t be cleaned up.
rbex ()
{
    num="$1" rem="$2" &&
    ((num < rem)) && 
        { echo "$num" ; } ||
        { exec bash -c "$(declare -f rbex)""$(echo ';' rbex  $((num - rem))  $rem)" ; } ;
} ;
## bash -c "$(declare -f rbex)"'; rbex 3 2' # out: 1

The above is a simple demonstration of ideas. The following will save more resources:

rbex ()
{
    num="$1" rem="$2" &&
    ((num < rem)) && 
        { echo "$num" ; } ||
        { exec bash -c "$(echo  rbex  $((num - rem))  $rem)" ; } ;
} &&
export -f rbex ;
## bash -c 'rbex 3 2' # out: 1
## echo rbex 3 2 | bash # out: 1
## echo rbex 33333 2 | bash # will out 1 , but slow ...
## (rbex 3 2) # out: 1
## (rbex 128 2) # out: 0

At tail recursionbash -c "$(echo rbex $((num - rem)) $rem)", equivalent to:
bash -c rbex' '$((num - rem))' '$remThe effect of this writing.

The space of the former is treated as a separator and automatically becomes a. This allows me to use the former form, regardless of the number of spacesbash -cThe content is always consistent.

Here’s a little truth:

  • Through inbash -cCall the function in the function so that the function can also be calledexecEffect.
  • I first thought that I could print the “$(declare – F rbex)” of the function as a whole and give the external function definition tobash -cLater orders.
  • However, I thought that since it wasSubprocessThen I just needGlobalize my defined functionsIsn’t that all? useexport -f rbexIf so, it will not fail in the child processes of the current process.

thus:

  • The defined logic can always exist: it can beexecPremise of function;
  • This defined life cycle that has always existed is reasonable,Destroy when it should be destroyed and not when it should not be destroyed

These two points can be met.

In addition, the parameters here areWithout spacesNumbers, so you don’t have to write them"$((num - rem))"So or"'""$((num - rem))""'"In this way.
However, as a whole, we should"Li Zai"Where should I be'Li Zai'You can be more rigorous. For example,In the above example, if a parameter with spaces is passed in,Then I don’t know what will happen
If you are sure that spaces may be included, you must use quotation marks to express a whole as a whole. Not much discussion here, pleaseAdd the necessary single and double quotation marks and pass the test

The call example gives more writing methods. You can think about why you can write this and choose the writing method suitable for you.
(no one should be so idle and flustered, just develop things with bash…)

It is more recommended to write in parentheses. Because of simplicity.
And, in parentheses,Do you think it looks very familiar!! Remember scheme!
(actually, it shouldn’t be a big deal, because it’s just like. At present, I can’t guarantee that I can implement a lisp on bash with this routine…. But who dares to be interested can try)

Powershell :: 7.1.3

function Rem-B
([int]$Num, [int]$Rem)
{
    if ($Num -lt $Rem) { return $Num }
    else { return Rem-B -Num ($Num - $Rem) -Rem $Rem }
} ;
## Rem-B 3 2 # ret: 1
## Rem-B 4 2 # ret: 0
## Rem-B 33333 2 # err: InvalidOperation: The script failed due to call depth overflow.

Visible plusreturnIt’s no use interrupting.

However, since it has pipes, let’s try:

function Rem-B
{
    process 
    {
        $Num = $_[0] ; $Rem = $_[1] ; 
        if ($Num -lt $Rem) { ,($Num,$Rem) }
        else { ,(($Num - $Rem),$Rem) }
    }
} ;
## ,(3,2) | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B # out: 1\n2
## ,(4,2) | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B # out: 0\n2
## ,(33333,2) | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B | Rem-B # out: 33123\n2

。。。。

Why do I do this?

If the pipeline is recursive, light is,(3,2)You can’t get out.

This part needs to be studied again. In addition, inSome articles on y zygotesA similar structure can be seen in.

Endless y-zygotes

This section tries to introduce one thing that has just been figured out recently:Y-zygoteY combinator )。

Extended reading on y combiner:

And two translations of a good book (extended reading of extended reading):

previously on

Introduced aboveRemb / rbIn that chapter, the supplementary content was found by mistakeY-zygoteSuch a thing.

After a careful look, I think this is the essence of tail recursion without stack design.

However, it seems that this combiner is only used to find ways for anonymous functions to call their own…

I used Erlang’s anonymous function to implement a function that can be used on eshellRemB, it is much faster than the anonymous function writing method in the previous chapter.

Of course, the purpose of using Erlang here is that its expression is easy to understand the calculation idea.

Test the left foot and step on the right foot to heaven

The following part should beY-zygoteThe part of relevant knowledge that is easy to understand.

But it hasn’t appeared yetY-zygoteItself, but just use a stupid way to let the nameless function (the nameless function is a value) call itself.

Erlang :: Erlang/OTP 24, Eshell V12.0, Windows-Version

Don’t follow firstLambda calculusThe anonymous functions defined here will bind the variable name first.

(this is not a function name. Function is nameless, just value. Here, only the name is bound to the value. But the call is written in the same way as a general function)

RembNourisherInsideRemb =
    fun (FuncNourisherInsideFunc) ->
        fun (Num, Rem) when Num < Rem -> Num ;
            (Num, Rem) when Num >= Rem -> 
                FuncGotWithFuncNourisher = FuncNourisherInsideFunc(FuncNourisherInsideFunc) , 
                FuncGotWithFuncNourisher(Num - Rem, Rem) 
        end 
    end , RemB = RembNourisherInsideRemb(RembNourisherInsideRemb) .

RemB(3,2). % ret: 1
RemB(333333,2). % ret: 1
RemB(333333,669). % ret: 171
333333 rem 669. % ret: 171

Erlang does not support Corey writing, that isFuncE(FuncE)(X)This way of writing, we must firstFunc = FuncE(FuncE)Then againFunc(X)Just do it.

But it doesn’t matter. In fact, it’s just the lack of a pair of parentheses. The method of writing without variable name will be given later.

Scheme :: Chez - 9.5.4

Let’s startLet lambda   calculus release itselfThe writing method of not binding variable names to anonymous functions:

Not familiar? You will be familiar with the following wording:

(+ 1 1) ;; ret: 2
(+ 1 1 1) ;; ret: 3
([lambda (x) (+ 1 1 x)] 3) ;; ret: 5
([lambda (x y) (+ x y)] 3 4) ;; ret: 7
([lambda (a x y) (+ x y)] + 3 4) ;; ret: 7

After the semicolon is the comment. I wrote the execution result of the formula in front of the semicolon in the comment. Of course, I’d better try it by myself.
From simple to complex, write it yourself, perform it, and feel the information for triggering the error, because this is the space for progress.

Scheme is a lisp dialect. It has brackets around it, clear boundaries, and spaces in the middle, so it is very flexible to use.
You can wrap lines at any place where there are spaces, and the blank characters such as line breaking of any number of consecutive spaces are semantically identical.

(
    [lambda (func-nourisher-inside-func)
        [lambda (num rem)
            (if (< num rem) num 
                ((func-nourisher-inside-func func-nourisher-inside-func) (- num rem) rem)
            )
        ]
    ]
    
    [lambda (func-nourisher-inside-func)
        [lambda (num rem)
            (if (< num rem) num 
                ((func-nourisher-inside-func func-nourisher-inside-func) (- num rem) rem)
            )
        ]
    ]
)

okay.

The whole lump above has two small lumps up and down. Look closely, they are exactly the same.
Enclose a bracket outside, which is what we did beforeFuncGotWithFuncNourisher = FuncNourisherInsideFunc(FuncNourisherInsideFunc)Work to the right of the equal sign.

That is, the lower lump is a parameter, which is substituted into the upper lump. The upper lump is called in a pair of parentheses, and the parameter is the lower lump.

By the way, you can also write:

(
    [lambda (be-nourishering)
        (be-nourishering be-nourishering)
    ]
    
    [lambda (func-nourisher-inside-func)
        [lambda (num rem)
            (if (< num rem) num 
                ((func-nourisher-inside-func func-nourisher-inside-func) (- num rem) rem)
            )
        ]
    ]
)

If this method is used for Erlang, there should be no need to use variable names. After all, althoughfun (X,Y) -> fun (Z) -> X+Y-Z end end(1,2)(4).Illegal in Erlang (it can be written in Python or Scala), but(fun (X,Y) -> fun (Z) -> X+Y-Z end end(1,2))(4).Is a grammatically correct way to get the expected results, likefun (X,Y) -> X+Y end(1,2).There will be no problem writing like this.

If it is used, it is used in this way – the definition of anonymous function itself is used as the function name:

((
    [lambda (func-nourisher-inside-func)
        [lambda (num rem)
            (if (< num rem) num 
                ((func-nourisher-inside-func func-nourisher-inside-func) (- num rem) rem)
            )
        ]
    ]
    
    [lambda (func-nourisher-inside-func)
        [lambda (num rem)
            (if (< num rem) num 
                ((func-nourisher-inside-func func-nourisher-inside-func) (- num rem) rem)
            )
        ]
    ]
) 3 2 )

Or:

((
    [lambda (be-nourishering)
        (be-nourishering be-nourishering)
    ]
    
    [lambda (func-nourisher-inside-func)
        [lambda (num rem)
            (if (< num rem) num 
                ((func-nourisher-inside-func func-nourisher-inside-func) (- num rem) rem)
            )
        ]
    ]
) 3 2 )

The return result is1

The above formatting principle is that a pair of parentheses must be aligned up and down or left and right. The part that will not be called (that is, the part that only defines the content) uses square brackets, and the part that will use parentheses (any parentheses used in syntax are line). Do not wrap the first few items in brackets as appropriate, and distinguish the head and body of the definition.
Some people say that people should not count parentheses. I agree. Therefore, parentheses should not be at the end. Although that looks simple, it also loses the function of parentheses. The s expression of scheme makes the language flexible and rigorous enough. This is its advantage, so we should give full play to it.

Of course, some characters added to the call example are not formatted in this way. Mainly because I’m lazy, and the new part is also very simple, so I don’t think it’s necessary.

There are still deficiencies

The above practices, inThis slide of Wang YinSnapshots that are not necessarily accessible)It’s calledpoor man's Y(of course, this may be what Wang’s gifted teacher called it.).

If you putFunction definitionRegarded asDetermined value, andfunction callRegarded asUncertain valueAnd assumeUncertain values will inevitably lead to multi stackThen, the above is obviously not enough.

Therefore, this logic can not be regarded as completing the original purpose: after all, the purpose here is not just to make anonymous functions tail recursion.

move on

Let’s start with a more general y-zygote.

fromPaper wood CityThis articleYou can see a good derivation.

Search around, includingWang Yinna pptIn general, they have done these things:

  • Make onepoor man's Y
  • Abstract what appears to be repeated (withlambda )
  • Give WayObjective function generator inside objective function (func-nourisher-inside-func)Take effect by passing in a specific logical block (taking effect means that a call occurs)
  • holdfunc-nourisher-inside-funcThe part itself is made into a separate piece, and then the recursive part is passed into it before it takes effect (that is, the call occurs)

The identifier of the following code comes from this comic work:

Recursion and miracle

Page from mengniang Encyclopedia:https://zh.moegirl.org.cn/%E5%8D%83%E5%A4%9C(%E5%A7%90%E5%A7%90)

🦎

The whole one belowrembAn example implemented in this way( This time, use scheme and then Erlang. Because anonymous functions seem to be the most intuitive s expression.

Scheme :: Chez - 9.5.4

This part is still implemented with Chez, even if it is not as convenient as other scheme implementations, such as pattern matching. Because I think it is a very concise and talented scheme implementation, I decided to still use Chez. If I can’t get through, just read the official documents (anyway, I have translated and offline one).

The following code style is just that I think it is convenient, and even human visual ability can easily assist in understanding the code. Because I think this is a clear level, not an unclear boundary.
I don’t guarantee that it will always benefit anyone’s thinking habits. So, if you’re not used to it, you’d better format it yourself to make you feel comfortable.

(
    (
        [lambda (koyuu)
            (
                [lambda (umareru)
                    (umareru umareru)
                ]
                
                [lambda (chiyo)
                    (
                        koyuu
                        
                        [lambda (pakotte)
                            ((chiyo chiyo) pakotte)
                        ]
                    )
                ]
            )
        ]
        
        [lambda (chiyo-oma)
            [lambda (pako-bako)
                (record-case pako-bako
                    [(remb) (num rem)
                        (if (< num rem) num
                            (chiyo-oma [list 'remb (- num rem) rem])
                        )
                    ]
                )
            ]
        ]
    )
    
    '[remb 3 2]
)

Only one parameter needs to be passed when calling. I used itrecord-caseTherefore, the first item of the passed in list must also be an atomic or symbolic variable, which I naturally use hererembThis name.

Probably for Chezpattern matchingIt seems that it has to be used like this.
——Not necessarily. I just hurried through Chez’s documentation in an hour or two and finished the code( Why are you in such a hurry? Because I’m so excited after finalizing the variable name above…)

In addition, if you are familiar with the cartoon mentioned above, especially itsRibenIf so, you will find that the naming of the above code will probably help you understand the relationship between anonymous functions….
——If you don’t understand what I’m talking about, it shows that you are a simple and healthy good child, then ignore this paragraph. Of course, you can also be a great child full of exploration spirit. Before that, please be happy to face the abyss.

(joking, joking…)

🐍

The one above was originallyTail recursionIn the code of, at the key recursion location, in essence, only the determined value obtained without recursion is passed in. If you can pass in two parameters, you need to be preceded by((chiyo chiyo) pako1mata pako2mata), that’s justchiyo-omaTwo definite values are inserted, that’s all.

It can be seen that the realization of real tail recursion still depends on sister Qianye
Sister Qianye is the best

Attach last racket(v7.9 [cs])Implementation and use on:

(
    (
        [lambda (koyuu)
            (
                [lambda (umareru)
                    (umareru umareru)
                ]
                
                [lambda (chiyo)
                    (
                        koyuu
                        
                        [lambda (pakotte)
                            ((chiyo chiyo) pakotte)
                        ]
                    )
                ]
            )
        ]
        
        [lambda (chiyo-oma)
            [lambda (pako-bako)
                (match pako-bako
                    [(list num rem)
                        (if (< num rem) num
                            (chiyo-oma [list (- num rem) rem])
                        )
                    ]
                )
            ]
        ]
    )
    
    '[333333 669]
)
; ret: 171

Pattern matching is actually to facilitate the parameter listpako-bakoJust take it apart.

Let’s briefly talk about what we are doing before and after:

Recursion and miracle
Recursion and miracle
Recursion and miracle
Recursion and miracle
Recursion and miracle
Recursion and miracle

Try the following. If Chinese characters are used as identifiers, it can also be executed:

(
    (
        [lambda (including logic)
            (
                [lambda (to be generated)
                    (to be generated)
                ]
                
                [lambda (logic generator)
                    (
                        Logical person
                        
                        [lambda (action parameter during recursion)
                            (action parameters during recursion)
                        ]
                    )
                ]
            )
        ]
        
        [lambda (line logic)
            [lambda (action parameter)
                (match action parameters)
                    [(list num rem)
                        (if (< num rem) num
                            (line logicians [list (- num REM) REM])
                        )
                    ]
                )
            ]
        ]
    )
    
    '[333333 669]
)
; ret: 171

This seems more clear.

An obvious fact is that there are still names in lambda calculus; It’s just like this:My name is with you, your name is with me; What I need is in you, what you need is in me

It’s like love

Erlang :: Erlang/OTP 24, Eshell V12.0, Windows-Version

With the scheme version above, you can write the Erlang version.

Erlang is used here, and the variable name is not assigned.

The square brackets above are definitions (this is my own specification), and the parentheses are calls. In Erlang, it is: move the previous parentheses below, indent the function name or the defined anonymous function to the left, and]change intoendhold[lambdachange intofunNote that there is a gap between the definition header and the definition body->(in short, everyfunThere must be one in the back->)Just remove the extra parentheses and simply fix the inappropriate parts of the grammar.

(
    fun (KoYuu) ->
        fun (UmaReru) ->
            UmaReru(UmaReru)
        end
        (
            fun (ChiYo) ->
                KoYuu
                (    
                    fun (PakoTTe) ->
                        (ChiYo(ChiYo))(PakoTTe)
                    end
                )
            end
        )
    end
    (
        fun (ChiYo_OMa) ->
            fun ({Num, Rem}) when Num < Rem -> Num ;
                ({Num, Rem}) when Num >= Rem ->
                    ChiYo_OMa({Num - Rem, Rem})
            end
        end
    )
)
({333333, 669}) . % ret: 171

There should be no extra parentheses above.

The way curry wrote the call isfun(a,b)(c,d)Different people have different opinions on whether this writing method is good or not. I can’t say it clearly anyway. It’s nothing more than support.

But I think it’s mandatory(fun(a,b))(c,d)In this way, it is not bad to not allow it to be written like that. Because that’s really emphasizedcurrying The essence of: first pass out a function, and then call it.

Python :: 3.9.5, GCC 10.3.1

It’s also easy to change from scheme to python.

I use it in PythonChinese variable nameYes. One was foundGood idea

At first, I hoped that he would contribute more to the maintenance in the future, but I felt that his motivation was not strong at that time. It took a week or so to make the prototype. What was unexpected and surprising was that the submitted this “painting tigers like cats” PR on September 28, and continued to improve after that. After October, I basically didn’t invest any energy except merging   PR  .
It can be seen that Chinese naming plays a role in encouraging novices to participate in open source projects.
After the basic architecture of the open source project is built, if the project itself is named in Chinese, users (often non programmers) should be more motivated to learn the code. It’s not that English naming will definitely prevent participation in the project, but it will deter a large number of people.

I think it makes sense.

This is what I changed from the beginning:

(
    (
        (
            Lambda contains logic:
            (
                (
                    Lambda to be generated by: 
                    (
                        To be generated (to be generated)
                    )
                )
                
                (
                    Lambda generated by: 
                    (
                        (including logic)
                        (
                            Lambda * action parameters during recursion: 
                            (
                                Generate logics (generate logics) (* action parameters during recursion)
                            )
                        )
                    )
                )
            )
        )
        
        (
            Lambda line logic: 
            (
                lambda num,rem :
                (
                    num if (num < rem) 
                    Else line logic (num - rem, REM)
                )
            )
        )
    )
    
    (333333, 669)
)
## ret: 171

Here, in order to ensure that anonymous functions can be called without giving a name, anonymous functions should be enclosed in parentheses.
However, this is the operation! This gives Python a clear flag for the end of the definition!!

See! Python is finally not thoroughVernier caliper languagela
Who says the dog’s head is thoroughVernier caliper language, you can use a string of pure ones like thislambdaBlow up his Python!!

🦉🦉🦉🦞🦞🦞🦔🦔🦔

And, so, although Python has not yetmatch-case, but it seems to worklambdaIt’s fake… At least it has the ability to unpack. But it’s just dismantling the parameter list… Not unpacking.

actuallymatch-caseThere are in Python, but not yet. Although it was only allowed at the beginning of this year.
find outthisandthisSay, to3.10Yes. And now(2021-07-03)The latest official version of the official website or3.9.6。。。( I also tried to update my WSLpythonI’ll read this part carefully.. A subtle difference.)

Oh, by the way, if the above code is in Python, many spaces can be removed. Accordingly, however, the parentheses of the call should not be too far from the function name (not to the next line). Then there can be an equivalent expression:

(
    Lambda contains logic:
    (
        (
            Lambda to be generated by: 
            (
                To be generated (to be generated)
            )
        )   (
            Lambda generated by: 
            (
                Logical person(
                    Lambda * action parameters during recursion: 
                    (
                        Generate logics (generate logics) (* action parameters during recursion)
                    )    )
            )   )
    )
)   (
    Lambda line logic: 
    (
        lambda num,rem :
        (
            num if (num < rem) 
            Else line logic (num - rem, REM)
        )
    )   )   (333333, 669) # ret: 171

You can continue to reduce parentheses, but I think it’s not necessary. Reduce the parentheses again, I can’t see the level anyway.

In addition, from this simplification, it can also be seen thatscalafun(args1)(args2)...What is the essence of Coriolis call writing.

But actually weThe original purpose has not been achieved, although it has been successfully used in Python.

We originally wanted Python to be tail recursive, but now if we put669change into1If you do, you’ll be hooded by the error message. I’ve just been hooded.

<!-- As for the reason, find it tomorrow (but this may also be recursive) - >

Learn fromThe link mentioned earlierIdeas in:

>>> r = (
... lambda contains logic:
...     (
...         (
... lambda to be generated by:
...             (
... to be generated (to be generated)
...             )
...         )   (
... lambda generated by:
...             (
... with logic(
... lambda * action parameters during recursion:
...                     (
... lambda: generate logics (generate logics) (* action parameters during recursion) 
... #^# this part will be passed to the external variable R 
... #|# so the following call (this is just a call) is also an empty bracket R () 
...                     )    )
...             )   )
...     )
... )   (
... lambda line logic:
...     (
...         lambda num,rem :
...         (
...             num if (num < rem)
... else row logicians (num - rem, REM)
...         )
...     )   )   (33333, 2)
>>> r
<function <lambda>.<locals>.<lambda>.<locals>.<lambda>.<locals>.<lambda> at 0x7f5215545ee0>
>>> r = r()
>>> r
<function <lambda>.<locals>.<lambda>.<locals>.<lambda>.<locals>.<lambda> at 0x7f5215545e50>
>>>While callable (R): r = R () #< -# this part is equivalent to manually walking through each layer of recursion with while
...
>>> r
1
>>>

So it won’t explode…

So… In the end, Python’s own loop is used in Python….

Solved the case! Python is a proper imperative language.

But python, an imperative style language, is fun: you can toss around like this, but it’s still it…

Unfortunately, the action with sister Qianye can’t be carried out automatically…. It’s essentially a machine instruction loop… Sister Qianye is gone… Damn….

Echo

Now, the so-calledY-zygoteIn fact, it is to find a way to transfer a generator that can generate its own to itself, and the generator generated by this generator also brings its own generator…. Well, in short, we should find a way to keep a piece of information existing during the execution through delivery.

Now it seems that beforeGenius and explosion! ❄Used as mentioned in sectionexec bash -cand"$(declare - F function name)"In fact, it is similar, but bash’s practice is actually more stupid,Splice a set of defined code before going to an undefined placeand that.

Here is an example that can execute a command and try again after an error. The style of printing information draws on Erlang’s return.

redoer () 
{
    cnt=${2:-0}  cmd="${1:-echo xargs-i is {}}" &&
    bash -c "$cmd" &&
    {
        echo \{ok, {}, $cnt\} ;
    } ||
    {
        echo \{err, {}, $cnt\} ;
        exec bash -c "$(declare -f redoer)""$(echo ' ; 
        ' redoer "'$cmd'" $((cnt+1)) )" ;
    } ;
} ;

That’s the definition. Using examples may be troublesome, mainly because I’m not sure they will be filled in in batches{}What is the location. But it doesn’t matter. I believe you can understand it.

This is an example that should work. You will see from the error message that there is an error in the download of a branch, and then try again later.

##This creates a shallow clone
git clone --depth 1 -- https://ghproxy.com/https://github.com/cisco/ChezScheme cisco/ChezScheme
##This is how to create several in batch
echo 'cisco/ChezScheme
rustdesk/rustdesk
tsasioglu/Total-Uninstaller
elixir-lang/ex_doc
triska/the-power-of-prolog
hashicorp/vagrant' | xargs -P0 -i -- bash -c "$(declare -f redoer) ; redoer 'git clone -q --depth 1 -- https://ghproxy.com/https://github.com/{} {}' "

The error message I wrote here is:Is it successfulContent filled in {}retry countThese three.

The above example is to pull the remote git library. At the same time, pull several.

But generally speaking, this thing is actually used for this kind of thing: you have a pile.jpgYour address, that one.jpgIt’s also very simple1.jpg 2.jpg …. 1024.jpgIn this way, if you want the computer to download in batches as much as possible and directly re execute the simple and crude failure, and you don’t want to write too much code, then this definition will be useful.

The above needs can be written as follows. Let’s assume that your complete website is as follows:

https://shenqibaobei.xx.yoo/pics/1.jpg
https://shenqibaobei.xx.yoo/pics/2.jpg
https://shenqibaobei.xx.yoo/pics/3.jpg
...
https://shenqibaobei.xx.yoo/pics/1024.jpg

And you’ve built one calledguiguiThe folder is prepared to store these lovely pictures, then, after executionredoerOn the premise of the definition of, just follow the following code:

seq 1 1024 | xargs -P0 -i -- bash -c "$(declare -f redoer) ; redoer 'wget -q https://shenqibaobei.xx.yoo/pics/{}.jpg -O guigui/{}.jpg' "

Remember to have this folder, or it will have multiple processes trying again and again. I don’t know what will happen.

🐙🐙🐙🐙

EOF

🐊


Reprint compliance

When reprinting this article, please be sure to indicate the author and source:https://segmentfault.com/a/1190000040173495