Tentang persaingan ketat (menggunakan pemrograman reaktif sebagai contoh)

1. Perkenalan


Persaingan untuk pikiran, suasana hati dan aspirasi pemrogram, menurut saya, merupakan tren modern dalam pengembangan pemrograman. Ketika hampir tidak ada yang diusulkan, meskipun di bawah slogan perjuangan untuk itu. Sangat, sangat sulit untuk dikenali dalam himpitan paradigma perangkat lunak sesuatu yang baru, yang pada kenyataannya sering ternyata cukup terkenal dan, kadang-kadang, hanya ketinggalan jaman. Semuanya “hanyut” oleh kesenangan terminologis, analisis verbose, dan contoh multiline dalam banyak bahasa pemrograman. Pada saat yang sama, permintaan untuk membuka dan / atau mempertimbangkan latar belakang solusi, esensi dari inovasi dihindarkan dengan keras kepala, upaya untuk mengetahui berapa banyak ini dibutuhkan dan apa yang akan memberikan pada akhirnya, yang secara kualitatif membedakan inovasi dari pendekatan yang sudah dikenal dan alat pemrograman, digagalkan pada tahap awal.

Saya muncul di Habré, seperti yang benar-benar diperhatikan dalam salah satu diskusi, setelah pembekuan tertentu. Saya bahkan tidak keberatan. Setidaknya, kesan itu, ternyata, hanya itu. Karena itu, saya setuju, saya akui, meskipun, kalau itu salah saya, itu hanya sebagian. Saya akui, saya hidup dengan ide-ide tentang pemrograman paralel, dibentuk pada tahun 80-an abad terakhir. Jaman dahulu? Mungkin. Tapi katakan padaku apa yang baru, yang tentangnya ilmu pemrograman [paralel] belum akan diketahui saat itu (lihat detail [1]). Pada saat itu, program paralel dibagi menjadi dua kelas - paralel-serial dan asinkron. Jika yang pertama sudah dianggap kuno, maka yang terakhir - maju dan benar-benar paralel. Di antara yang terakhir, pemrograman dengan kontrol acara (atau hanya pemrograman acara), kontrol aliran, dan pemrograman dinamis dipilih.Itu semua secara umum. Rincian lebih lanjut sudah.

Dan apa yang ditawarkan pemrograman saat ini selain apa yang sudah diketahui setidaknya 40 tahun yang lalu? Dalam "tampilan beku" saya - tidak ada. Coroutine, sekarang ternyata, sekarang disebut coroutine atau bahkan goroutine, istilah konkurensi dan kompetisi masuk ke dalam keadaan pingsan, tampaknya, bukan hanya penerjemah. Dan tidak ada contoh seperti itu. Misalnya, apa perbedaan antara pemrograman reaktif (RP) dan pemrograman acara atau streaming? Manakah dari kategori dan / atau klasifikasi yang dikenal yang termasuk dalam kategori ini? Sepertinya tidak ada yang tertarik pada ini, dan tidak ada yang bisa mengklarifikasi ini. Atau bisakah Anda mengklasifikasikan sekarang berdasarkan nama? Kemudian, memang, coroutine dan coroutine adalah hal yang berbeda, dan pemrograman paralel diharuskan berbeda dari yang kompetitif. Bagaimana dengan mesin negara? Teknik keajaiban macam apa ini?

"Spaghetti" di kepala muncul dari pelupaan teori di mana, ketika model baru diperkenalkan, itu dibandingkan dengan model yang sudah dikenal dan dipelajari dengan baik. Apakah ini akan dilakukan dengan baik, tetapi setidaknya Anda bisa mengetahuinya, karena prosesnya diformalkan. Tetapi bagaimana cara mengatasinya jika Anda memberikan nama panggilan baru pada coroutine dan kemudian memilih "kode kap mesin" secara bersamaan dalam lima bahasa, mengevaluasi juga prospek migrasi ke stream. Dan ini hanya coroutine, yang, sejujurnya, harus sudah dilupakan karena sifat dasar mereka dan penggunaannya yang kecil (ini, tentu saja, tentang pengalaman saya).

2. Pemrograman reaktif dan segalanya, segalanya, segalanya


