Istnieje wiele języków, które pozwalają na pewien rodzaj metaprogramowania . W szczególności jestem zaskoczony brakiem odpowiedzi na temat rodziny języków Lisp .
Z wikipedii:
Metaprogramowanie to pisanie programów komputerowych z możliwością traktowania programów jako ich danych.
Później w tekście:
Lisp jest prawdopodobnie kwintesencją języka z narzędziami do metaprogramowania, zarówno ze względu na historyczne pierwszeństwo, jak i ze względu na prostotę i siłę jego metaprogramowania.
Języki Lisp
Następuje szybkie, zmyślone wprowadzenie do Lisp.
Jednym ze sposobów na zobaczenie kodu jest zestaw instrukcji: zrób to, następnie zrób to, a następnie zrób jeszcze jedną rzecz ... To jest lista! Lista rzeczy do zrobienia dla programu. I oczywiście możesz mieć listy wewnątrz list do reprezentowania pętli i tak dalej ...
Jeśli będziemy reprezentować listę zawierającą elementy a, b, c, d tak: (ABCD) otrzymujemy coś, co wygląda jak Lisp wywołania funkcji, gdzie a
jest funkcja, i b
, c
, d
są argumenty. Jeśli faktem jest typowy „Hello World!” program można napisać tak:(println "Hello World!")
Oczywiście b
, c
lub d
mogą to być listy, które również coś oceniają. Następujące: (println "I can add :" (+ 1 3) )
wydrukuje „„ Mogę dodać: 4 ”.
Tak więc program to seria zagnieżdżonych list, a pierwszy element to funkcja. Dobra wiadomość jest taka, że możemy manipulować listami! Możemy więc manipulować językami programowania.
Zaleta Lisp
Lisps to nie tyle języki programowania, co zestaw narzędzi do tworzenia języków programowania. Programowalny język programowania.
W Lisps jest to nie tylko łatwiejsze do tworzenia nowych operatorów, ale także prawie niemożliwe jest napisanie niektórych operatorów w innych językach, ponieważ argumenty są oceniane po przekazaniu do funkcji.
Na przykład w języku podobnym do C powiedzmy, że chcesz sam napisać if
operator, coś w stylu:
my-if(condition, if-true, if-false)
my-if(false, print("I should not be printed"), print("I should be printed"))
W takim przypadku oba argumenty zostaną ocenione i wydrukowane, w kolejności zależnej od kolejności oceny argumentów.
W Lisps pisanie operatora (nazywamy to makro) i pisanie funkcji jest mniej więcej tym samym i jest używane w ten sam sposób. Główną różnicą jest to, że parametry makra nie są oceniane przed przekazaniem ich jako argumentów do makra. Jest to niezbędne, aby móc napisać niektóre operatory, takie jak if
powyższe.
Prawdziwe języki
Pokazuje, jak dokładnie jest to trochę poza zasięgiem, ale zachęcam do wypróbowania programowania w jednym Lisp, aby dowiedzieć się więcej. Na przykład możesz rzucić okiem na:
- Schemat , stara, dość „czysta” Lisp z małym rdzeniem
- Common Lisp, większy Lisp z dobrze zintegrowanym systemem obiektowym i wieloma implementacjami (jest standaryzowany przez ANSI)
- Ruszaj na maszynie Lisp
- Clojure mój ulubiony, powyższe przykłady to kod Clojure. Nowoczesny Lisp działający na JVM. Istnieją również przykłady makr Clojure na SO (ale nie jest to właściwe miejsce na początek. Na początku przyjrzałbym się 4clojure , braveclojure lub clojure koans ).
A tak przy okazji, Lisp oznacza LISt Processing.
Jeśli chodzi o twoje przykłady
Podam przykłady przy użyciu Clojure poniżej:
Jeśli możesz napisać add
funkcję w Clojure (defn add [a b] ...your-implementation-here... )
, możesz ją +
tak nazwać (defn + [a b] ...your-implementation-here... )
. To właśnie dzieje się w rzeczywistej implementacji (treść funkcji jest nieco bardziej zaangażowana, ale definicja jest zasadniczo taka sama, jak napisałem powyżej).
Co z notacją infix? Cóż, Clojure używa prefix
notacji (lub polskiej), więc możemy stworzyć infix-to-prefix
makro, które zamienia prefiks w kod Clojure. Co jest w rzeczywistości zaskakująco łatwe (w rzeczywistości jest to jedno z ćwiczeń makro w kojojach clojure)! Można go również zobaczyć na wolności, na przykład zobacz makro Incanter$=
.
Oto wyjaśniona najprostsza wersja z koanów:
(defmacro infix [form]
(list (second form) (first form) (nth form 2)))
;; takes a form (ie. some code) as parameter
;; and returns a list (ie. some other code)
;; where the first element is the second element from the original form
;; and the second element is the first element from the original form
;; and the third element is the third element from the original form (indexes start at 0)
;; example :
;; (infix (9 + 1))
;; will become (+ 9 1) which is valid Clojure code and will be executed to give 10 as a result
Aby jeszcze bardziej rozwinąć sprawę , niektóre cytaty Lisp :
„Częścią wyróżniającą Lisp jest to, że został zaprojektowany z myślą o ewolucji. Możesz użyć Lisp do zdefiniowania nowych operatorów Lisp. Gdy nowe abstrakcje stają się popularne (na przykład programowanie obiektowe), zawsze łatwo jest je zaimplementować w Lisp. Podobnie jak DNA, taki język nie wychodzi z mody. ”
- Paul Graham, ANSI Common Lisp
„Programowanie w Lisp jest jak zabawa z pierwotnymi siłami wszechświata. Czuje się jak błyskawica między opuszkami palców. Żaden inny język nie jest nawet blisko. ”
- Glenn Ehrlich, Road to Lisp