Lisp dibumbui dengan Pascal atau bahasa pemrograman 8501

Beberapa waktu yang lalu (sekitar tiga tahun) saya memutuskan untuk membaca buku teks tentang Lisp. Tanpa tujuan khusus, hanya demi perkembangan umum dan kemampuan untuk mengejutkan lawan bicara dengan eksotis (begitu tampaknya, itu bahkan berhasil).

Tetapi setelah diperiksa lebih dekat, Lisp ternyata sangat kuat, fleksibel dan, anehnya, berguna dalam kehidupan sehari-hari. Semua tugas otomatisasi kecil dengan cepat dimigrasikan ke skrip di Lisp, dan ada juga peluang untuk otomatisasi yang lebih kompleks.

Perlu dicatat di sini bahwa dengan "kemampuan otomatisasi" yang saya maksudkan adalah situasi di mana total waktu untuk menulis dan men-debug suatu program kurang dari waktu yang dihabiskan secara manual untuk menyelesaikan tugas yang sama.

Paul Graham telah menulis lebih dari satu artikel dan bahkan sebuah buku tentang manfaat Lisp. Pada saat penulisan ini, Lisp berada di peringkat ke-33 di peringkat TOIBE (Delphi tiga kali mati daripada mati). Muncul pertanyaan: mengapa bahasanya sangat kecil jika nyaman? Sekitar dua tahun penggunaan memberi beberapa petunjuk alasannya.

Kekurangan


1. Struktur data bersama
Sebuah konsep yang memungkinkan Anda untuk mengoptimalkan program-program fungsional, tetapi penuh dengan kesalahan-kesalahan halus secara imperatif. Kemungkinan kerusakan yang tidak disengaja pada struktur data asing ketika suatu variabel dimodifikasi yang tidak memiliki koneksi yang terlihat dengan struktur mengharuskan programmer untuk terus memantau apa yang terjadi di belakang layar dan untuk mengetahui implementasi internal dari setiap fungsi yang digunakan (baik sistem dan pengguna). Hal yang paling menakjubkan adalah kemampuan untuk merusak tubuh dari suatu fungsi dengan memodifikasi nilai baliknya.

2. Kurangnya enkapsulasi
Meskipun konsep paket ada, itu tidak ada hubungannya dengan paket di Ada atau unit di Delphi. Kode apa pun dapat menambahkan apa pun ke paket apa pun (kecuali yang sistem). Kode apa pun dapat mengekstraksi apa pun dari paket apa pun menggunakan operator :: .

3. Singkatan serampangan
Apa perbedaan antara MAPCAN dan MAPCON? Mengapa di SETQ, huruf terakhir Q? Mengingat usia bahasanya, Anda dapat memahami alasan dari keadaan ini, tetapi saya ingin sedikit bahasa yang lebih bersih.

4. Multithreading
Kelemahan ini secara tidak langsung terkait dengan Lisp dan terutama menyangkut implementasi yang saya gunakan - SteelBank Common Lisp. Lisp umum tidak mendukung multithreading. Upaya untuk menggunakan implementasi yang disediakan oleh SBCL gagal.

Sangat disayangkan untuk menolak alat yang nyaman seperti itu, tetapi ketidakpuasan secara bertahap menumpuk.

Cari solusinya


Pertama, Anda bisa pergi ke Wikipedia pada halaman Lisp. Periksa bagian "Dialek". Baca pengantar singkat untuk masing-masing. Dan sadari bahwa rasa dan warna semua marker berbeda.
Jika Anda ingin melakukan sesuatu, Anda harus melakukannya sendiri
- Jean Baptiste Emmanuel Sorg
Mari kita coba membuat Lisp kita sendiri dengan menambahkan Ada, banyak Delphi dan setetes Oberon ke sana. Kami menyebutnya campuran yang dihasilkan Fox.

Konsep dasar


1. Tidak ada petunjuk
Dalam perang melawan MASALAH-1, semua operasi harus dilakukan dengan menyalin nilai-nilai. Berdasarkan jenis struktur data dalam kode atau saat mencetak, semua propertinya, koneksi eksternal dan internal harus sepenuhnya terlihat.