Kami tidak akan menetapkan tujuan memahami konsep "pemrograman reaktif", meskipun kami akan mengambil "contoh reaktif" sebagai dasar untuk diskusi lebih lanjut. Model formalnya akan dibuat berdasarkan model formalnya yang terkenal. Dan ini, saya harap, akan memungkinkan kita untuk secara jelas, akurat, secara terperinci memahami interpretasi dan pengoperasian program asli. Tetapi berapa banyak model yang dibuat dan implementasinya akan "reaktif" tergantung pada para apologis dari jenis pemrograman ini untuk memutuskan. Saat ini, sudah cukup untuk sekarang bahwa model baru harus menerapkan / memodelkan semua nuansa contoh asli. Jika ada sesuatu yang tidak diperhitungkan, maka saya berharap ada orang yang mengoreksi saya.

Jadi, dalam [2], contoh program reaktif dipertimbangkan, kode yang ditunjukkan pada Listing 1.

Daftar 1. Kode program reaktif
1. 1 = 2 
2. 2 = 3 
3. 3 = 1 + 2 
4.  1, 2, 3 
5. 1 = 4 
6.  1, 2, 3


Dalam dunia pemrograman reaktif, hasil kerjanya akan berbeda dari hasil program reguler dengan jenis yang sama. Ini saja buruk, jika tidak dikatakan jelek, karena Hasil program harus jelas dan tidak tergantung pada implementasi. Tapi yang lain lebih membingungkan. Pertama, dalam penampilan hampir tidak mungkin untuk membedakan kode serupa biasa dari yang reaktif. Kedua, tampaknya, penulis sendiri tidak sepenuhnya yakin dengan pekerjaan program reaktif, berbicara tentang hasil "kemungkinan besar". Dan ketiga, hasil manakah yang dianggap benar?

Ambiguitas dalam penafsiran kode tersebut telah menyebabkan fakta bahwa tidak mungkin untuk "memotong" kode itu. Tetapi kemudian, seperti yang sering terjadi, semuanya ternyata jauh lebih sederhana dari yang diperkirakan. Gambar 1 menunjukkan dua diagram struktural yang, semoga, sesuai dengan struktur dan menjelaskan operasi contoh. Di diagram atas, blok X1 dan X2 mengatur entri data, memberi sinyal blok X3 tentang perubahannya. Yang terakhir melakukan penjumlahan dan memungkinkan blok Pr untuk mencetak nilai saat ini dari variabel. Setelah dicetak, blok Pr memberi sinyal ke blok X3, apalagi, kepadanya dan hanya kepadanya bahwa ia siap mencetak nilai baru.

Ara. 1. Dua model struktural dari contoh
image

Skema kedua, dibandingkan dengan yang pertama, cukup mendasar. Sebagai bagian dari satu blok, ia memasukkan data dan mengimplementasikan secara berurutan: 1) menghitung jumlah data input dan 2) mencetaknya. Pengisian internal blok pada tingkat presentasi ini tidak diungkapkan. Meskipun dapat dikatakan bahwa pada tingkat struktural itu bisa menjadi "kotak hitam termasuk skema empat blok. Tapi tetap saja, perangkat [algoritmik] -nya seharusnya berbeda.

Komentar. Pendekatan terhadap program sebagai kotak hitam pada dasarnya mencerminkan sikap pengguna terhadapnya. Yang terakhir ini tidak tertarik pada implementasinya, tetapi pada hasil pekerjaan. Apakah itu program reaktif, program acara, atau yang lainnya, tetapi hasilnya sesuai dengan teori algoritma harus jelas dan dapat diprediksi.

Dalam gbr. 2 menyajikan model algoritmik yang menjelaskan secara detail struktur internal [algoritmik] dari blok sirkuit. Model atas diwakili oleh jaringan automata, di mana masing-masing automata adalah model algoritmik dari blok yang terpisah. Sambungan antara automata yang ditunjukkan oleh busur putus-putus sesuai dengan koneksi rangkaian. Model otomat tunggal menjelaskan algoritma operasi diagram blok yang terdiri dari satu blok (lihat blok Pr terpisah pada Gambar. 1).

Ara. 2. Model algoritma untuk skema struktural
image

Automata X1 dan X2 (nama-nama automata dan blok bertepatan dengan nama-nama variabel mereka), mendeteksi perubahan dan, jika otomat X3 siap untuk melakukan operasi penambahan (dalam keadaan "s0"), masuk ke negara "s1", mengingat nilai variabel saat ini. Mesin X3, setelah menerima izin untuk memasuki status "s1", melakukan operasi tambahan dan, jika perlu, menunggu penyelesaian pencetakan variabel. “Mesin cetak“ Pr, setelah selesai mencetak, kembali ke status awal “p0”, di mana ia menunggu perintah berikutnya. Perhatikan bahwa statusnya "p1" memulai rantai transisi mundur - robot X3 ke keadaan "s0", dan X1 dan X2 ke keadaan "s0". Setelah itu, analisis data input, kemudian penjumlahan dan pencetakan berikutnya diulang.

