Menghasilkan Cabang Acak dengan Python

gambar

Ingat Dawkins, gagasan utama dapat diungkapkan sebagai berikut: jika Anda menyimpan tornado di tempat sampah untuk waktu yang lama , maka Boeing 747 dapat berkumpul. Munculnya suatu struktur dari kekacauan oleh seorang durik: menyortir dan menggabungkan kembali semuanya secara berurutan, dari semua proses yang tidak berarti dan tidak teratur, orang dapat melihat proses yang cukup bermakna dan teratur. Jika proses semacam itu diperbaiki dan diulangi dalam beberapa cara, maka sistem, yang kemarin adalah gerakan Brown, hari ini mulai terlihat seolah-olah perilakunya diatur oleh tangan yang tak terlihat, dan bahwa ia membuat beberapa tindakan yang bermakna dari sudut pandang kami. Pada saat yang sama, tidak ada tangan sama sekali. Dia mengatur dirinya sendiri.

Untuk memastikan hal ini lagi, saya berusaha untuk menulis semacam kehidupan digital, yang, dari kekacauan dan tanpa instruksi yang tidak perlu dari seseorang, akan dapat secara acak menghasilkan logika untuk dirinya sendiri dan ada di sana di habitat aslinya - sistem operasi. Ya, dalam hal ini, mungkin, ada perbedaan dari banyak program dari arah "Kehidupan Buatan", yang "hidup" dalam koral, menghasilkan "predator" dan "herbivora," dan hidup berdampingan di ladang buatan dengan "makanan" dan satu sama lain. Tidak satu pun dari program ini berinteraksi dengan objek sistem (proses, file, dll.), Yang berarti bahwa kode tersebut tidak benar-benar hidup. Selain itu, kode ini dengan satu atau lain cara masih melakukan beberapa jenis tugas yang dibutuhkan seseorang dan cakupannya sangat terbatas karena hal ini.

Untuk mengimplementasikan kode dengan tingkat kebebasan tindakan yang besar dalam sistem operasi, yang pada saat yang sama tidak hanya berupa kumpulan instruksi yang dapat dieksekusi yang kacau, sebuah model muncul yang terdiri dari 3 modul.

  1. Modul generasi acak dari kode yang dapat dieksekusi utama
  2. Modul Pendidikan Acak
  3. Modul "visi komputer" objek OS

Pada artikel ini, kita akan berbicara tentang modul pertama, yang sejauh ini hanya generasi percabangan acak, yaitu konstruksi seperti "if-elif-else". Mengapa bercabang? Karena, pada umumnya, kehidupan setiap organisme hidup terdiri dari reaksi terkondisi: semua yang kita lakukan adalah respons terhadap informasi yang dirasakan. Sel membelah jika kondisi tertentu terjadi, korban mencoba melarikan diri jika ia melihat predator yang lebih kuat, dan jika ia lebih lemah, ia dapat mencoba untuk menyerangnya, kecoak berserakan jika lampu menyala, seseorang pergi makan, jika ia lapar, dll. dll. - baris ini tidak ada habisnya. Tidak ada tindakan independen dan terpisah yang tidak dikondisikan oleh apa pun. Akibatnya, perilaku organisme hidup khususnya digambarkan sebagai reaksi terhadap kondisi: JIKA [sesuatu] MAKA [sesuatu]. Kami mencoba menghasilkan perilaku ini.

Mengapa secara acak? Untuk meninggalkan kode peluang maksimum untuk bertindak secara independen dan memindahkan orang (programmer) sejauh mungkin dari proses ini (idealnya sepenuhnya dikecualikan). Yang terakhir adalah yang paling sulit bagi programmer, karena pemrograman standar, yang semua orang terbiasa, menyerupai pelatihan keras hewan, yang harus melakukan persis apa yang ditunjukkan oleh programmer, persis seperti yang ditunjukkannya ketika ditunjukkan. Di sini situasinya sebaliknya: kode yang dihasilkan akhir harus bertindak sehingga tidak dapat diprediksi oleh pembuat generatornya.

