common-lisp
Formularze grupujące
Szukaj…
Kiedy potrzebne jest grupowanie?
W niektórych miejscach w Common Lisp szereg formularzy jest poddawany ocenie w kolejności. Na przykład w ciele rozczłonkowanym lub lambda lub w ciele dotimesa . W takich przypadkach pisanie wielu formularzy w kolejności działa zgodnie z oczekiwaniami. W kilku miejscach, jednak, jak i ówczesnych else if części wyrazu, tylko jedna forma jest dozwolona. Oczywiście, można chcieć ocenić wiele wyrażeń w tych miejscach. W takich sytuacjach potrzebny jest pewien rodzaj jawnej formy grupowania.
Progn
Prognozowanie specjalnego operatora ogólnego przeznaczenia służy do oceny zerowej lub większej liczby formularzy. Zwracana jest wartość ostatniego formularza. Na przykład w poniższym przykładzie (print 'hello) jest oceniane (a jego wynik jest ignorowany), a następnie 42 jest oceniane i zwracany jest jego wynik ( 42 ):
(progn
(print 'hello)
42)
;=> 42
Jeśli w prognozie nie ma żadnych form, zwracane jest zero :
(progn)
;=> NIL
Oprócz grupowania serii formularzy progn ma również ważną właściwość, że jeśli forma prognozy jest formą najwyższego poziomu , wówczas wszystkie zawarte w niej formy są przetwarzane jako formy najwyższego poziomu. Może to być ważne przy pisaniu makr rozwijających się do wielu formularzy, które powinny być przetwarzane jako formularze najwyższego poziomu.
Progn jest także cenny, ponieważ zwraca wszystkie wartości ostatniej formy. Na przykład,
(progn
(print 'hello)
(values 1 2 3))
;;=> 1, 2, 3
W przeciwieństwie do tego, niektóre wyrażenia grupujące zwracają tylko podstawową wartość formularza generującego wynik.
Niejawny postęp
Niektóre formularze używają niejawnych progów do opisania swojego zachowania. Na przykład makra kiedy i chyba , że są zasadniczo jednostronne, jeśli formy, opisują swoje zachowanie w kategoriach domyślnego prognozy . Oznacza to, że forma jak
(when (foo-p foo)
form1
form2)
ocenia się i warunek (bla-P bla) jest prawdziwy, wówczas form1 i Form2 są pogrupowane tak, jakby były zawarte w progn. Rozwinięcie makra when jest zasadniczo:
(if (foo-p foo)
(progn
form1
form2)
nil)
Prog1 i Prog2
Często pomocne jest oszacowanie wielu wyrażeń i zwrócenie wyniku z pierwszej lub drugiej formy zamiast z ostatniej. Można to łatwo osiągnąć za pomocą let i, na przykład:
(let ((form1-result form1))
form2
form3
;; ...
form-n-1
form-n
form1-result)
Ponieważ ta forma jest powszechna w niektórych aplikacjach, Common Lisp zawiera prog1 i prog2, które są podobne do prognozy , ale zwracają wynik odpowiednio pierwszej i drugiej formy. Na przykład:
(prog1
42
(print 'hello)
(print 'goodbye))
;; => 42
(prog2
(print 'hello)
42
(print 'goodbye))
;; => 42
Ważną różnicą między prog1 / prog2 i progn jest jednak to, że progn zwraca wszystkie wartości ostatniej postaci, podczas gdy prog1 i prog2 zwracają tylko wartość pierwotną pierwszej i drugiej postaci. Na przykład:
(progn
(print 'hello)
(values 1 2 3))
;;=> 1, 2, 3
(prog1
(values 1 2 3)
(print 'hello))
;;=> 1 ; not 1, 2, 3
W przypadku wielu wartości z oceną stylu prog1 użyj zamiast tego wielu wartości prog1 . Nie ma podobnego prog2 z wieloma wartościami , ale implementacja nie jest trudna, jeśli jej potrzebujesz.
Blok
Specjalny blok operatora pozwala na grupowanie kilku form Lisp (takich jak niejawne progn
), a także nazywa nazwę bloku. Podczas oceniania formularzy w bloku można użyć specjalnego operatora return-from do opuszczenia bloku. Na przykład:
(block foo
(print 'hello) ; evaluated
(return-from foo)
(print 'goodbye)) ; not evaluated
;;=> NIL
return-from może być również wyposażony w wartość zwracaną:
(block foo
(print 'hello) ; evaluated
(return-from foo 42)
(print 'goodbye)) ; not evaluated
;;=> 42
Nazwane bloki są przydatne, gdy fragment kodu ma znaczącą nazwę lub gdy bloki są zagnieżdżone. W pewnym kontekście ważna jest tylko możliwość wcześniejszego powrotu z bloku. W takim przypadku możesz użyć nil jako nazwy bloku i zwrócić . Zwrot jest podobny do powrotu , z tym wyjątkiem, że nazwa bloku jest zawsze równa zero .
Uwaga: załączone formularze nie są formularzami najwyższego poziomu. Różni się to od progn
, w której zamknięte formy progn
najwyższego poziomu są nadal uważane za formy najwyższego poziomu .
Tagbody
W celu uzyskania dużej kontroli w formularzach grupowych operator specjalny tagbody może być bardzo pomocny. Formularze wewnątrz formularza tagbody są albo tagami go (które są tylko symbolami lub liczbami całkowitymi), albo formularzami do wykonania. W tagbody operator go special służy do przeniesienia wykonania do nowej lokalizacji. Ten rodzaj programowania można uznać za dość niski poziom, ponieważ umożliwia on dowolne ścieżki wykonywania. Poniżej znajduje się pełny przykład tego, jak może wyglądać pętla for zaimplementowana jako tagbody :
(let (x) ; for (x = 0; x < 5; x++) { print(hello); }
(tagbody
(setq x 0)
prologue
(unless (< x 5)
(go end))
begin
(print (list 'hello x))
epilogue
(incf x)
(go prologue)
end))
Chociaż tagbody i go nie są powszechnie używane, być może z powodu „GOTO uważanego za szkodliwe”, ale może być pomocne przy wdrażaniu złożonych struktur kontrolnych, takich jak maszyny stanu. Wiele konstrukcji iteracyjnych rozwija się również w domniemany tagbody . Na przykład treść kropek jest określona jako seria znaczników i formularzy.
Której formy użyć?
Pisząc makra, które rozwijają się w formy, które mogą wymagać grupowania, warto poświęcić trochę czasu na zastanowienie się, w którą konstrukcję grupowania się rozwinąć.
Dla form typu definicja, na przykład, definiować-widget makro, które zazwyczaj pojawiają się jako forma najwyższego poziomu, a kilka defun s, defstruct s, itd., To zwykle ma sens używać progn tak, że dzieci są formularze przetwarzane jako formularze najwyższego poziomu. W przypadku formularzy iteracyjnych bardziej niejawne jest tagbody .
Na przykład, ciało dotimes , dolist i zrobić każdy ekspansję na niejawny tagbody.
W przypadku formularzy, które definiują nazwaną „część” kodu, niejawny blok jest często przydatny. Na przykład, podczas gdy ciało defun jest wewnątrz niejawny progn, że niejawna progn jest w bloku wymiany nazwę funkcji. Oznacza to, że można użyć return-from do wyjścia z funkcji. Taki komp