Dibandingkan dengan jaringan otomat, algoritma otomat Pr terpisah cukup sederhana, tetapi, kami mencatat, ia melakukan pekerjaan yang sama dan bahkan mungkin lebih cepat. Predikatnya mengungkapkan perubahan dalam variabel. Jika ini terjadi, maka transisi ke status "p1" dilakukan dengan dimulainya tindakan y1 (lihat Gambar. 2), yang merangkum nilai saat ini dari variabel, sambil mengingatnya. Kemudian, pada transisi tanpa syarat dari keadaan "p1" ke keadaan "p0", tindakan y2 mencetak variabel. Setelah itu, proses kembali ke analisis data input. Kode implementasi untuk model terbaru ditunjukkan pada Listing 2.

Daftar 2. Implementasi Protokol
#include "lfsaappl.h"
#include "fsynch.h"
extern LArc TBL_PlusX3[];
class FPlusX3 : public LFsaAppl
{
public:
    LFsaAppl* Create(CVarFSA *pCVF) { Q_UNUSED(pCVF)return new FPlusX3(nameFsa, pCVarFsaLibrary); }
    bool FCreationOfLinksForVariables();

    FPlusX3(string strNam, CVarFsaLibrary *pCVFL): LFsaAppl(TBL_PlusX3, strNam, nullptr, pCVFL) { }

    CVar *pVarY;        		// 
    CVar *pVarX1;        		// 
    CVar *pVarX2;        		// 
    CVar *pVarX3;        		// 
    CVar *pVarStrNameX1;		//   X1
    CVar *pVarStrNameX2;		//   X2
    CVar *pVarStrNameX3;		//   X3
protected:
    int x1(); int x2();
    int x12() { return pVarX1 != nullptr && pVarX2 && pVarX3; };
    void y1();
    void y12() { FInit(); };
    double dSaveX1{0};
    double dSaveX2{0};
};

#include "stdafx.h"
#include "fplusx3.h"

LArc TBL_PlusX3[] = {
    LArc("st",		"st","^x12","y12"), 		//
    LArc("st",		"p0","x12",	"--"),			//
    LArc("p0",		"p1","x1",  "y1"),			//
    LArc("p0",		"p1","x2",  "y1"),			//
    LArc("p1",		"p0","--",  "--"),			//
    LArc()
};

// creating local variables and initialization of pointers
bool FPlusX3::FCreationOfLinksForVariables() {
// creating local variables
    pVarY = CreateLocVar("strY", CLocVar::vtString, "print of output string");			//  
    pVarX1 = CreateLocVar("dX1", CLocVar::vtDouble, "");			//  
    pVarX2 = CreateLocVar("dX2", CLocVar::vtDouble, "");			//  
    pVarX3 = CreateLocVar("dX3", CLocVar::vtDouble, "");			//  
    pVarStrNameX1 = CreateLocVar("strNameX1", CLocVar::vtString, "");			//   
    pVarStrNameX2 = CreateLocVar("strNameX2", CLocVar::vtString, "");			//   
    pVarStrNameX3 = CreateLocVar("strNameX3", CLocVar::vtString, "");			//   
// initialization of pointers
    string str;
    str = pVarStrNameX1->strGetDataSrc();
    if (str != "") { pVarX1 = pTAppCore->GetAddressVar(str.c_str(), this);	}
    str = pVarStrNameX2->strGetDataSrc();
    if (str != "") { pVarX2 = pTAppCore->GetAddressVar(str.c_str(), this);	}
    str = pVarStrNameX3->strGetDataSrc();
    if (str != "") { pVarX3 = pTAppCore->GetAddressVar(str.c_str(), this);	}
    return true;
}

int FPlusX3::x1() { return pVarX1->GetDataSrc() != dSaveX1; }
int FPlusX3::x2() { return pVarX2->GetDataSrc() != dSaveX2; }

