"... mereka yang tidak menolak untuk menatap seorang amatir yang secara terbuka menyalahkan orang bodoh, biarkan mereka mengamati bagaimana aku membuktikan bahwa Java dan Visual Basic dipisahkan oleh kembar sejak lahir, dan C ++ mereka bahkan bukan kerabat jauh."Bruce McKinney "Die Hard Basic Basic"Pendahuluan
Ketertarikan yang konstan dalam pendekatan pemrograman fungsional saat ini mengarah pada fakta bahwa bahasa pemrograman tradisional secara aktif memperoleh sarana fungsional. Dan meskipun bahasa fungsional murni masih tidak terlalu populer, fungsionalitasnya mapan dalam bahasa seperti C ++, Java, JavaScript, Python, dan lain-lain. Bahasa VBA telah populer di kalangan pengguna Microsoft Office yang cukup besar selama bertahun-tahun, namun bahasa ini praktis tidak mengandung alat fungsional.
Mari kita coba mengisi celah ini - saya mengusulkan implementasi yang lengkap (walaupun mungkin tidak sempurna) dari antarmuka fungsional yang diimplementasikan oleh VBA. Implementasi dapat berfungsi sebagai dasar untuk penyempurnaan dan peningkatan selanjutnya.
Masalah Argumen Fungsional
Masalah pertama yang akan kita hadapi di sepanjang jalan ini adalah masalah meneruskan argumen fungsional ke fungsi atau metode. Bahasa VBA tidak mengandung alat yang sesuai (operator AddressOf hanya berfungsi untuk menyampaikan alamat ke fungsi Windows API dan tidak sepenuhnya aman untuk digunakan). Hal yang sama dapat dikatakan tentang metode pemanggilan fungsi yang terkenal oleh pointer (G. Magdanurov Visual Basic dalam praktik St. Petersburg: "BHV Petersburg", 2008). Jangan mengambil risiko - kami hanya menggunakan fitur bahasa standar dan perpustakaan standar dalam implementasi.
Sayangnya, di sini PLO tidak banyak membantu. Untuk mentransfer objek fungsional ke dalam prosedur atau fungsi, VBA menawarkan peluang standar - untuk membungkus fungsionalitas yang diperlukan dengan shell objek (membuat objek, salah satu metode yang akan menjadi fungsionalitas yang diperlukan). Objek dapat dilewatkan sebagai parameter. Pendekatan ini bisa diterapkan, tetapi sangat berat - untuk setiap fungsionalitas yang diperlukan Anda harus membuat kelas Anda sendiri dan objek dari kelas ini.
Ada cara lain, yang jauh lebih sederhana dan tidak memerlukan penciptaan kelas yang terpisah untuk setiap fungsionalitas.
Misalkan Anda ingin meneruskan fungsi anonim ke prosedur proc tertentu yang menambah argumennya satu per satu. Fungsi ini dapat ditulis sebagai berikut:
x -> x+1
Notasi penugasan fungsi anonim ini sekarang hampir menjadi "standar de facto." Satu-satunya cara untuk meneruskan fungsi seperti itu ke parameter adalah dengan menggunakan representasi string:
r=proc(a,b,โx->x+1โ)
di sini a dan b adalah parameter biasa, dan parameter ketiga adalah fungsi yang tidak disebutkan namanya, yang sangat jelas dan sedikit berbeda dari entri dalam bahasa pemrograman populer.
Untuk menggunakan fungsi anonim yang didefinisikan dengan cara ini, Anda harus membawanya ke bentuk standar fungsi VBA. Ini melakukan prosedur utilitas berikut:
Private Function prepCode(Code As String) As String k% = InStr(Code, "->") parms$ = Trim$(Left$(Code, k% - 1)) body$ = Mid$(Code, k% + 2) If Left$(parms$, 1) <> "(" Then parms$ = "(" + parms$ + ")" If InStr(body$, "self") = 0 Then body$ = ";self=" & body$ & ";" body$ = Replace(body$, ";", vbCrLf) prepCode = "function self" & parms & vbCrLf & body & _ vbCrLf & "end function" End Function
Fungsi memilih daftar parameter dan badan perhitungan, dan kemudian membentuk fungsi yang disebut diri. Untuk kasus kami, fungsi mandiri akan memiliki bentuk berikut:
function self(x) self=x+1 End function
Jelas, sesuai dengan sintaks VBA, fungsi ini akan melakukan persis apa yang seharusnya dilakukan fungsi anonim - meningkatkan nilai argumennya dengan 1. Benar, fungsi ini bukan fungsi VBA, tetapi hanya baris yang berisi kode yang ditentukan. Untuk mengubah string menjadi suatu fungsi, Anda dapat menggunakan perpustakaan Microsoft standar "Msscript.ocx". Pustaka COM ini memungkinkan Anda untuk mengeksekusi kode VBA sewenang-wenang yang ditentukan dalam bentuk string. Untuk melakukan ini, lakukan hal berikut:
- Buat objek ScriptControl
- Panggil metode instalasi bahasa (VBScript);
- Panggil metode pemuatan fungsi;
- Panggil metode eval untuk melakukan panggilan.
Itu semua terlihat seperti ini:
Set locEv=new ScriptControl locEv.Language = "VBScript" locEv.AddCode prepCode(โx->x+1โ) r=locEv.eval(โself(5)โ)
Setelah mengeksekusi kode ini, nilai variabel r akan menjadi 6.
Tiga poin harus dibuat di sini:
- Badan fungsi anonim dapat berisi beberapa baris. Pernyataan individu dalam kasus ini diakhiri dengan tanda titik koma. Dari kode terakhir, karakter ";" dikecualikan. Badan multi-garis memungkinkan Anda untuk menerapkan fungsi yang sangat canggih dalam fungsi anonim;
- Fakta bahwa fungsi anonim "pada kenyataannya" memiliki nama "diri" memberikan bonus yang tidak terduga - fungsi anonim dapat bersifat rekursif.
- Karena objek ScriptControl mendukung dua bahasa - VBScript dan Jscript, fungsi anonim dapat (secara teoritis) ditulis dalam Jscript (mereka yang ingin dapat mencobanya).
Selanjutnya, model implementasi objek akan dijelaskan.
Model objek
Dasar dari model adalah objek dari dua jenis: Kontainer dan Generator. Objek Container adalah repositori dari array ukuran sewenang-wenang, objek Generator, seperti namanya, mengimplementasikan generator bentuk umum.
Kedua objek mengimplementasikan antarmuka aIter, yang dijelaskan secara lebih rinci di bawah ini. Antarmuka mencakup 19 fungsi:
Untuk objek generator, sejumlah metode tidak diterapkan secara langsung - Anda harus terlebih dahulu memilih sejumlah nilai tertentu dalam wadah. Ketika mencoba memanggil metode yang belum direalisasi untuk generator, kesalahan dihasilkan dengan kode 666. Selanjutnya, beberapa contoh penggunaan antarmuka yang dijelaskan akan dipertimbangkan.
Contohnya
Mencetak angka Fibonacci berturut-turut:
Sub Test_1() Dim fibGen As aIter Set fibGen = New Generator fibGen.Init Array(1, 0), "(c,p)->c+p" For i% = 1 To 50 Debug.Print fibGen.getNext() Next i% End Sub
Di sini generator dibuat dengan nilai awal 0 dan 1 dan fungsi pembangkit yang sesuai dengan deret Fibonacci. Selanjutnya, 50 angka pertama dicetak dalam satu lingkaran.
Peta dan filter:
Sub Test_2() Dim co As aIter Dim Z As aIter Dim w As aIter Set co = New Container co.Init frange(1, 100) Set Z = co.map("x -> 1.0/x"). _ take(20).filter(" x -> (x>0.3) or (x<=0.1)") iii% = 1 Do While Z.hasNext() Debug.Print iii%; " "; Z.getNext() iii% = iii% + 1 Loop End Sub
Wadah dibuat dan diinisialisasi dengan urutan numerik dari rentang 1 hingga 100. Selanjutnya, angka dengan peta digantikan oleh invers. Dari jumlah tersebut, dua puluh satu diambil. Kemudian populasi ini disaring dan angka yang lebih besar dari 0,3 atau kurang dari 0,1 dipilih dari itu. Hasilnya dikembalikan dalam wadah, komposisi yang dicetak.
Menggunakan konvolusi:
Sub Test_4() Dim co As aIter Set co = New Container co.Init frange(1, 100) v = co.reduce(0, "(acc,x)->acc+x") Debug.Print v v = co.reduce(1, "(acc,x)->acc*x") Debug.Print v End Sub
Di sini, dengan menggunakan konvolusi, jumlah dan produk angka dari 1 hingga 100 dipertimbangkan.
Sub Test_5() Dim co1 As aIter Dim co2 As aIter Dim co3 As aIter Set co1 = New Generator co1.Init Array(123456789), "x -> INT(x/10)" Set co2 = co1.takeWhile(100, "x -> x > 0") Set co3 = co2.map("x -> x mod 10") Debug.Print co3.maximun Debug.Print co3.minimum Debug.Print co3.summa Debug.Print co3.production End Sub
Dalam contoh ini, generator co1 dibangun, secara berurutan membagi angka asli dengan derajat 10. Kemudian quotients dipilih sampai nol muncul. Setelah itu, daftar pribadi yang dihasilkan ditampilkan oleh fungsi untuk mengambil sisa divisi dengan 10. Hasilnya adalah daftar angka-angka. Daftar ini dirangkum, ia menghitung maksimum, minimum dan produk.
Kesimpulan
Pendekatan yang diusulkan cukup bisa diterapkan dan dapat berhasil diterapkan untuk menyelesaikan tugas sehari-hari seorang programmer-VBA dalam gaya fungsional. Mengapa kita lebih buruk dari orang Jawa?
Unduh contoh
di siniSemoga berhasil !!!