Sebelum kita beralih ke diagram dan kode generator, kita perlu memikirkan fungsi pengambilan keputusan, yang digunakan sebagai konduktor, memungkinkan satu atau bagian lain dari kode dieksekusi. Saya menulis tentang dia sebelumnyadi sini . Saya kemudian diminta untuk menggambarkan ide Reinforcement Learning dan permainan John Conway, berjudul "Life." Mungkin saya tidak menentang penggunaan apa yang telah dikembangkan atau secara terbuka. Pada akhirnya, semua yang baru adalah sintesis dari yang sudah diketahui, dan saya sendiri mengakui bahwa saya mengadopsi ide memprioritaskan aliran, yang digunakan di Windows. Di sini dia sangat cocok.

Saat ini, fungsi yang disebutkan telah sedikit diubah:

def make_solution(p_random, p_deter):                       
    deter_flag = 0
    random_flag = 0
    if p_random >= random.random():
            p_random-=0.01                                  #  
            p_deter+=0.01
            random_flag = 1
    if p_deter >= random.random():
            p_deter-=0.01                                   #  
            p_random+=0.01
            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)

Pada input, dibutuhkan 2 probabilitas (secara default di awal keduanya sama dengan 0,5), setelah itu memeriksa operasi mereka satu per satu. Probabilitas yang dipicu berkurang sendiri sebesar 1% dan pada saat yang sama meningkatkan yang lain sebesar 1%. Oleh karena itu, setiap kali probabilitas bekerja, ia menurun, dan yang lainnya meningkat. Akibatnya, tidak ada probabilitas mendapat terlalu banyak keuntungan di atas yang lain, dan mereka menyeimbangkan diri, membentuk distribusi normal yang berpusat pada 0,5 dan dengan lingkungan kerja tidak lebih dari + -10%, yang membedakan fungsi ini dari acak standar, di mana probabilitas dalam kasus kami Itu akan selalu sama dengan 0,5 dan tidak akan tergantung pada perhitungan sebelumnya.

Secara kiasan, itu adalah pendulum probabilitas dengan amplitudo kecil. Jika probabilitas pertama berhasil dan yang kedua tidak berhasil, maka mengembalikan 1, jika tidak -1 dikembalikan, dan jika keduanya bekerja atau tidak berhasil, 0. Dengan demikian, fungsi make_solution untuk 2 probabilitas yang masuk mengembalikan satu dari 3 tindakan yang mungkin, memberikan keseimbangan solusi bercabang dua dengan kemungkinan opsi kelanjutan. Di masa depan, fungsi ini mungkin bersifat universal, dan akan dapat mengambil jumlah probabilitas yang tidak terbatas, karena variasi pada fork bisa lebih dari 3, tetapi dalam kasus if-elif-else generator, tiga opsi untuk kelanjutan sudah cukup.

Perlu juga dicatat di sini bahwa dalam kode ada yang berbeda, sehingga bisa dikatakan, garpu khas. Misalnya, seperti yang akan dilihat di bawah ini, dalam fungsi utama generator ada garpu di mana ada pilihan skema untuk membangun cabang, di mana hanya ada 3, tetapi kasus-kasus lain juga hadir dalam kode: masukkan blok tindakan atau mulai rekursi, berapa banyak garis tindakan untuk menghasilkan, seberapa rumit seharusnya Sejalan dengan kondisi, menempatkan atau atau dan, elif atau yang lain.

Saya percaya bahwa pendulum probabilistik, yang telah kita bahas di atas, harus ditetapkan untuk setiap jenis tindakan: maka garpu hanya seimbang berdasarkan apa yang terjadi sebelumnya pada garpu ini, dan bukan di beberapa bagian lain dari kode. Itu ketika memilih struktur percabangan umum, kita memiliki pasangan probabilitas kita sendiri, dan di dalam, ketika elemen-elemennya dibangun, yang lain.