void FPlusX3::y1() {
// X3 = X1 + X2
    double dX1 = pVarX1->GetDataSrc(); double dX2 = pVarX2->GetDataSrc();
    double dX3 = dX1 + dX2;
    pVarX3->SetDataSrc(this, dX3);
    dSaveX1 = dX1; dSaveX2 = dX2;
//  1, 2, 3
    QString strX1; strX1.setNum(dX1); QString strX2; strX2.setNum(dX2);
    QString strX3; strX3.setNum(dX3);
    QString qstr = "X1=" + strX1 + ", X2=" + strX2 + ", X3=" + strX3;
    pVarY->SetDataSrc(nullptr, qstr.toStdString(), nullptr);
}


Jumlah kode jelas jauh lebih besar dari contoh aslinya. Tapi, perhatikan, bukan kode tunggal. Solusi baru menghilangkan semua masalah fungsi, tidak memungkinkan untuk mengalami fantasi dalam penafsiran program. Sebuah contoh yang terlihat kompak dan elegan, tetapi yang dapat Anda katakan “kemungkinan besar”, tidak menyebabkan, katakanlah, emosi positif dan keinginan untuk bekerja dengannya. Perlu juga dicatat bahwa perlu membandingkan sebenarnya dengan aksi otomat y1.

Sisa kode terkait dengan persyaratan "lingkungan otomatis", yang, saya perhatikan, tidak diucapkan dalam kode sumber. Jadi, metode FCreationOfLinksForVariables dari kelas automaton dasar LFsaApplmembuat variabel lokal untuk mesin dan tautan ke mereka ketika di tingkat VKPA simbolik nama lingkungan dari variabel lingkungan lain yang terkait dengannya ditunjukkan. Pertama kali dimulai ketika membuat otomat, dan kemudian dalam kerangka metode FInit (lihat langkah y12), karena tidak semua tautan diketahui saat membuat objek. Mesin akan berada dalam status "st" sampai semua tautan yang diperlukan yang diperiksa oleh predikat x12 diinisialisasi. Referensi ke variabel, jika diberi namanya, mengembalikan metode GetAddressVar.

Untuk menghapus kemungkinan pertanyaan, kami sajikan kode jaringan otomat. Itu ditunjukkan pada Listing 3 dan termasuk kode untuk tiga kelas otomat. Atas dasar itulah banyak objek dibuat yang sesuai dengan diagram struktural jaringan yang ditunjukkan pada Gambar. 1. Perhatikan bahwa objek X1 dan X2 diturunkan dari kelas umum FSynch.

Daftar 3. Kelas jaringan otomatis
#include "lfsaappl.h"

extern LArc TBL_Synch[];
class FSynch : public LFsaAppl
{
public:
    double dGetData() { return pVarX->GetDataSrc(); };
    LFsaAppl* Create(CVarFSA *pCVF) { Q_UNUSED(pCVF)return new FSynch(nameFsa, pCVarFsaLibrary); }
    bool FCreationOfLinksForVariables();

    FSynch(string strNam, CVarFsaLibrary *pCVFL): LFsaAppl(TBL_Synch, strNam, nullptr, pCVFL) { }

    CVar *pVarX;			// 
    CVar *pVarStrNameX;		//   
    CVar *pVarStrNameObject;//  -
    LFsaAppl *pL {nullptr};
protected:
    int x1() { return pVarX->GetDataSrc() != dSaveX; }
    int x2() { return pL->FGetState() == "s1"; }
    int x12() { return pL != nullptr; };
    void y1() { dSaveX = pVarX->GetDataSrc(); }
    void y12() { FInit(); };
    double dSaveX{0};
};

#include "stdafx.h"
#include "fsynch.h"

LArc TBL_Synch[] = {
    LArc("st",		"st","^x12","y12"), 		//
    LArc("st",		"s0","x12",	"y1"),			//
    LArc("s0",		"s1","x1",  "y1"),			//
    LArc("s1",		"s0","x2",	"--"),			//
    LArc()
};

// creating local variables and initialization of pointers
bool FSynch::FCreationOfLinksForVariables() {
// creating local variables
    pVarX = CreateLocVar("x", CLocVar::vtDouble, " ");
    pVarStrNameX = CreateLocVar("strNameX1", CLocVar::vtString, "name of external input variable(x1)");			//   
    pVarStrNameObject = CreateLocVar("strNameObject", CLocVar::vtString, "name of function");                   //  
// initialization of pointers
    string str;
    if (pVarStrNameX) {
        str = pVarStrNameX->strGetDataSrc();
        if (str != "") { pVarX = pTAppCore->GetAddressVar(str.c_str(), this);	}
    }
    str = pVarStrNameObject->strGetDataSrc();
    if (str != "") { pL = FGetPtrFsaAppl(str);	}
    return true;
}

