Generation of random branches on Python /geek magazine

Remembering Dawkins, the main idea can be expressed as follows: if __ long __ keep a tornado over a garbage can, then a Boeing 747 might gather. The emergence of a structure from chaos by a durik: sorting and recombining everything in a row, from all the meaningless and disordered processes, one can see quite meaningful and ordered ones. If such processes are fixed and repeated in some way, then the system, which yesterday was a Brownian movement, today begins to look as if its behavior was set up by an invisible hand, and that it is making some actions that are meaningful from our point of view. At the same time, there is no hand at all. She set herself up.

To make sure of this again, I strive to write some kind of digital life, which, out of chaos and without unnecessary instructions from a person, will be able to randomly generate logic for itself and exist on it in its natural habitat - the operating system. Yes, in this, probably, there is a difference from many programs from the “Artificial Life” direction, which “live” in corrals, produce “predators” and “herbivores,” and co-exist in artificial fields with “food” and each other. None of these programs interact with system objects (processes, files, etc.), which means that the code does not really live. In addition, this code one way or another still performs some kind of task that a person needs and is very limited in scope because of this.

To implement code with a large degree of freedom of action in the operating system, which at the same time would not be just a chaotic set of executable instructions, a model appeared that consists of 3 modules. here . I was then prompted that I described the idea of Reinforcement Learning and the game of John Conway, entitled "Life." It may well be that I have nothing against using what has already been developed or openly. In the end, everything new is a synthesis of the already known, and I myself admitted that I adopted the idea of prioritizing flows, which is used in Windows. Here she is very suitable.

Currently, the mentioned function has slightly transformed:

def make_solution (p_random, p_deter):

deter_flag = 0

random_flag = 0

if p_random> = random.random ():

p_random- = ??? # priority balance

p_deter + = ???r3r3866. random_flag = 1

if p_deter> = random.random ():

p_deter- = ??? # priority balance

p_random + = ???r3r3866. deter_flag = 1

if random_flag == 1 and deter_flag == 0:

return (p_random, p_deter, 1)

elif deter_flag == 1 and random_flag == 0:

return (p_random, p_deter, -1)

else:

return (p_random, p_deter, 0)

At the input, it takes 2 probabilities (by default at the start they are both equal to 0.5), after which it checks their operation one by one. The triggered probability decreases itself by 1% and at the same time increases the other by 1%. Therefore, each time the probability works, it decreases, and the other increases. As a result, no probability gets too much advantage over another, and they self-balance, forming a normal distribution centered at 0.5 and with a working neighborhood of no more than + -10%, which distinguishes this function from the standard random, where the probability in our case It would always be equal to 0.5 and would not depend on previous calculations.

Figuratively speaking, it is a probability pendulum with a small amplitude. If the first probability worked and the second did not work, it returns ? otherwise -1 is returned, and if both worked or did not work, it is 0. Thus, the function ** make_solution ** 2 incoming probabilities returns one of 3 possible actions, giving, accordingly, a balanced decision at the fork with 3 possible continuation options. In the future, this function is likely to be universal, and will be able to take an indefinite number of probabilities, because the variation at the forks can be more than ? but in the case of the if-elif-else generator, three options for continuation are quite enough.

It should also be noted here that in the code there are different, so to say, typical forks. For example, as will be seen below, in the main function of the generator there is a fork in which there is a choice of a scheme for constructing a branch, of which there are only ? but other cases are also present in the code: insert an action block or start a recursion, how many action lines should be generated, how complicated it should be line with the condition, put or or and, elif or else.

I believe that the probabilistic pendulum, which we talked about above, should be set for each type of action: then the fork is balanced only on the basis of what happened earlier on this fork, and not in some other parts of the code. Those. when choosing the general branching structure, we have our own pair of probabilities, and inside, when its elements are built, another.