Tentu saja, Anda dapat menyeimbangkan semua tindakan dengan satu pasangan, tetapi kemudian probabilitas pada setiap garpu akan sangat sulit dan akan tergantung pada semua tindakan sebelumnya di persimpangan lain. Keacakan dari desain seperti itu akan lebih tinggi, tetapi untuk saat ini saya pribadi cenderung ke skema pertama, karena saya suka desain di mana yang kecil berayun dalam kerangka satu pendulum ayunan besar, yaitu. saldo yang lebih kecil dilahirkan dalam satu saldo besar. Plus, dalam skema kedua, keacakan juga lebih dari cukup.

Saat menulis generator cabang, perlu untuk membuat tidak hanya kode yang bisa diterapkan yang menghasilkan generasi bebas kesalahan, tetapi juga kode seperti itu yang bisamenghasilkan konstruksi maksimum yang mungkin dari if-elif-else, tetapi tidak ada 2 atau 3 opsi yang memungkinkan. Pertimbangkan, misalnya, skema yang mungkin berikut.

gambar

Dengan ikon [..] dalam skema yang saya maksud serangkaian ekspresi untuk kondisi atau blok tindakan acak. Skema yang paling dasar adalah 1, di mana kondisinya cukup, dan setelah itu blok tindakan. 2a dan 2b adalah jika variasi dengan satu elif atau yang lain. Dalam opsi 2c, jika sudah datang dalam kombinasi dengan beberapa elif tanpa yang lain. Dan akhirnya, dalam opsi 2d, skema yang paling umum disajikan, di mana jika berisi beberapa elif dan 1 lainnya.

Semuanya akan sederhana jika bukan karena kebutuhan untuk membangun cabang yang tidak terbatas. Setelah masing-masing jika, elif atau yang lain, rekursi dapat disebut, yang pada gilirannya juga dapat berulang lagi dan menghasilkan blok elif-else baru di "kanan". Mari kita lihat skema opsi yang memungkinkan.

gambar

Perwujudan 2e dan 2f menunjukkan kasus khusus sederhana dari percabangan rekursif seperti ketika rekursi disebut setelah satu elif tunggal atau setelah satu lainnya. Opsi 2g menjelaskan kasus rekursi yang paling kompleks dan umum, ketika setelah setiap elif akan ada blok tindakan + rekursi (atau rekursi segera), dan hal yang sama dapat terjadi setelah yang lain.

Masih ada variasi ketika rekursi terjadi segera setelah jika atau setelah jika dan blok tindakan.

gambar

Ini terlihat dalam opsi 3a dan 3b. Opsi 3c menunjukkan skema seperti itu dalam bentuk paling umum.

Ini bukan untuk mengatakan bahwa skema di atas mencakup semua opsi yang mungkin untuk membangun cabang, tetapi bahkan dalam bentuk ini, kode akhir dengan mudah menimbulkan cabang dari 150 baris, pergi "ke kanan" selama 10-15 langkah. Bagaimanapun, memperumit skema jika perlu tidak sulit.

Anda dapat melihat contoh salah satu generasi tersebut untuk memastikan bahwa cabang-cabangnya dapat sangat beragam.

gambar

Anda tidak perlu memperhatikan komposisi ekspresi kondisional dan blok aksi - untuk kesederhanaan visual, mereka dihasilkan dari kombinasi hanya dari dua variabel, 3 ekspresi dan sejumlah kecil aritmatika dan tanda-tanda logis. Diskusi tentang โ€œdagingโ€ nyata untuk rekombinasi berada di luar ruang lingkup artikel ini (ini akan dibahas dalam diskusi 3 modul).

Sebelum melanjutkan ke pemeriksaan langsung kode generator, perlu diingat bahwa blok yang dihasilkan harus bergeser horizontal ke kanan, jika itu elif, jika tidak, rekursi atau blok aksi, dan juga "kembali" ke kiri setelah cabang selesai. Selain itu, mengingat bahwa Python sangat pilih-pilih tentang indentasi horisontal, diinginkan untuk membuat langkah yang sama (dalam kasus kami, langkahnya adalah 3).