#include "lfsaappl.h"
#include "fsynch.h"

extern LArc TBL_X1X2X3[];
class FX1X2X3 : public LFsaAppl
{
public:
    double dGetData() { return pVarX3->GetDataSrc(); };
    LFsaAppl* Create(CVarFSA *pCVF) { Q_UNUSED(pCVF)return new FX1X2X3(nameFsa, pCVarFsaLibrary); }
    bool FCreationOfLinksForVariables();

    FX1X2X3(string strNam, CVarFsaLibrary *pCVFL): LFsaAppl(TBL_X1X2X3, strNam, nullptr, pCVFL) { }

    CVar *pVarX1{nullptr};			//
    CVar *pVarX2{nullptr};			//
    CVar *pVarX3{nullptr};			//
    CVar *pVarStrNameFX1;		//  X1
    CVar *pVarStrNameFX2;		//  X2
    CVar *pVarStrNameFPr;		//  Pr
    CVar *pVarStrNameX3;		//   
    FSynch *pLX1 {nullptr};
    FSynch *pLX2 {nullptr};
    LFsaAppl *pLPr {nullptr};
protected:
    int x1() { return pLX1->FGetState() == "s1"; }
    int x2() { return pLX2->FGetState() == "s1"; }
    int x3() { return pLPr->FGetState() == "p1"; }
    int x12() { return pLPr != nullptr && pLX1 && pLX2 && pVarX3; };
    void y1() { pVarX3->SetDataSrc(this, pLX1->dGetData() + pLX2->dGetData()); }
    void y12() { FInit(); };
};
#include "stdafx.h"
#include "fx1x2x3.h"

LArc TBL_X1X2X3[] = {
    LArc("st",		"st","^x12","y12"), 		//
    LArc("st",		"s0","x12",	"--"),			//
    LArc("s0",		"s1","x1",  "y1"),			//
    LArc("s0",		"s1","x2",  "y1"),			//
    LArc("s1",		"s0","x3",	"--"),			//
    LArc()
};
// creating local variables and initialization of pointers
bool FX1X2X3::FCreationOfLinksForVariables() {
// creating local variables
    pVarX3 = CreateLocVar("x", CLocVar::vtDouble, " ");
    pVarStrNameFX1 = CreateLocVar("strNameFX1", CLocVar::vtString, "");
    pVarStrNameFX2 = CreateLocVar("strNameFX2", CLocVar::vtString, "");
    pVarStrNameFPr = CreateLocVar("strNameFPr", CLocVar::vtString, "");
    pVarStrNameX3 = CreateLocVar("strNameX3", CLocVar::vtString, "");
// initialization of pointers
    string str; str = pVarStrNameFX1->strGetDataSrc();
    if (str != "") { pLX1 = (FSynch*)FGetPtrFsaAppl(str);	}
    str = pVarStrNameFX2->strGetDataSrc();
    if (str != "") { pLX2 = (FSynch*)FGetPtrFsaAppl(str);	}
    str = pVarStrNameFPr->strGetDataSrc();
    if (str != "") { pLPr = FGetPtrFsaAppl(str);	}
    return true;
}
#include "lfsaappl.h"
#include "fsynch.h"

extern LArc TBL_Print[];
class FX1X2X3;
class FPrint : public LFsaAppl
{
public:
    LFsaAppl* Create(CVarFSA *pCVF) { Q_UNUSED(pCVF)return new FPrint(nameFsa, pCVarFsaLibrary); }
    bool FCreationOfLinksForVariables();

    FPrint(string strNam, CVarFsaLibrary *pCVFL): LFsaAppl(TBL_Print, strNam, nullptr, pCVFL) { }

    CVar *pVarY;        		// 
    CVar *pVarStrNameFX1;		//    X1
    CVar *pVarStrNameFX2;		//    X2
    CVar *pVarStrNameFX3;		//    X3
    FSynch *pLX1 {nullptr};     //    X1
    FSynch *pLX2 {nullptr};     //    X2
    FX1X2X3 *pLX3 {nullptr};    //    X3
protected:
    int x1();
    int x12() { return pLX3 != nullptr && pLX1 && pLX2 && pLX3; };
    void y1();
    void y12() { FInit(); };
};
#include "stdafx.h"
#include "fprint.h"
#include "fx1x2x3.h"