Of course, you can balance all actions with one pair, but then the probability at each fork will be very difficult and will depend on all previous actions at other junctions. The randomness of such a design will be even higher, but for now I personally am inclined to the first scheme, because I like the design where other small ones swing within the framework of one large swinging pendulum, i.e. smaller balances are born in one big balance. Plus, in the second scheme, randomness is also more than sufficient.

When writing the branch generator, it was necessary to make not only workable code that produces error-free generations, but also such code that __ maybe __ generate the maximum possible constructs of if-elif-else, but there are not 2 or 3 of such possible options. Consider, for example, the following possible schemes.

Under the ** icon.[] ** in schemes, I mean a set of expressions for a condition or a block of random actions. The most elementary scheme is ? where the condition simply goes, and after it the action block. 2a and 2b are if variations with one elif or one else. In option 2c, if already comes in combination with several elif without else. And finally, in option 2d, the most general scheme is presented, where if contains several elif and 1 else.

Everything would be simple if it were not for the need to build unlimited branches. After each if, elif or else, recursion can be called, which in turn can also recurse further and produce new elif-else blocks to the “right”. Let's look at the scheme of possible options.

Embodiments 2e and 2f show simple special cases of such recursive branching when recursion is called either after a single elif or after a single else. Option 2g describes the most complex and general case of such recursion, when after each elif there can be an action block + recursion (or immediately recursion), and the same thing can happen after else.

There are still variations when recursion occurs immediately after if or after if and an action block.

This is seen in options 3a and 3b. Option 3c shows such a scheme in the most general form.

This is not to say that the above schemes cover all possible options for constructing branches, but even in this form, the final code easily gives rise to branches of 150 lines, going “to the right” for 10-15 steps. In any case, complicating the scheme if necessary is not difficult.

You can look at an example of __ one __ such generation to make sure that the branches can be very diverse.

You do not need to pay attention to the composition of conditional expressions and action blocks - for visual simplicity, they are generated from only combinations of two variables, 3 expressions and a small number of arithmetic and logical signs. A discussion of the real “meat” for recombination is beyond the scope of this article (this will be discussed in the discussion of 3 modules).

Before proceeding to a direct review of the generator code, it is necessary to remember that the generated blocks must be shifted horizontally to the right, if it is elif, else, if recursion or action blocks, and also “go back” to the left after the branch completes. Moreover, given that Python is very picky about horizontal indents, it is desirable to make the step the same (in our case, the step is 3).

The following diagram illustrates how displacements are shifted.

The most important thing here is that the displacements with the deepening of the branch are always shifted to the right. However, if we have, for example, an elif-else block in which there are several elif or a single elif-else pair, then there is a need to “return” the carriage that floated to the right so that the next elif (or else) starts same offsets as the previous one in the block. To do this, you must save the original offset ( ** Wall_offset ** ) And after the generation of the branch (for example, complete branching of one elif), restore it. This ensures that the elif, else elements in the block are “on top of each other” evenly. Moreover, each new block has its own displacement. The same trick provides harmony in the overall if-elif-else construct (including recursions).

Now let's move on to the code. The code with a total volume of about 200 lines consists of 8 functions, one of which we examined above. Due to the recursive nature and the large number of parameters passed to functions, it can be poorly readable in places. To begin with, I will cite the very “meat” that is used to generate conditional expressions and action blocks.

var_list =['a','b']

exp_list =['a+b','b-a', 'b//a']

sign =['+','-','/','*','//']

sign2 =['>','<','==','>=','<=','!=']

a = 3

b = 2

As you can see, two variables are used: a and b ( ** Var_list ** ), Which are initialized, 3 arithmetic expressions ( ** Exp_list ** ), And also two sheets with signs ( ** Sign, sign2 ** ). As mentioned earlier, the composition of the resulting expressions does not matter now and is not considered in this article - they are needed mainly to illustrate the code. One more peculiarity should be noted: in the generation of the elif-else block, you need to track the appearance of the else and stop the generation, otherwise else may appear before elif, which naturally will cause an error. The flag ** is used for this purpose. fin_else_flag ** .