2. Tambahkan modul
Sebagai bagian dari perjuangan melawan masalah-2, kami mengimpor paket , dengan dan menggunakan pernyataan dari Ada. Dalam prosesnya, kami membuang skema impor / bayangan yang terlalu rumit untuk simbol Lisp.
(package - (  ) () ()) 

 (with -) ;  «-.lisya»    

 (use -) ;,       


3. Kurang singkatan
Karakter yang paling umum dan umum masih akan disingkat, tetapi sebagian besar yang paling jelas: const , var . Fungsi output yang diformat - FMT membutuhkan reduksi, karena sering ditemukan di dalam ekspresi. Elt - mengambil elemen - bocor dari Common Lisp dan mengambil root, meskipun tidak perlu menguranginya.

4. pengidentifikasi kasus-sensitif
Saya percaya bahwa bahasa (dan sistem file) yang benar {$ HOLYWAR +} harus case-insensitive {$ HOLYWAR-} agar tidak memutar otaknya sekali lagi.

5. Kemudahan penggunaan dengan tata letak keyboard Rusia
Sintaksis Lisi dengan segala cara yang mungkin menghindari penggunaan karakter yang tidak tersedia di salah satu tata letak. Tidak ada kawat gigi bujur sangkar atau keriting. Tidak #, ~, &, <,>, |. Saat membaca literal angka, koma dan titik dianggap sebagai pemisah desimal.

6. Alfabet yang diperluas
Salah satu hal baik tentang SBCL adalah kode UTF-8. Kemampuan untuk mendeklarasikan konstanta BEAR, VODKA dan BALALAYKA sangat menyederhanakan penulisan kode aplikasi. Kemampuan untuk memasukkan Ω, Ψ, dan Σ membuat rumus dalam kode lebih visual. Meskipun secara teori ada kemungkinan menggunakan karakter Unicode, sulit untuk menjamin kebenaran bekerja dengan mereka (agak malas daripada sulit). Kami membatasi diri pada bahasa Sirilik, Latin, dan Yunani.

7. Numerik literal
Ini adalah ekstensi bahasa yang paling berguna bagi saya.

 10_000 ;    10k ;       10 ;       10° 10pi 10deg 10 ;   10π ; pi     10+i10 ;   10+10 ;    1010deg ;          

Pilihan yang terakhir bagi saya tampaknya yang paling tidak estetika, tetapi itu yang paling populer.

8. Siklus
Siklus di Lisp adalah non-standar dan sangat berantakan. Sederhanakan ke set standar minimum.

 (for i 5 ;   i = 0..4 ) (for i 1..6 ;   i = 1..5 ) (for i  ;     ;      ) (for i (subseq  2) ;           ) 

Variabel loop tidak terlihat di luar.

 (while  ) 

9. GOTO
Bukan operator yang sangat diperlukan, tetapi tanpanya sulit untuk menunjukkan pengabaian aturan pemrograman struktural.

 (block : (goto :)) ;     

10. Penyatuan ruang lingkup
Ada dua jenis ruang lingkup di Lisp: TOPLEVEL dan lokal. Dengan demikian, ada dua cara berbeda untuk mendeklarasikan variabel.

 (defvar A 1) (let ((a 1)) …) 

Di Fox hanya ada satu metode yang digunakan baik di tingkat atas skrip dan di area lokal, termasuk paket.

 (var A 1) 

Jika Anda ingin membatasi ruang lingkup, gunakan operator

 (block (var A 1) (set A 2) (fmt nil A)) 

Tubuh loop terkandung dalam pernyataan BLOCK implisit (seperti tubuh fungsi / prosedur). Semua variabel yang dideklarasikan dalam loop dihancurkan pada akhir iterasi.

11. Karakter slot tunggal
Dalam Lisp, fungsi adalah objek khusus dan disimpan dalam slot simbol khusus. Satu karakter dapat secara bersamaan menyimpan variabel, fungsi, dan daftar properti. Dalam rubah, setiap karakter dikaitkan hanya dengan satu makna.