LArc TBL_Print[] = {
    LArc("st",		"st","^x12","y12"), 		//
    LArc("st",		"p0","x12",	"--"),			//
    LArc("p0",		"p1","x1",  "y1"),			//
    LArc("p1",		"p0","--",	"--"),			//
    LArc()
};
// creating local variables and initialization of pointers
bool FPrint::FCreationOfLinksForVariables() {
// creating local variables
    pVarY = CreateLocVar("strY", CLocVar::vtString, "print of output string");			//  
    pVarStrNameFX1 = CreateLocVar("strNameFX1", CLocVar::vtString, "name of external input object(x1)");			//   
    pVarStrNameFX2 = CreateLocVar("strNameFX2", CLocVar::vtString, "name of external input object(x2)");			//   
    pVarStrNameFX3 = CreateLocVar("strNameFX3", CLocVar::vtString, "name of external input object(pr)");			//   
// initialization of pointers
    string str;
    str = pVarStrNameFX1->strGetDataSrc();
    if (str != "") { pLX1 = (FSynch*)FGetPtrFsaAppl(str);	}
    str = pVarStrNameFX2->strGetDataSrc();
    if (str != "") { pLX2 = (FSynch*)FGetPtrFsaAppl(str);	}
    str = pVarStrNameFX3->strGetDataSrc();
    if (str != "") { pLX3 = (FX1X2X3*)FGetPtrFsaAppl(str);	}
    return true;
}

int FPrint::x1() { return pLX3->FGetState() == "s1"; }

void FPrint::y1() {
    QString strX1; strX1.setNum(pLX1->dGetData());
    QString strX2; strX2.setNum(pLX2->dGetData());
    QString strX3; strX3.setNum(pLX3->dGetData());
    QString qstr = "X1=" + strX1 + ", X2=" + strX2 + ", X3=" + strX3;
    pVarY->SetDataSrc(nullptr, qstr.toStdString(), nullptr);
}


Kode ini berbeda dari Listing 1, seperti gambar pesawat terbang dari dokumentasi desainnya. Tapi, saya pikir, kita terutama programmer, dan, tidak ada pelanggaran yang akan diberitahukan kepada mereka, beberapa desainer. "Kode desain" kami harus mudah dipahami dan diinterpretasikan secara jelas sehingga "pesawat" kami tidak jatuh pada penerbangan pertama. Dan jika kemalangan seperti itu terjadi, dan dengan program ini terjadi lebih sering daripada dengan pesawat terbang, maka alasannya dapat ditemukan dengan mudah dan cepat.

Oleh karena itu, mempertimbangkan Listing 3, Anda perlu membayangkan bahwa jumlah kelas tidak secara langsung terkait dengan jumlah objek yang sesuai dalam program paralel. Kode tidak mencerminkan hubungan antara objek, tetapi berisi mekanisme yang membuatnya. Jadi, kelas FSynch berisi pointer pL ke objek bertipeLFsaAppl . Nama objek ini ditentukan oleh variabel lokal, yang dalam lingkungan VKPa akan sesuai dengan variabel automaton dengan nama strNameObject . Pointer diperlukan untuk menggunakan metode FGetState untuk memantau keadaan saat ini dari objek robot tipe FSynch (lihat kode predikat x2). Pointer yang mirip dengan objek, variabel untuk menentukan nama objek, dan predikat yang diperlukan untuk mengatur hubungan mengandung kelas lain.

Sekarang beberapa kata tentang "pembangunan" program paralel di lingkungan VKPA. Itu dibuat selama memuat konfigurasi program. Dalam hal ini, objek pertama dibuat berdasarkan kelas dari pustaka dinamis tematik tipe otomat (setnya ditentukan oleh konfigurasi aplikasi / program). Objek yang dibuat diidentifikasi dengan namanya (sebut saja variabel otomatis) Kemudian, nilai yang diperlukan ditulis ke variabel lokal automata. Dalam kasus kami, variabel dengan tipe string diatur ke nama variabel objek lain dan / atau nama objek. Dengan cara ini, koneksi antara objek program otomat paralel dibangun (lihat Gambar. 1). Selanjutnya, mengubah nilai-nilai variabel input (menggunakan dialog kontrol objek individu atau dialog standar / dialog lingkungan untuk menetapkan nilai-nilai untuk variabel lingkungan), kami memperbaiki hasilnya. Itu dapat dilihat menggunakan dialog lingkungan standar untuk menampilkan nilai-nilai variabel.