We begin our consideration with the main generation function.

def if_gen (exp_list, var_list, if_str, offset_koeff, fin_else_flag, prob_list):

choice_list =[exp_list, var_list]

base_offset = ''

# main structural fork

prob_list[0], prob_list[1], sol = make_solution (prob_list[0], prob_list[1])

# if + action block (1 option in the scheme)

if sol == 0:

# generate an action block with an offset of + 3

action_str = action_str_gen (choice_list, offset_koeff + ? prob_list)

return (base_offset * offset_koeff + 'if' + if_sub (exp_list, var_list, sign, prob_list) + ': n' + action_str, offset_koeff, fin_else_flag, prob_list)

# if + elif /else (option 2 in the diagram)

elif sol == -1:

if_str = base_offset * offset_koeff + 'if' + if_sub (exp_list, var_list, sign, prob_list) + ': n' + action_str_gen (choice_list, offset_koeff + ? prob_list) # if[]:

# fork elif /else

prob_list[2], prob_list[3], sol2 = make_solution (prob_list[2], prob_list[3])

if sol2! = 0:

ee_string = 'elif'

else:

ee_string = 'else'

# generation of elif /else

block. if_str, offset_koeff, fin_else_flag, prob_list = elif_else_block (ee_string, offset_koeff, exp_list, var_list, sign, if_str, choice_list, fin_else_flag, prob_list)

return (if_str, offset_koeff, fin_else_flag, prob_list)

# if + if (recursion) (3 option in the scheme)

else:

if_str = base_offset * offset_koeff + 'if' + if_sub (exp_list, var_list, sign, prob_list) + ': n' # if[]:

# fork if /if + action block

prob_list [4]prob_list[5], sol = make_solution (prob_list[4], prob_list[5])

if sol == 0:

# generate an action block with an offset of + 3

if_str + = action_str_gen (choice_list, offset_koeff + ? prob_list)

# save the offset

wall_offset = offset_koeff

if_rek, offset_koeff, fin_else_flag, prob_list = if_gen (exp_list, var_list, if_str, offset_koeff + ? fin_else_flag, prob_list) # if + if

recursion. # we attach the generated recursive piece

if_str + = if_rek

# fork elif-else block /action block

prob_list[4]prob_list[5], sol2 = make_solution (prob_list[4], prob_list[5])

if sol2! = 0:

prob_list[2], prob_list[3], sol3 = make_solution (prob_list[2], prob_list[3])

if sol3! = 0:

ee_string = 'elif'

else:

ee_string = 'else'

if_str, offset_koeff, fin_else_flag, prob_list = elif_else_block (ee_string, wall_offset, exp_list, var_list, sign, if_str, choice_list, fin_else_flag, prob_list)

else:

# generate an action block with an offset of + 3

if_str + = action_str_gen (choice_list, offset_koeff + ? prob_list)

return (if_str, offset_koeff, fin_else_flag, prob_list)

The function accepts in addition to lists with "meat" for generation (exp_list, var_list) also ** if_str ** - this is the line where the generated code is collected in turn. It is accepted here because the function itself is ** if_gen ** can be called recursively, and it would be advisable not to lose the piece of code generated earlier.

Parameter ** offset_koeff ** - this is the offset coefficient, which is a factor for a line with one space ( ** Base_offset ** ) And, accordingly, it is he who is responsible for the horizontal displacements of the code blocks.

About ** fin_else_flag ** we said above, here it is simply passed to a function that is responsible for generating if + elif /else (see below).

Well, there is another parameter - ** prob_list ** , which is a sheet with 10 probabilities (5 pairs of probabilities)

prob_list =[0.5 for y in range(0,10)]