12. ELT yang nyaman
Akses khas ke elemen struktur kompleks di Lisp terlihat seperti ini

 (elt (slot-value (elt  1) '-2) 3) 

Fox mengimplementasikan operator ELT terpadu yang menyediakan akses ke elemen tipe komposit apa pun (daftar, string, catatan, array byte, tabel hash).

 (elt  1 \-2 3) 

Fungsi identik juga dapat diperoleh dengan makro di Lisp

 (defmacro field (object &rest f) "       . (field *object* 0 :keyword symbol \"string\")       .        plist.   ( )    .        ." (if f (symbol-macrolet ((f0 (elt f 0))(rest (subseq f 1))) (cond ((numberp f0) `(field (elt ,object ,f0) ,@rest)) ((keywordp f0) `(field (getf ,object ,f0) ,@rest)) ((stringp f0) `(field (cdr (assoc ,f0 ,object :test 'equal)) ,@rest)) ((and (listp f0) (= 2 (length f0))) `(field (,(car f0) ,(cadr f0) ,object) ,@rest)) ((symbolp f0) `(field (,f0 ,object) ,@rest)) (t `(error "   ")))) object)) 

13. Pembatasan mode transfer parameter subrutin
Setidaknya ada lima mode transfer parameter di Lisp: wajib, & opsional , & sisanya , & kunci , & keseluruhan dan kombinasi sewenang-wenang mereka diizinkan. Bahkan, sebagian besar kombinasi memberikan efek aneh.
Di Fox, hanya diperbolehkan menggunakan kombinasi parameter yang diperlukan dan salah satu mode berikut untuk dipilih : kunci ,: opsional ,: bendera ,: istirahat .

14. Multithreading
Untuk menyederhanakan penulisan program multithreaded sepenuhnya, konsep pemisahan memori diadopsi. Ketika sebuah utas muncul, semua variabel yang tersedia untuk utas baru disalin. Semua referensi untuk variabel-variabel ini digantikan oleh referensi untuk salinan. Transfer informasi antar aliran hanya dimungkinkan melalui objek yang dilindungi atau melalui hasil yang dikembalikan oleh aliran setelah selesai.

Benda-benda yang dilindungi selalu mengandung bagian-bagian penting untuk memastikan operasi atom. Masuk ke bagian kritis adalah otomatis - tidak ada operator terpisah untuk ini dalam bahasa. Objek yang dilindungi meliputi: antrian pesan, konsol, dan deskriptor file.

Membuat utas dimungkinkan dengan fungsi tampilan multi-utas

 (map-th (function (x) …) --) 

Map-th secara otomatis memulai jumlah utas sama dengan jumlah prosesor dalam sistem (atau dua kali lebih banyak jika Anda memiliki Intel di dalam). Dalam panggilan rekursif, panggilan peta-th berikutnya bekerja dalam satu utas.

Selain itu, ada fungsi utas bawaan yang menjalankan prosedur / fungsi pada utas terpisah.

 ;   (var  (thread --1)) (+ (--2) (wait )) 

15. Kebersihan fungsional dalam kode imperatif
Fox memiliki fungsi untuk pemrograman fungsional dan prosedur prosedural. Rutinitas yang dinyatakan menggunakan kata kunci fungsi tunduk pada persyaratan tidak adanya efek samping dan independensi hasil dari faktor eksternal.

Belum direalisasi


Beberapa fitur menarik dari Lisp tetap tidak terpenuhi karena prioritas rendah.

1. Metode umum
Kemampuan untuk membebani fungsi dengan defgeneric / defmethod.

2. Warisan

3. debugger bawaan
Ketika pengecualian terjadi, interpreter Lisp beralih ke mode debug.

4. UFFI
Antarmuka untuk menghubungkan modul yang ditulis dalam bahasa lain.

5. BIGNUM
Dukungan kedalaman bit sewenang-wenang

Dibuang

Beberapa fitur Lisp telah dianggap dan dianggap tidak berguna / berbahaya.

1. Kombinasi metode yang dipandu
Ketika suatu metode dipanggil untuk suatu kelas, kombinasi dari metode induk dilakukan dan dimungkinkan untuk mengubah aturan kombinasi. Perilaku akhir dari metode ini tampaknya tidak dapat diprediksi.

2. Mulai ulang
Penangan pengecualian dapat membuat perubahan pada status program dan mengirim perintah restart ke kode yang menghasilkan pengecualian. Efek dari aplikasi ini mirip dengan menggunakan operator GOTO untuk beralih dari fungsi ke fungsi.

3. Kisah Romawi
Lisp mendukung sistem angka, yang sudah ketinggalan zaman sebelum kemunculannya.

Gunakan


Berikut adalah beberapa contoh kode sederhana.

 (function crc8 (data :optional seed) (var result (if-nil seed 0)) (var s_data data) (for bit 8 (if (= (bit-and (bit-xor result s_data) $01) 0) (set result (shift result -1 8)) (else (set result (bit-xor result $18)) (set result (shift result -1 8)) (set result (bit-or result $80)))) (set s_data (shift s_data -1 8))) result) 

 ;     (map (function (x) (** x 2)) \(1 2 3)) 

 ;   ,   qwe      (filter (function (x) (regexp:match x «^qwe...»)) -) ;   ,   ,    (filter-th (function (x) (regexp:match x «^qwe...»)) -) 

Implementasi


Penerjemah ditulis dalam Delphi (FreePascal dalam mode kompatibilitas). Itu dibangun di Lazarus 1.6.2 dan lebih tinggi, di bawah Windows dan Linux 32 dan 64 bit. Dari dependensi eksternal, memerlukan libmysql.dll. Berisi sekitar 15_2000..20_000 baris. Ada sekitar 200 fungsi bawaan untuk berbagai keperluan (beberapa kelebihan beban delapan kali).

Disimpan di sini

Dukungan untuk pengetikan dinamis dilakukan dengan cara sepele - semua tipe data yang diproses diwakili oleh ahli waris dari kelas TValue yang sama.

Jenis yang paling penting untuk Lisp - daftarnya adalah, seperti kebiasaan di Delphi, kelas yang berisi array dinamis objek tipe TValue. Untuk jenis ini, mekanisme CopyOnWrite diimplementasikan.

Manajemen memori otomatis berdasarkan penghitungan referensi. Untuk struktur rekursif, semua tautan dalam struktur dihitung secara bersamaan. Pelepasan memori dimulai segera ketika variabel keluar dari lingkup. Tidak ada mekanisme untuk menunda awal pengumpul sampah.

Penanganan pengecualian bekerja pada mekanisme yang dibangun ke dalam Delphi. Dengan demikian, kesalahan yang terjadi pada kode interpreter dapat diproses oleh kode yang dapat dieksekusi pada Fox.

Setiap operator atau fungsi Lisi bawaan diimplementasikan sebagai metode atau fungsi dalam kode juru bahasa. Script dieksekusi oleh panggilan implementasi yang saling rekursif. Kode juru bahasa dan skrip memiliki tumpukan panggilan umum.

Variabel skrip disimpan dalam memori dinamis secara independen. Setiap fungsi yang ditentukan pengguna memiliki tumpukan sendiri untuk menyimpan referensi variabel, terlepas dari tumpukan tingkat atas atau tumpukan fungsi induk.

Kesulitan khusus adalah implementasi operator penugasan (set) untuk elemen struktural. Menghitung langsung pointer ke elemen yang diperlukan mengarah pada risiko menggantung tautan, karena sintaksis Lisi tidak melarang memodifikasi struktur selama perhitungan elemen yang diperlukan. Sebagai solusi kompromi, "penunjuk rantai" diimplementasikan - objek yang berisi referensi ke variabel dan array indeks numerik untuk menunjukkan jalur di dalam struktur. Pointer seperti itu juga rentan terhadap masalah tautan yang menggantung, tetapi jika terjadi kegagalan, ia menghasilkan pesan kesalahan yang bermakna.

Alat pengembangan


1. Konsol

2. Editor teks
Dilengkapi dengan penyorotan sintaks dan kemampuan untuk menjalankan skrip yang dapat diedit di F9.


Kesimpulan


Dalam kondisi saat ini, proyek menyelesaikan masalah yang dikandungnya, dan tidak memerlukan pengembangan aktif lebih lanjut. Banyak ketidaksempurnaan yang ada tidak mempengaruhi pekerjaan secara signifikan.

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


All Articles