3. Untuk analisis program paralel


Tentang berfungsinya program paralel, kecuali jika paralelnya cukup sederhana, sangat, sangat sulit untuk mengatakan sesuatu yang konkret. Jaringan automata yang dipertimbangkan tidak terkecuali. Selanjutnya, kita akan melihat ini, memahami apa yang bisa diharapkan darinya.

Otomat yang dihasilkan dan jaringan yang dibangun ditunjukkan pada Gambar. 3. Dari jaringan pada Gambar. 2, selain mengganti nama elemen-elemennya - automata, sinyal input dan output, itu dibedakan dengan tidak adanya "mesin cetak" variabel. Yang terakhir tidak penting untuk operasi jaringan, dan penggantian nama memungkinkan Anda untuk menggunakan operasi komposisi untuk membangun otomat yang dihasilkan. Selain itu, untuk membuat nama yang lebih pendek, pengkodean diperkenalkan ketika, misalnya, status "a0" dari otomat A diwakili oleh simbol "0", dan "a1" dengan simbol "1". Begitu pula untuk mesin lain. Dalam hal ini, status komponen jaringan, misalnya, "a1b0c1", diberi nama "101". Demikian pula, nama-nama dibentuk untuk semua status komponen jaringan, yang jumlahnya ditentukan oleh produk status komponen automata.

Ara. 3. Otomat jaringan yang dihasilkan
image

Otomat yang dihasilkan dapat, tentu saja, dihitung dengan cara formal murni, tetapi untuk ini kita memerlukan "kalkulator" yang tepat. Tetapi jika tidak, maka Anda dapat menggunakan algoritma intuitif yang cukup sederhana. Dalam kerangka kerjanya, satu atau beberapa komponen lain dari jaringan direkam dan kemudian, menyortir semua situasi input yang mungkin, status komponen target ditentukan oleh "pegangan". Jadi, setelah memperbaiki keadaan "000" sesuai dengan keadaan saat ini dari komponen automata - "a0", "b0", "c0", transisi untuk konjungsi dari variabel input ^ x1 ^ x2, ^ x1x2, x1 ^ x2, x1x2 ditentukan. Kita masing-masing mendapatkan transisi dalam menyatakan "a0b0c0", "a0b1c0", "a1b0c0", "a1b1c0", yang ditandai "000", "010", "010", "100" dan "110" pada mesin yang dihasilkan. loopyang tidak dimuat dengan tindakan dapat dikecualikan dari grafik.

Apa yang kita miliki "di residu kering". Kami mencapai hal utama - kami menerima otomat yang dihasilkan, yang secara akurat menggambarkan operasi jaringan. Kami menemukan bahwa dari delapan kemungkinan status jaringan, satu tidak dapat diakses (terisolasi) - status "001". Ini berarti bahwa operasi penjumlahan dalam keadaan apapun tidak akan dipicu untuk variabel input yang belum mengubah nilai saat ini.

Yang mengganggu, meskipun pengujian tidak mengungkapkan kesalahan. Pada grafik automaton yang dihasilkan, transisi yang bertentangan dalam tindakan keluaran ditemukan. Mereka ditandai dengan kombinasi tindakan y1y3 dan y2y3. Tindakan y1 dan y2 dipicu ketika input data berubah, dan kemudian tindakan lain y3 menghitung jumlah variabel secara paralel dengan mereka. Nilai apa yang akan beroperasi pada - lama atau hanya diubah oleh yang baru? Untuk menghilangkan ambiguitas, Anda cukup mengubah tindakan y3 dan y4. Dalam hal ini, kode mereka adalah sebagai berikut: X3 = X1Sav + X2Sav dan cetak (X1Sav, X2Sav, X3).

Begitu. Konstruksi otomat yang dihasilkan mengungkapkan masalah yang jelas dalam model paralel yang dibuat. Apakah mereka muncul dalam program reaktif adalah pertanyaan. Semuanya akan, tampaknya, tergantung pada pendekatan implementasi paralelisme dalam paradigma reaktif. Bagaimanapun, ketergantungan seperti itu harus diperhitungkan dan entah bagaimana dihilangkan. Dalam kasus jaringan otomatis, lebih mudah meninggalkan versi yang diubah daripada mencoba mengubah jaringan. Tidak apa-apa jika data "lama" yang memulai operasi jaringan dicetak pertama, dan kemudian data saat ini dicetak berikutnya.