and is used by the function ** make_solution ** as we discussed above: this or that pair of probabilities from it corresponding to the type of fork is transferred to it (for example, the main structural fork uses the first 2 probabilities in the sheet: ** prob_list[0] ** and ** prob_list[1] ** ). . The results of the probability changes in this sheet, as an example, can be seen in the following figure.

The probabilities in this list change from generation to generation, if during the next generation the corresponding piece of code gets executed.

In the function itself, the nested choice_list is initialized at the beginning - it is needed for convenient random generation of expressions from "meat", and the base offset is ** base_offset = '' ** in one space.

After that comes the main fork, which, through the make_solution function, gets the solution into the sol variable. Sol takes one of three values (? -1.1) and determines, therefore, according to what scheme the structure will be built.

The first option implements the simplest option if +[]. The response is formed as a string with the current offset (it is not necessarily equal to 0!), The string “if”, a random condition that is generated by the function ** if_sub ** (to be discussed later), carriage translation, and generation of an action block using the function ** action_str ** (see below). As a result, we get something like:

if ((a + b) == (b)):

b = b

a = b-a

a = a

The second option is responsible for generating this type: if[]+ elif /else-block (option 2 in the schemes). First, the if +[]line is formed there similarly. , then comes the elif /else fork, which decides whether the elif-else block will be generated, just if-elif or if-else (function e ** lif_else_block ** - see below). Results may vary. For example:

if ((a + b) == (a)):

b = a + b