Diagram berikut menggambarkan bagaimana perpindahan digeser.

gambar

Yang paling penting di sini adalah bahwa pemindahan dengan pendalaman cabang selalu bergeser ke kanan. Namun, jika kita memiliki, misalnya, blok elif-lain di mana ada beberapa pasangan elif atau satu pasangan elif lain, maka ada kebutuhan untuk "mengembalikan" kereta yang melayang ke kanan, sehingga elif berikutnya (atau yang lain) dimulai dengan offset yang sama dengan yang sebelumnya di blok. Untuk melakukan ini, Anda harus menyimpan offset asli ( wall_offset) dan setelah akhir generasi cabang (misalnya, percabangan penuh satu elif), kembalikan. Ini memastikan bahwa elemen elif, kalau tidak, di blok "di atas satu sama lain" secara merata. Apalagi setiap blok baru memiliki perpindahannya sendiri. Trik yang sama memberikan harmoni dalam keseluruhan konstruksi if-elif-else (termasuk rekursi).

Sekarang mari kita beralih ke kode. Kode dengan volume total sekitar 200 baris terdiri dari 8 fungsi, salah satunya kami periksa di atas. Karena sifat berulang dan sejumlah besar parameter dilewatkan ke fungsi, itu bisa dibaca di tempat. Untuk mulai dengan, saya akan mengutip "daging" yang digunakan untuk menghasilkan ekspresi kondisional dan blok aksi.

var_list = ['a','b']
exp_list = ['a+b','b-a', 'b//a']
sign = ['+','-','/','*','//']
sign2 = ['>','<','==','>=','<=','!=']
a = 3
b = 2

Seperti yang Anda lihat, dua variabel digunakan: a dan b ( var_list ), yang diinisialisasi, 3 ekspresi aritmatika ( exp_list ), serta dua lembar dengan tanda ( sign, sign2 ). Seperti disebutkan sebelumnya, komposisi dari ekspresi yang dihasilkan tidak masalah sekarang dan tidak dipertimbangkan dalam artikel ini - mereka diperlukan terutama untuk menggambarkan kode. Satu lagi keanehan yang harus diperhatikan: pada generasi blok elif-else, Anda perlu melacak tampilan yang lain dan menghentikan generasi, jika tidak, mungkin akan muncul sebelum elif, yang secara alami akan menyebabkan kesalahan. Bendera fin_else_flag digunakan untuk tujuan ini .

Kami memulai pertimbangan kami dengan fungsi generasi utama.

def if_gen(exp_list, var_list, if_str, offset_koeff, fin_else_flag, prob_list):             
    choice_list = [exp_list, var_list]
    base_offset = ' '
    #   
    prob_list[0],prob_list[1],sol = make_solution(prob_list[0],prob_list[1])       
    # if +   (1   )        
    if sol == 0: 
        #     +3                                                                   
        action_str = action_str_gen(choice_list, offset_koeff+3, 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 (2   )           
    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+3, prob_list) # if [..]:
        #  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'
        #   elif/else
        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() (3   )
    else:                                                                                   
            if_str= base_offset*offset_koeff+'if '+ if_sub(exp_list,var_list, sign, prob_list) +':\n' # if [..]:
            #  if/if+ 
            prob_list[4],prob_list[5],sol = make_solution(prob_list[4],prob_list[5])        
            if sol==0:
                #     +3
                if_str+=action_str_gen(choice_list, offset_koeff+3, prob_list)      
            #          
            wall_offset = offset_koeff                                                      
            if_rek, offset_koeff, fin_else_flag, prob_list = if_gen(exp_list, var_list, if_str, offset_koeff+3, fin_else_flag, prob_list) #  if+if
            #    
            if_str+=if_rek   
            #   elif-else/                                                                
            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:
                #     +3
                if_str+=action_str_gen(choice_list, offset_koeff+3, prob_list)              
            return(if_str, offset_koeff,fin_else_flag, prob_list)