4. Kesimpulan


Setiap solusi yang dipertimbangkan memiliki pro dan kontra. Yang pertama sangat sederhana, jaringan lebih rumit, dan dibuat berdasarkan satu mesin, ia akan mulai menganalisis data input hanya setelah visualisasi. Karena paralelismenya, jaringan otomatis yang sama akan memulai analisis data input sebelum akhir prosedur pencetakan. Dan jika waktu visualisasi panjang, tetapi ini akan menjadi kasus terhadap operasi penjumlahan, maka jaringan akan lebih cepat dari sudut pandang kontrol input. Itu penilaian berdasarkan estimasi jumlah kode dalam kasus program paralel tidak selalu objektif. Dalam istilah yang lebih sederhana, jaringan itu paralel, solusi satu komponen sebagian besar berurutan (predikat dan tindakannya paralel). Dan kami, pertama-tama, berbicara tentang program paralel.

Model jaringan juga merupakan contoh dari solusi yang fleksibel. Pertama, komponen dapat dirancang secara independen satu sama lain. Kedua, komponen apa pun dapat diganti oleh yang lain. Dan ketiga, komponen jaringan apa pun dapat menjadi elemen pustaka proses otomatis dan digunakan dalam solusi jaringan lain. Dan ini hanyalah manfaat paling nyata dari solusi paralel.

Tetapi kembali ke pemrograman reaktif. Apakah RP menganggap semua pernyataan program pada awalnya paralel? Kita hanya dapat berasumsi bahwa tanpa ini, sulit untuk berbicara tentang paradigma pemrograman "yang berorientasi pada aliran data dan penyebaran perubahan" (lihat definisi pemrograman reaktif dalam [3]). Tapi apa bedanya pemrograman dengan kontrol streaming (untuk lebih jelasnya lihat [1])? Jadi kita kembali ke tempat kita mulai: bagaimana mengklasifikasikan pemrograman reaktif dalam kerangka klasifikasi terkenal? Dan, jika RP adalah sesuatu yang pemrograman khusus, lalu apa bedanya dengan paradigma pemrograman yang dikenal?

Nah, tentang teorinya. Tanpa itu, analisis algoritma paralel tidak hanya akan menjadi sulit - tidak mungkin. Proses analisis kadang-kadang mengungkapkan masalah yang, bahkan dengan tampilan yang cermat dan penuh perhatian pada program, seperti, secara kebetulan, pada "dokumen desain", tidak mungkin untuk ditebak. Bagaimanapun, saya mendukung fakta bahwa pesawat, baik dalam arti kiasan maupun dalam arti lain, tidak jatuh. Ini saya dengan fakta bahwa, tentu saja, Anda perlu berjuang untuk kesederhanaan dan keanggunan bentuk, tetapi tanpa kehilangan kualitas. Kami, programmer, tidak hanya “menggambar” program, tetapi sering mengontrol apa yang tersembunyi di sana, termasuk dengan pesawat terbang!

Ya, saya hampir lupa. Saya akan mengklasifikasikan pemrograman otomatis (AP) sebagai pemrograman dengan kontrol dinamis. Adapun asynchrony - saya bertaruh. Mengingat bahwa dasar dari model kontrol AP adalah jaringan dalam satu waktu, yaitu jaringan automata sinkron, maka itu sinkron. Tetapi karena lingkungan VKPa juga mengimplementasikan banyak jaringan melalui konsep "dunia otomat," itu sepenuhnya tidak sinkron. Secara umum, saya menentang kerangka klasifikasi yang sangat kaku, tetapi tidak untuk anarki. Dalam hal ini, dalam VKPa, saya berharap kompromi tertentu telah dicapai antara kekakuan pemrograman paralel-serial dan anarkisme asinkron tertentu. Mengingat fakta bahwa pemrograman otomatis juga mencakup kelas program acara (lihat [4]), dan program streaming mudah dimodelkan di dalamnya,pemrograman apa yang masih bisa Anda impikan? Pasti - bagi saya.

literatur
1. /.. , .. , .. , .. ; . .. . – .: , 1983. – 240.
2. . [ ], : habr.com/ru/post/486632 . . . ( 07.02.2020).
3. . . [ ], : ru.wikipedia.org/wiki/_ . . . ( 07.02.2020).
4. — ? [ ], : habr.com/ru/post/483610 . . . ( 07.02.2020).

Source: https://habr.com/ru/post/undefined/


All Articles