elif ((b //a) == (a)):

None

elif ((a + b) <=(a)):

a = b //a

else:

if ((b) <=(a)):

a = b-a

b = a

3-3r3835.3-3r3862.

if ((a) == (b-a)):

b = b-a

b = b

a = b

a = b-a

elif ((b)> (ba)) and ((a) <(b-a)):

if ((b //a) <(a)):

b = ba

elif ((a + b) <(b-a))and((b)<(a+b))or((a+b)==(a+b)):

b = b

a = ba

elif ((a)> (ba)): 3-3-33866. None 3-3-33834.3-3-33835.3-33838.

if ((b) <=(b-a))or((a+b)> = (b)):

a = a

b = b

elif ((b) <=(b)):

if ((a)> = (b)):

a = a + b

a = b

elif ((b)> = (a)):

a = ba

A = a

If ((a)> = (b)) and ((b //a) == (a)) and ((b //a)! = (B)):

B = ba

else:

a = b //a

if ((b //a) <(b-a)):

a = b

a = ba

else:

if ((a) == (b) ):

A = a

A = b //a

B = b

B = a + b

B = a

Else:

None

The third option implements recursion from the very beginning (option 3 in the schemes), i.e. gives rise to a branch of the form:

if ((a) == (a)):

if ((a + b) <(b)): 3-3r3835.3-3r3862.

or

if ((b-a) <=(a)):

a = a

if ((b-a) == (b)):

a = a

a = a

First, the if line is formed (similarly), then a fork appears, which decides whether to further insert the action block or not, after which the offset is saved and recursion is called. The offset must be saved so that after the recursion is completed and the piece of code is returned, it is possible to add another elif-else block at the same offset as the original line with if. Here you can see how elif and else in the branch stand at the same offset with their "native" if.

if ((b-a) == (b)):

if ((a)> (a + b)):

if ((b) == (b-a)):

b = b

a = a

elif ((b)> (b)):

None

else:

None

b = a

b = b

Next comes a fork in the elif-else-block /action block, which decides whether to add an action block or an elif-else block after recursion. If you decide to add an elif-else block, then there, similarly to the case described above, in scheme ? elif or else is selected.

Here it is necessary to pay attention to the fact that recursion is called with an offset of + 3 to shift the generated code to the right by a step, and the elif-else block is called with an offset of wall_offset so that this block does not go to the right after the recursion, but remains with the the offset of the original if.

The results can be quite different: from simple to complex, but the appearance of recursion immediately produces the most ornate branches.

if ((ba)> (a + b)) and ((b) <(a+b)):

if ((ba) <=(a+b)):

b = b //a

elif ((b)! = (a)):

a = ba

else:

if ((a + b)! = (ba)):

a = a

if ((b) <(b-a)):

if ((a + b) == (ba)) and ((ba) <(a+b))and((b-a)==(a))and((a)> (b //a)) or ((a + b)> (b //a)):

If ((b)> = (ba)):

A = b

B = b

If ((b)> (b)):

A = a + b

B = a + b

a = a

b = a + b

b = b //a

b = a

else:

b = a + b

a = b

a = b

elif ((a) <(b-a)):

a = b //a

a = ba

if ((a)> = (ba)) or ((a)> = (a)) or ((b) <=(b)):

a = a

a = a

elif ((a) == (a)) and ((b)> (ba)):

a = b //a

if ((a) <(b)):

if ((a + b) == (ba)):

a = a

if ((a)! = (b //a)):

if ((b //a)! = (a)) and ((ba)> = (b)):

a = b

else :

None

A = b //a

Else:

B = b

B = a + b

If ((ba) <=(b//a)):

A = b

A = b

A = a + b

else:

a = a + b

if ((ba)> = (a)):

a = b

if ((ba) == (a)) or ((b) ! = (b //a)):

a = ba

a = a

a = a

a = b //a

a = a + b

b = a

.

Now let's look at the function ** elif_else_block ** , which is responsible for forming the elif-else block and is called from the main function ** if_gen ** .

def elif_else_block (ee_string, offset_koeff, exp_list, var_list, sign, if_str, choice_list, fin_else_flag, prob_list):

if ee_string == 'elif':

sol3 = 9

# save the offset

wall_offset = offset_koeff

# elif generation in

loop. while sol3! = 0 and fin_else_flag! = 1:

temp_str, offset_koeff, fin_else_flag, prob_list = elif_else ('elif', wall_offset, exp_list, var_list, sign, if_str, choice_list, fin_else_flag, prob_list)

if_str + = temp_str

prob_list[6], prob_list[7], sol3 = make_solution (prob_list[6], prob_list[7])

# fork - do I add else to the elif group?

prob_list[2], prob_list[3], sol = make_solution (prob_list[2], prob_list[3])

if sol! = 0:

# add else, so set the flag

fin_else_flag = 1

temp_str, offset_koeff, fin_else_flag, prob_list = elif_else ('else', wall_offset, exp_list, var_list, sign, if_str, choice_list, fin_else_flag, prob_list)

if_str + = temp_str

return (if_str, offset_koeff, fin_else_flag, prob_list)

# else

generation. else:

temp_str, offset_koeff, fin_else_flag, prob_list = elif_else ('else', offset_koeff, exp_list, var_list, sign, if_str, choice_list, fin_else_flag, prob_list)

if_str + = temp_str

return (if_str, offset_koeff, fin_else_flag, prob_list)

This function decides whether to add an elif or elif /else block to the code. She does not decide whether to simply put else, but depends on the input value e ** e_string ** which she gets from the main function ** if_gen ** . First, the elif block is generated in the ** cycle. while ** where checked 2 conditions: probabilistic - the number of elifs in the block and the flag ** depend on it. fin_else_flag ** , which, if it suddenly turns on, it means that else else has joined before, and therefore it is necessary to exit the loop.

The decision whether to attach else to the elif block is also decided by a fork with the help of the same function ** make_solution ** , and if else is attached, then the flag ** is turned on immediately. fin_else_flag ** which stops block generation.

The direct joining of elif and else is carried out by the function ** elif_else ** (see below). It should be noted that when generating the elif block (as well as when else is attached to it), the offset ** is used. wall_offset ** to smoothly build the block as a whole.

Now consider the function ** elif_else ** .

** def elif_else (ee_string, offset_koeff, exp_list, var_list, sign, if_str, choice_list, fin_else_flag, prob_list):ee_str = ''# forming an else: or elif[]string. :if ee_string == 'else':ee_str + = '' * offset_koeff + ee_string + ': n'elif ee_string == 'elif':ee_str + = '' * offset_koeff + ee_string + '' + if_sub (exp_list, var_list, sign, prob_list) + ': n'# fork action block-None /action block + recursionprob_list[2], prob_list[3], sol = make_solution (prob_list[2], prob_list[3])if sol! = 0:prob_list[6], prob_list[7], sol2 = make_solution (prob_list[6], prob_list[7])if sol2! = 0:# action blockee_str + = action_str_gen (choice_list, offset_koeff + ? prob_list)else:# Noneee_str + = '' * (offset_koeff + 3) + 'Nonen'return (ee_str, offset_koeff, fin_else_flag, prob_list)else:# subdivision action blockprob_list[6], prob_list[7], sol2 = make_solution (prob_list[6], prob_list[7])if sol2 == 0:# action blockee_str + = action_str_gen (choice_list, offset_koeff + ? prob_list)# recursion if_genif_str, offset_koeff, fin_else_flag, prob_list = if_gen (exp_list, var_list, if_str, offset_koeff + ? fin_else_flag, prob_list)ee_str + = if_strreturn (ee_str, offset_koeff, fin_else_flag, prob_list) The function is responsible for the formation of the elif or else line itself, as well as for the subsequent generation of action or recursion blocks after these lines. It also takes the variable [b] ee_string ** , which contains either elif or else, and forms the corresponding string. Then there is a fork, where it is determined what will go next: (action block or None), or (action block or action block + recursion). Inside this fork there is a division, respectively, into two subforks, and in each case the function

**is called. make_solution**with appropriate parameters for making a decision.

It should be noted that when

is found in the code.

if sol! = 0

, this means that we intentionally give an advantage to one part of the code over another, because if sol! = ? then it equals either -? or ? and therefore another piece of code will be executed less often (only when sol == 0). This is used in particular in the function

**elif_else_block**, where it is more profitable for us to let more elifs form in the block, rather than give equal probability of elif and else. Or, for example, in the function

**elif_else**we give an advantage to the option when an action block or None is formed rather than what the recursion is going for - otherwise the branches can grow to very indecent sizes.

We only need to consider the functions responsible for the random generation of expressions in conditions and blocks of actions. As I said above, at this stage they do not play a decisive role and are introduced here to generally show what the final generated code will look like. But since they are used in the generator, we will look at them briefly.

Function responsible for generating the action block

**action_str**.

def action_str_gen (choice_list, offset_koeff, prob_list):

sol = 9

curr_offset = '' * offset_koeff

act_str = ''

while sol! = 0:

act_str + = curr_offset + rand (rand (choice_list[1])) + '=' + rand (rand (choice_list)) + 'n'

prob_list[6], prob_list[7], sol = make_solution (prob_list[6], prob_list[7])

return (act_str)

Everything is quite simple here: from the nested list choise_list, which, as we recall, consists of v

**ar_list**(list of variables) and

**exp_list**(list of expressions), this function makes up one or more lines of this kind: a = a + b or b = b. Those. either an expression is assigned to the variable, or another variable (including itself). The rand function randomly selects an element from the list and is needed here solely in order not to produce monstrous strings.

def rand (t_list):

return (t_list[random.randint(0,len(t_list)-1)])

The function of generating expressions is

**if_sub**for conditions it looks more.

def if_sub (exp_list, var_list, sign, prob_list):

sub_str = ''

sol = 9

choice_list =[exp_list, var_list]

flag = 0

while sol! = 0:

prob_list[6], prob_list[7], sol = make_solution (prob_list[6], prob_list[7])

sub_str + = '((' + rand (rand (choice_list)) + ')' + rand (sign2) + '(' + rand (rand (choice_list)) + '))'

if flag == 1 and sol == 1:

sub_str + = ')'

flag = 0

or_and_exp = or_and (prob_list)

if len (or_and_exp):

sub_str + = or_and_exp

else:

break

prob_list[6], prob_list[7], sol2 = make_solution (prob_list[6], prob_list[7])

if sol2 == 1 and (sub_str[-1]== 'D' or sub_str[-1]== 'R') and flag == 0:

sub_str + = '('

flag = 1

if sub_str[-1]== '(':

if sub_str[-2]== 'd':

sub_str = sub_str6r2. = 'r':

sub_str = sub_str[0:-3]

else:

sub_str = sub_str[0:-1]

elif sub_str[-1]== 'd':

'r':

sub_str = sub_str[0:-2]

else:

None

if flag == 1:

sub_str + = ')'

return (sub_str)

rr6666r else. .3-3r3834.3-3r3835.3-3r3862.

It generates expressions by type:

**((a)> = (ba)) or ((a)> = (a)) or ((b) <=(b))**Moreover, both the left and right sides can have different options and stand as separate variables, as well as expressions or their groups.The logical operators 3–3–3844. or 3–3–3845. and 3–3–3844. and 3–3–3845., which are selected for convenience using the function 3–3–3842. or_and_exp 3–3–3843.

def or_and (prob_list):

prob_list[8], prob_list[9], sol = make_solution (prob_list[8], prob_list[9])

if sol == - 1:

return ('and')

elif sol == 1:

return ('or')

else:

return ('')

The rest of the function is

**if_sub**cuts off excess tails from expressions and adds, when necessary, closing brackets - to consider these dances with tambourines here, I think, is inexpedient.

Well, that's all. You can start the generator, for example, like this:

var_list =['a','b']

exp_list =['a+b','b-a', 'b//a']

sign =['+','-','/','*','//']

sign2 =['>','<','==','>=','<=','!=']

a = 3

b = 2

prob_list =[0.5 for y in range(0,10)]

while True:

if_str = ''

if_str, offset_koeff, fin_else_flag, prob_list = if_gen (exp_list, var_list, if_str, ?? prob_list)

try:

exec (compile (if_str, 'gen', 'exec'))

print (if_str)

input ()

except ZeroDivisionerror:

None

except:

print ('error')

print (if_str)

input ()

First, input, including a sheet with probabilities of

**prob_list**, then in an infinite loop call the main function

**if_gen**and start the resulting generated string for execution. It is worth processing separately ZeroDivisionerror, because division by zero with such a random construction of expressions is very common. After the launch, just press Enter for the next generation to appear. Most often they will be quite simple, but often branched and even very branched. Well,

__import random__at the beginning it would also be nice to insert;) For those who do not want to see collecting everything by hand, you can download a file with Github (file if_gen.py).

In conclusion, I want to say that the code I presented was tested on hundreds of thousands of generations without errors, while it demonstrated the whole palette of if-elif-else schemes that I wanted to finally see. Once, by mistake, I gave in one part of the code a too high probability of recursion and I got 5?000 (!) Lines of generation and it was working (although the comp suspended 30 seconds). This also indicates the reliability of the algorithm.

Probably, it was possible to write more concisely somewhere, to optimize somewhere, to compose the main function in another way, but the main thing is that this code works and generates about 250 generations per second, which, in my opinion, is quite acceptable.

I never considered this code as self-sufficient - it is just a module of the future digital organism and was written for research purposes, so it hardly has any practical applications. At the same time, I am not responsible for any consequences for anyone using the above code, and I urge everyone to cut bread with a knife for cutting bread, and not something else.

In the next article, we will consider the second module, which will be responsible for the random formation of experience. This topic promises to be much more interesting than the if generator, and I will definitely post the results as soon as I have them.

It may be interesting

#### r6lxcko396

Author**28-05-2020, 17:10**

Publication Date
#### Abnormal programming / Python / Programming / Algorithms

Category- Comments: 0
- Views: 19