Selain daftar dengan "daging" untuk generasi (exp_list, var_list), fungsi juga menerima if_str - ini adalah baris di mana kode yang dihasilkan dikumpulkan secara bergantian. Ini diterima di sini karena fungsi if_gen itu sendiri dapat dipanggil secara rekursif, dan disarankan untuk tidak kehilangan bagian kode yang dihasilkan sebelumnya.

Parameter offset_koeff adalah koefisien offset, yang merupakan faktor untuk sebuah garis dengan satu spasi ( base_offset ) dan, karenanya, bertanggung jawab atas perpindahan horizontal blok kode. Kami berbicara

tentang fin_else_flag di atas, di sini ia hanya diteruskan ke fungsi yang bertanggung jawab untuk menghasilkan if + elif / else (lihat di bawah).

Nah, ada parameter lain -prob_list , yang merupakan sheet dengan 10 probabilitas (5 pasang probabilitas)
prob_list = [0.5 for y in range(0,10)] 
dan itu digunakan oleh fungsi make_solution seperti yang kita bahas di atas: satu atau beberapa probabilitas dari itu terkait dengan jenis garpu diteruskan ke sana (misalnya, garpu struktural utama menggunakan 2 probabilitas pertama dalam lembaran: prob_list [0] dan prob_list [1] ). Hasil dari perubahan probabilitas dalam lembar ini, sebagai contoh, dapat dilihat pada gambar berikut.

gambar

Probabilitas dalam daftar ini berubah dari generasi ke generasi, jika selama generasi berikutnya sepotong kode yang sesuai dijalankan.

Dalam fungsi itu sendiri, choice_list bersarang diinisialisasi pada awal - diperlukan untuk menghasilkan ekspresi acak yang mudah dari "daging", dan base_offset base_offset = '' dalam satu ruang.

Setelah itu muncul garpu utama, yang, melalui fungsi make_solution, mendapatkan solusi ke dalam variabel sol. Sol mengambil satu dari tiga nilai (0, -1.1) dan menentukan, oleh karena itu, sesuai dengan skema apa struktur akan dibangun.

Opsi pertama mengimplementasikan opsi paling sederhana jika + [..]. Jawabannya dibentuk sebagai string dengan offset saat ini (tidak harus sama dengan 0!), String "jika", kondisi acak yang dihasilkan oleh fungsi if_sub (yang akan dibahas nanti), carriage return, dan pembuatan blok tindakan menggunakan fungsi action_str (lihat di bawah) . Akibatnya, kami mendapatkan sesuatu seperti:

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

Opsi kedua bertanggung jawab untuk menghasilkan tipe ini: if [..] + elif / else-block (opsi 2 dalam skema). Pertama, garis if [[]] terbentuk di sana, kemudian garpu elif / else muncul, yang memutuskan apakah blok elif-else akan dihasilkan, hanya jika-elif atau if-else (fungsi e lif_else_block - lihat di bawah). Hasil dapat bervariasi. Contohnya:

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

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

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=b-a
   a=a
   if ((a)>=(b))and((b//a)==(a))and((b//a)!=(b)):
      b=b-a
else:
   a=b//a
   if ((b//a)<(b-a)):
      a=b
      a=b-a
   else:
      if ((a)==(b)):
         a=a
         a=b//a
         b=b
         b=a+b
         b=a
      else:
         None

Opsi ketiga mengimplementasikan rekursi dari awal (opsi 3 dalam skema), yaitu memunculkan cabang bentuk:

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

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

Pertama, jika garis terbentuk (sama), maka garpu muncul, yang memutuskan apakah akan lebih lanjut menyisipkan blok tindakan atau tidak, setelah itu offset disimpan dan rekursi disebut. Offset harus disimpan sehingga setelah rekursi selesai dan potongan kode dikembalikan, dimungkinkan untuk menambahkan blok elif-else lain pada offset yang sama dengan baris asli dengan if. Di sini Anda dapat melihat bagaimana elif dan lainnya di cabang berdiri diimbangi dengan "asli" mereka jika.

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

Selanjutnya datang garpu di blok elif-else-block / action, yang memutuskan apakah akan menambahkan blok aksi atau blok elif-else setelah rekursi. Jika Anda memutuskan untuk menambahkan blok elif-else, maka di sana, mirip dengan kasus yang dijelaskan di atas, dalam skema 2, elif atau yang lain dipilih.

Di sini perlu untuk memperhatikan fakta bahwa rekursi disebut dengan offset +3 untuk menggeser kode yang dihasilkan ke kanan dengan langkah, dan blok elif-lain dipanggil dengan offset wall_offset sehingga blok ini tidak pergi ke kanan setelah rekursi, tetapi tetap dengan "asli" offset dari yang asli jika.

Hasilnya bisa sangat berbeda: dari yang sederhana sampai yang kompleks, tetapi penampilan rekursi segera menghasilkan cabang yang paling berhias.

if ((b-a)>(a+b))and((b)<(a+b)):
   if ((b-a)<=(a+b)):
      b=b//a
   elif ((b)!=(a)):
      a=b-a
else:
   if ((a+b)!=(b-a)):
      a=a

if ((b)<(b-a)):
   if ((a+b)==(b-a))and((b-a)<(a+b))and((b-a)==(a))and((a)>(b//a))or((a+b)>(b//a)):
      if ((b)>=(b-a)):
         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=b-a

if ((a)>=(b-a))or((a)>=(a))or((b)<=(b)):
   a=a
   a=a
elif ((a)==(a))and((b)>(b-a)):
   a=b//a
   if ((a)<(b)):
      if ((a+b)==(b-a)):
         a=a
         if ((a)!=(b//a)):
            if ((b//a)!=(a))and((b-a)>=(b)):
               a=b
            else:
               None
               a=b//a
      else:
         b=b
         b=a+b
         if ((b-a)<=(b//a)):
            a=b
            a=b
            a=a+b
else:
   a=a+b
   if ((b-a)>=(a)):
      a=b
      if ((b-a)==(a))or((b)!=(b//a)):
         a=b-a
         a=a
         a=a
         a=b//a
         a=a+b
         b=a

Sekarang mari kita lihat fungsi elif_else_block , yang bertanggung jawab untuk membentuk blok elif-else dan dipanggil dari fungsi if_gen utama .

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
        #  
        wall_offset = offset_koeff
        #  elif  
        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])
        #  -   else   elif?
        prob_list[2],prob_list[3],sol = make_solution(prob_list[2],prob_list[3])
        if sol!=0:
            #  else,   
            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
    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)

Fungsi ini memutuskan apakah akan menambahkan blok elif atau elif / else ke kode. Dia tidak memutuskan apakah akan memasukkan yang lain, tetapi tergantung pada nilai input e e_string , yang dia terima dari fungsi utama if_gen . Pertama, blok elif dihasilkan di loop sementara , di mana 2 kondisi diperiksa: probabilistic - jumlah elif di blok dan flag fin_else_flag bergantung padanya , dan jika tiba-tiba menyala, itu berarti ada yang terhubung sebelum itu, dan karena itu Anda harus keluar dari loop .

Keputusan apakah akan melampirkan yang lain ke blok elif juga diputuskan oleh garpu menggunakan fungsi make_solution yang sama , dan jika lagi dilampirkan, flag fin_else_flag dihidupkan segerayang menghentikan pemblokiran. Penggabungan

langsung elif dan lainnya dilakukan oleh fungsi elif_else (lihat di bawah). Di sini perlu diperhatikan bahwa saat membuat blok elif (dan juga ketika memasang yang lain), offset wall_offset digunakan untuk membangun blok secara keseluruhan dengan lancar.

Sekarang perhatikan fungsi elif_else .

<b>def elif_else(ee_string, offset_koeff, exp_list, var_list, sign, if_str, choice_list, fin_else_flag, prob_list):
    ee_str = ''
    #   else:  elif [..]:
    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'
    #   -None /  +
    prob_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:
            #  
            ee_str+=action_str_gen(choice_list,offset_koeff+3, prob_list)
        else:
            # None
            ee_str+=' '*(offset_koeff+3)+'None\n'
        return(ee_str, offset_koeff, fin_else_flag, prob_list)
    else:
        #   
        prob_list[6],prob_list[7],sol2 = make_solution(prob_list[6],prob_list[7])
        if sol2==0:
            #  
            ee_str+=action_str_gen(choice_list,offset_koeff+3, prob_list)
        #  if_gen
        if_str, offset_koeff,  fin_else_flag, prob_list = if_gen(exp_list, var_list, if_str, offset_koeff+3, fin_else_flag, prob_list)                 
        ee_str+=if_str
        return(ee_str, offset_koeff, fin_else_flag, prob_list)

Fungsi ini bertanggung jawab untuk pembentukan elif atau garis itu sendiri, serta untuk generasi aksi berikutnya atau blok rekursi setelah garis-garis ini. Itu juga mengambil variabel ee_string , yang berisi elif atau yang lain, dan membentuk string yang sesuai. Lalu ada garpu, di mana ditentukan apa yang akan terjadi selanjutnya: (blok tindakan atau Tidak ada), atau (blok tindakan atau blok tindakan + rekursi). Di dalam fork ini, ada divisi, masing-masing, menjadi dua sub-fork, dan dalam setiap kasus fungsi make_solution dipanggil dengan parameter yang sesuai untuk membuat keputusan.

Perlu dicatat bahwa ketika itu terjadi dalam kodeif sol!=0, ini berarti bahwa kami sengaja memberi keuntungan pada satu bagian kode daripada yang lain, karena jika sol! = 0, maka itu sama dengan -1, atau 1, dan oleh karena itu bagian kode lain akan dieksekusi lebih jarang (hanya ketika sol == 0). Ini digunakan, khususnya, dalam fungsi elif_else_block , di mana lebih menguntungkan bagi kita untuk membiarkan lebih banyak bentuk elif di blok, daripada memberikan probabilitas yang sama untuk elif dan lainnya. Atau, misalnya, dalam fungsi elif_else, kami memberikan keuntungan untuk opsi ketika blok tindakan atau Tidak ada yang terbentuk daripada apa yang terjadi untuk rekursi - jika tidak, cabang-cabang dapat tumbuh menjadi ukuran yang benar-benar tidak senonoh.

Kita hanya perlu mempertimbangkan fungsi yang bertanggung jawab untuk generasi ekspresi acak dalam kondisi dan blok tindakan. Seperti yang saya katakan di atas, pada tahap ini mereka tidak memainkan peran yang menentukan dan diperkenalkan di sini untuk secara umum menunjukkan seperti apa nantinya kode yang dihasilkan. Tetapi karena mereka digunakan dalam generator, kita akan melihatnya secara singkat.

Fungsi yang bertanggung jawab untuk menghasilkan blok aksi 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)

Semuanya cukup sederhana di sini: dari daftar bersarang choise_list, yang, seperti kita ingat, terdiri dari v ar_list (daftar variabel) dan exp_list (daftar ekspresi), fungsi ini terdiri dari satu atau lebih baris dari formulir ini: a = a + b atau b = b . Itu baik ekspresi ditugaskan ke variabel, atau variabel lain (termasuk dirinya sendiri). Fungsi rand secara acak memilih elemen dari daftar dan diperlukan di sini semata-mata agar tidak menghasilkan string yang mengerikan.

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

Fungsi generasi ekspresi if_sub untuk kondisi terlihat lebih besar.

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_str[0:-4]
        elif sub_str[-2]=='r':
             sub_str=sub_str[0:-3]
        else:
            sub_str=sub_str[0:-1]
    elif sub_str[-1]=='d':
         sub_str=sub_str[0:-3]
    elif sub_str[-1]=='r':
         sub_str=sub_str[0:-2]
    else:
         None
    if flag == 1:
        sub_str+=')'
        return(sub_str)
    else:
        return(sub_str)

Ini menghasilkan ekspresi berdasarkan tipe: ((a)> = (ba)) atau ((a)> = (a)) atau ((b) <= (b)) . Pada saat yang sama, kedua sisi kiri dan kanan dapat memiliki berbagai opsi dan berdiri sebagai variabel yang terpisah, serta ekspresi atau grup mereka. Operator logis atau dan dan juga digunakan di sini , yang dipilih untuk kenyamanan menggunakan fungsi or_and_exp .

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('')

Sisa fungsi if_sub memotong ekor ekstra dari ekspresi dan menambahkan, bila perlu, kurung tutup - untuk mempertimbangkan tarian ini dengan rebana di sini, saya pikir, tidak layak.

Yah, itu saja. Anda dapat memulai generator, misalnya, seperti ini:

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, 0,0, prob_list)
     try:
         exec(compile(if_str,'gen','exec'))
         print(if_str)
         input()
         
     except ZeroDivisionError:
         None
     except:
         print('error')
         print(if_str)
         input()

Pertama, input, termasuk prob_list dengan probabilitas , kemudian dalam infinite loop, memanggil fungsi utama if_gen dan memulai string yang dihasilkan untuk dieksekusi. Perlu memproses secara terpisah ZeroDivisionError, karena pembagian dengan nol dengan konstruksi ekspresi acak seperti itu sangat umum. Setelah peluncuran, cukup tekan Enter agar generasi berikutnya muncul. Paling sering mereka akan sangat sederhana, tetapi sering bercabang dan bahkan sangat bercabang. Nah, impor acak di awal juga akan lebih baik untuk menyisipkan;) Bagi mereka yang tidak ingin melihat mengumpulkan semuanya dengan tangan, Anda dapat mengunduh file dari Github (file if_gen.py).

Sebagai kesimpulan, saya ingin mengatakan bahwa kode yang saya sajikan diuji pada ratusan ribu generasi tanpa kesalahan, sementara itu menunjukkan seluruh palet skema if-elif-else yang akhirnya ingin saya lihat. Suatu kali, secara tidak sengaja, saya memberi dalam satu bagian kode kemungkinan rekursi yang terlalu tinggi dan saya mendapat 52.000 (!) Baris generasi dan pada saat yang sama itu berfungsi (walaupun comp ditangguhkan selama 30 detik). Ini juga menunjukkan keandalan algoritma.

Mungkin, mungkin untuk menulis lebih ringkas di suatu tempat, untuk mengoptimalkan suatu tempat, untuk menyusun fungsi utama dengan cara lain, tetapi yang utama adalah bahwa kode ini bekerja dan menghasilkan sekitar 250 generasi per detik, yang, menurut saya, cukup dapat diterima.

Saya tidak pernah menganggap kode ini swasembada - itu hanya modul dari organisme digital masa depan dan ditulis untuk tujuan penelitian, sehingga hampir tidak memiliki aplikasi praktis. Pada saat yang sama, saya tidak bertanggung jawab atas konsekuensi bagi siapa pun yang menggunakan kode di atas, dan saya mendorong semua orang untuk memotong roti dengan pisau untuk memotong roti, dan bukan sesuatu yang lain.

Pada artikel selanjutnya, kita akan mempertimbangkan modul kedua, yang akan bertanggung jawab untuk pembentukan pengalaman secara acak. Topik ini berjanji akan jauh lebih menarik daripada generator if, dan saya pasti akan memposting hasilnya segera setelah saya memilikinya.

All Articles