[Common Lisp] 'Porządne' stałe

0

Jak w Lispie definiować 'porządne' stałe (jak te definiowane za pomocą const w C++ czy #define w C) - tj. takie, których wartości będą podstawiane do kodu w trakcie kompilacji jeszcze przed ekspansją makr itp.
Potrzebna by mi była taka jedna do tego, by przy wywoływaniu makr takich jak:

(defmacro selective-eval (source switcher)
	   `(list ,@(loop for element in source
			  for choice in switcher
			  collecting (if choice element))))

nie musieć cały czas wpisywać argumentów 'z ręki'. Obszukałem http://www.cs.cmu.edu/Groups/AI/html/cltl/clm/index.html, próbowałem 'zwykłego' defconstant, symbol-macrolet, compile-let i nic z tego. Najwyraźniej kompilator zaczyna od rozwijania najniższych makr a dopiero potem łaskawie zabiera się za wyrażenia wyżej położone. Żeby temu zaradzić napisałem sobie makro implementujące część preprocesora C:

(defmacro preprocessor (change-from change-to body-tree)
	   (subst change-to change-from body-tree))

tyle że to działa tylko dla pojedynczego podstawiania. Próbowałem dorobić obsługę kilku 'stałych' przez:

(defmacro with-gensyms ((&rest names) &body body) ; zerżnięte z http://www.gigamonkeys.com/book/ ;)
  `(let ,(loop for n in names collect `(,n (gensym)))
     ,@body))

(defmacro preprocessor (changes-list body-tree)
	   (with-gensyms (temp-val-name)
	     (let ((temp-val-name body-tree))
	       `(progn ,@(last (loop for change-pair in changes-list
			    do (setf temp-val-name (subst
						 (second change-pair)
						 (first change-pair)
						 temp-val-name))
			    collecting temp-val-name))))))

tyle że to obowiązkowe progn mnie trochę martwi (bez niego dostaję w wyniku niekompilowalne ((foo))) bo jest w tym wypadku absolutnie niepotrzebne a wykonać się musi ;)
Więc pytanie brzmi: czy nie wyważam przypadkiem otwartych drzwi? Czy jest jakieś wsparcie języka dla takich stałych?

// poprawiłem tagi code - 'code=lisp'... hm, jednak trochę to kolorowanie nawala :/ (dop. deus)

EDIT:
dzięki, nie wiedziałem nawet, że tu jest takie kolorowanie ;)

Odśmieciłem trochę kod (tamten był pisany na szybko ;))

(defmacro preprocessor (changes-list body-tree)
	   `(progn ,@(last (loop for change-pair in changes-list
	   	   	   	   do (setf body-tree (subst (second change-pair)
                                                                      (first change-pair) body-tree))
		   	   	   collecting body-tree)))
0
Ghostek napisał(a)

Jak w Lispie definiować 'porządne' stałe (jak te definiowane za pomocą const w C++ czy #define w C) - tj. takie, których wartości będą podstawiane do kodu w trakcie kompilacji jeszcze przed ekspansją makr itp.
Potrzebna by mi była taka jedna do tego, by przy wywoływaniu makr takich jak:

(defmacro selective-eval (source switcher)
	   `(list ,@(loop for element in source
			  for choice in switcher
			  collecting (if choice element))))

Mógłbyś dać przykład użycia tego makra który nie działa z obecnymi stałymi (czyli defconstant), myślałem chwilę i nie jestem w stanie podać żadnego.

Anonimowe stałe można w makrze tworzyć przy pomocy gensym, więc rezultat będzie identyczny do tego jakby tam była liczba (stała zostanie zewaluowana do wartości, a liczba do samej siebie).

0
fr3 napisał(a)

Mógłbyś dać przykład użycia tego makra który nie działa z obecnymi stałymi (czyli defconstant), myślałem chwilę i nie jestem w stanie podać żadnego.

Ja nie myślałem ani chwili i przetestowałem:
(defconstant foo '(1 2 3))
(selective-eval foo (nil nil t))
Type-error in KERNEL::OBJECT-NON-LIST-ERROR-HANDLER : FOO is not of type LIST
:P

Anonimowe stałe można w makrze tworzyć przy pomocy gensym, więc rezultat będzie identyczny do tego jakby tam była liczba (stała zostanie zewaluowana do wartości, a liczba do samej siebie).

A niby jak to ma zadziałać? Nazwa stałej NIE jest ewaluowana przed przekazaniem do makra (kompiluję pod CMUCL + SLIME, jeśli ma to jakieś znaczenie) więc makro próbuje operować na samej nazwie jako takiej, co nie ma szans powodzenia. Przechowywanie jej w zmiennej nic nie da (jakby co - sprawdziłem). Poza tym gensym jest bezużyteczny - i tak cały kod makra jest wykonywany podczas kompilacji, na wyjściu ma być tylko gotowa lista ;)

0

Ok, nie załapałem w ogóle o co chodzi.

W takim przypadku najlepszym wyjściem jest po prostu deklaracja makra (lokalnego (macrolet) albo nie) z ustalonym argumentem

(defmacro selective-eval-with-+foo+ (a)
  `(selective-eval ,+foo+ ,a))

albo

(defmacro selective-eval-with (co a)
	   `(selective-eval ,(symbol-value co) ,a))

Działa to dla wartości top-level, a stałe takimi są.

Ew całkowicie na chama

(defmacro selective-eval (source switcher)
	   (let ((wtf (etypecase source
			(list source)
			(symbol (symbol-value source)))))
	     `(list ,@(loop for element in wtf
			 for choice in switcher
			 collecting (if choice element)))))

Chociaż tutaj dobrze by było zrobić to w innym makrze, żeby nie zagnieżdzać, hm.
Afair można uczynić to działające dla zmiennych leksykalnych przy użyciu środowiska (&environment) ale nie wiem dokładnie jak ;]

Btw, o wiele prościej można zapisać samo makro bez pętli:

(defmacro selective-eval (source switcher)
	   (mapcar (lambda (a b) (when b a)) source switcher))

P.S. Imho najlepszym wyjściem będzie lokalne makro (macrolet) z ustawioną wartością.

Jak w Lispie definiować 'porządne' stałe (jak te definiowane za pomocą const w C++ czy #define w C) - tj. takie, których wartości będą podstawiane do kodu w trakcie kompilacji jeszcze przed ekspansją makr itp

Ale makra są rozwijane właśnie podczas kompilacji i wartości stałych zdefiniowanych przed użyciem danego makra są dostępne.
Same stałe nigdy nie są podstawiane (najwyżej jako optymalizacja, ale w sposób niewidoczny dla kodera) - to są po prostu zmienne read-only (zdaje się że dokładnie tak jak const w c++).

P.S.S Może oczywiście istnieć jakieś banalne rozwiązanie tego problemu, proponuje spytać się na comp.lang.lisp

0
fr3 napisał(a)

P.S. Imho najlepszym wyjściem będzie lokalne makro (macrolet) z ustawioną wartością.

Hmm, to już chyba zostanę przy moim makrze preprocessor skoro i tak trza kombinować ;)

JSame stałe nigdy nie są podstawiane (najwyżej jako optymalizacja, ale w sposób niewidoczny dla kodera) - to są po prostu zmienne read-only (zdaje się że dokładnie tak jak const w c++).

Nie, tak działa (dorobione nieco na siłę i zdrowo po czasie) const w nowszych standardach C (chyba, że coś zmieniono - nie jestem zbyt na bieżąco w temacie). W C++ stałe są podstawiane do kodu jak #define'y w C, można ich używać jako parametrów szablonów, rozmiarów tablic statycznych itp.

0

Sorki za doubleposta ale chciałem temat odświerzyć ;)

Teraz trochę z innej beczki, ale jest to związane z poprzednim problemem więc po co mam nowy temat zakładać ;)

Mam problem z tym kodem, używających 2 makr które opisałem wcześniej (znajdują się w pliku dołączanym na samym początku pliku), implementuje on automat komórkowy (na razie tylko 2 podstawowe klasy + konstruktor inicjalizujący komórki tak, by każda miała referencję do swoich sąsiadów):

(load "/home/marcin/programy/lisp/useful.lisp")

(defclass Cell ()
  ((neighbours
    ;; neighbour arrangement:
    ;; 123
    ;; 0x4
    ;; 765
    :initarg :neighbours
    :initform nil
    :accessor neighbours)
   (occupant
    :initarg :occupant
    :initform nil
    :accessor occupant)))

(defclass CellArray ()
  ((my-array
    :initarg :my-array
    :initform nil
    :accessor my-array)
   (size
    :initarg :size
    :initform 10
    :accessor size)))

(defmethod initialize-instance :after ((obj CellArray) &key)
  (with-accessors ((my-array my-array) (size size)) obj
  (preprocessor ((neighbour-list
		  ((aref my-array (- x 1) y)
		   (aref my-array (- x 1) (- y 1))
		   (aref my-array x (- y 1))
		   (aref my-array (+ x 1) (- y 1))
		   (aref my-array (+ x 1) y)
		   (aref my-array (+ x 1) (+ y 1))
		   (aref my-array x (+ y 1))
		   (aref my-array (- x 1) (+ y 1)))))
  (progn
  (setf my-array (make-array (list size size) :element-type 'Cell))
  
  ;;  x->
  ;; yoooo
  ;; |oooo
  ;; Voooo
  ;;  oooo

  ;; xoox
  ;; oooo
  ;; oooo
  ;; xoox

  (setf (neighbours (aref my-array 0 0))
		    (selective-eval neighbour-list (nil nil nil nil t t t nil)))

  (setf (neighbours (aref my-array (- size 1) 0))
		    (selective-eval neighbour-list (t nil nil nil nil nil t t)))

  (setf (neighbours (aref my-array (- size 1) (- size 1)))
		    (selective-eval neighbour-list (t t t nil nil nil nil nil)))

  (setf (neighbours (aref my-array 0 (- size 1)))
		    (selective-eval neighbour-list (nil nil t t t nil nil nil)))

  ;; oxxo
  ;; oooo
  ;; oooo
  ;; oooo

  (loop for x from 1 upto (- size 1) with y = 0 do
	(setf (neighbours (aref my-array x y))
	      (selective-eval neighbour-list (t nil nil nil t t t t))))

  ;; oooo
  ;; xooo
  ;; xooo
  ;; oooo

  (loop for y from 1 upto (- size 1) with x = 0 do
	(setf (neighbours (aref my-array x y))
	      (selective-eval neighbour-list (nil nil t t t t t nil))))

  ;; oooo
  ;; oooo
  ;; oooo
  ;; oxxo

  (loop for x from 1 upto (- size 1) with y = (- size 1) do
	(setf (neighbours (aref my-array x y))
	      (selective-eval neighbour-list (t t t t t nil nil nil))))

  ;; oooo
  ;; ooox
  ;; ooox
  ;; oooo

  (loop for y from 1 upto (- size 1) with x = (- size 1) do
	(setf (neighbours (aref my-array x y))
	      (selective-eval neighbour-list (t t t nil nil nil t t))))

  ;; oooo
  ;; oxxo
  ;; oxxo
  ;; oooo

  (loop for y from 1 upto (- size 1)
	for x from 1 upto (- size 1)
	do (setf (neighbours (aref my-array x y)) neighbour-list))))))

Otóż niby wszystko dobrze, ale dostaję błąd:

; Error: (during macroexpansion)
;
; Error in function WALKER::GET-WALKER-TEMPLATE:
; Can't get template for (NEIGHBOUR-LIST #)

... hę? Raz, że nie za bardzo widzę związek z moim kodem (jak przepuściłem jedną z pętli przez macroexpand to wszystko wyglądało ok) a dwa że męczenie wujka Google na ten temat nic nie dało. Jak temu zaradzić?

0

Pobawiłem się trochę przy makrach - teraz preprocesor ma złożoność liniową (twój ma kwadratową) i tworzy mniejszą ilość śmieci (twój tworzy kwadratową ;)) no i fajnie się to pisało.

Samego konstruktora za bardzo nie rozumiem - w tych setfach w ogóle nie jest widoczny żaden x ani y, więc prawa działać nie mają. W ostatnim loopie jako wynik jest ((aref ...)) - bez list, czyli mamy formę lambda, próbe wywołania funkcji (

CL-USER> ((lambda (a) (+ 2 a)) 4)
6

)
Co jest naprawione przez makro w macrolet.

A tutaj nowy kod...

Makra w każdym razie działają.

(defmacro selective-eval (source switcher)
  `(list ,@(mapcar (lambda (a b) (when b a)) source switcher)))

(defun curry (fn &rest arguments)
  (let* ((arguments (copy-list arguments))
	 (last (last arguments)))
    (if arguments
	(lambda (&rest args)
	  (setf (cdr last) args)
	  (apply fn arguments))
	fn)))

;;metody zeby mozna bylo pozniej latwo rozszerzyc na wlasne typy.
;;(czemu w ogole istnieje cos takiego jak funkcja :|)
(defmethod fsubst (subst-function obj)
  (funcall subst-function obj))

(defmethod fsubst (subst-function (obj list))
  (mapcar (curry #'fsubst subst-function) obj))

(defmacro macrotime-let (bindings &body code)
  (let ((symbols (make-hash-table :test #'eq)))
    (loop for (symbol initform) in bindings do
	 (setf (gethash symbol symbols) initform))
    `(progn ,@(fsubst (lambda (obj) (if (gethash obj symbols)
					(gethash obj symbols)
					obj))
		      code))))

(defclass Cell ()
  ((neighbours
    ;; neighbour arrangement:
    ;; 123
    ;; 0x4
    ;; 765
    :initarg :neighbours
    :initform nil
    :accessor neighbours)
   (occupant
    :initarg :occupant
    :initform nil
    :accessor occupant)))

(defclass CellArray ()
  ((my-array
    :initarg :my-array
    :initform nil
    :accessor my-array)
   (size
    :initarg :size
    :initform 10
    :accessor size)))

(defmethod initialize-instance :after ((obj CellArray) &rest initargs)
  (declare (ignorable initargs))
  (with-accessors ((my-array my-array) (size size)) obj
    (macrotime-let ((neighbour-list
		     ((aref my-array (- x 1) y)
		      (aref my-array (- x 1) (- y 1))
		      (aref my-array x (- y 1))
		      (aref my-array (+ x 1) (- y 1))
		      (aref my-array (+ x 1) y)
		      (aref my-array (+ x 1) (+ y 1))
		      (aref my-array x (+ y 1))
		      (aref my-array (- x 1) (+ y 1)))))
      
      #|
      ;;WTF - gdzie sa ustawiane x i y?	; ;

      (setf my-array (make-array (list size size) :element-type 'Cell))
      ;;  x->				; ; ; ; ;
      ;; yoooo				; ; ; ; ;
      ;; |oooo				; ; ; ; ; ;
      ;; Voooo				; ; ; ; ;
      ;;  oooo				; ; ; ; ;

      ;; xoox				; ; ; ; ;
      ;; oooo				; ; ; ; ;
      ;; oooo				; ; ; ; ;
      ;; xoox				; ; ; ; ;

      (setf (neighbours (aref my-array 0 0))
      (selective-eval neighbour-list (nil nil nil nil t t t nil)))

      (setf (neighbours (aref my-array (1- size) 0))
      (selective-eval neighbour-list (t nil nil nil nil nil t t)))

      (setf (neighbours (aref my-array (1- size) (- size 1)))
      (selective-eval neighbour-list (t t t nil nil nil nil nil)))

      (setf (neighbours (aref my-array 0 (1- size)))
      (selective-eval neighbour-list (nil nil t t t nil nil nil)))

      |#

      ;; oxxo				; ; ;
      ;; oooo				; ; ;
      ;; oooo				; ; ;
      ;; oooo				; ; ;

      (loop for x from 1 upto (1- size) with y = 0 do
	   (setf (neighbours (aref my-array x y))
		 (selective-eval neighbour-list (t nil nil nil t t t t))))

      ;; oooo				; ; ;
      ;; xooo				; ; ;
      ;; xooo				; ; ;
      ;; oooo				; ; ;

      (loop for y from 1 upto (1- size) with x = 0 do
	   (setf (neighbours (aref my-array x y))
		 (selective-eval neighbour-list (nil nil t t t t t nil))))

      ;; oooo				; ; ;
      ;; oooo				; ; ;
      ;; oooo				; ; ;
      ;; oxxo				; ; ;

      (loop for x from 1 upto (1- size) with y = (1- size) do
	   (setf (neighbours (aref my-array x y))
		 (selective-eval neighbour-list (t t t t t nil nil nil))))

      ;; oooo				; ; ;
      ;; ooox				; ; ;
      ;; ooox				; ; ;
      ;; oooo				; ; ;

      (loop for y from 1 upto (1- size) with x = (1- size) do
	   (setf (neighbours (aref my-array x y))
		 (selective-eval neighbour-list (t t t nil nil nil t t))))

      ;; oooo				; ; ;
      ;; oxxo				; ; ;
      ;; oxxo				; ; ;
      ;; oooo				; ; ;
      (macrolet ((%add-list (code)
		      (print code)
		      `(list ,@code)))
	   (loop for y from 1 upto (1- size)
	      for x from 1 upto (1- size)
	   ;;tutaj dodalem list, inaczej wychodzil z tych arefow kod
	      do (setf (neighbours (aref my-array x y)) (%add-list neighbour-list)))))))

P.S Można specyfikować na każdy typ, nie tylko klasę, a w tym wypadku struktury byłby szybsze (zapewne o tym wiesz, tak do wszystkich pisze ;). Za to kod byłby trudniejszy do rozszerzenia...

0

Dzięki za poprawione makra, zaraz potestuję ;) Tylko o jakie śmieci w moich makrach ci chodzi? selective-eval nie daje żadnych (te nile są potrzebne ;)) a preprocessor dowala tylko to nieszczęsne progn na początku bloku (i wymusza zastosowanie drugiego, ale to chyba dalej nic wielkiego ;))
A co do

;;WTF - gdzie sa ustawiane x i y?

Nigdzie. ;) Makra preprocessor i selective-eval miały za zadanie wstawienie tamtego kodu 'na chama' w odpowiednie miejsca pętli, np. pętla

(loop for x from 1 upto (- size 1) with y = 0 do
        (setf (neighbours (aref my-array x y))
              (selective-eval neighbour-list (t nil nil nil t t t t))))

była by rozwinięta do (pomijam rozwinięcie loopa)

(loop for x from 1 upto (- size 1) with y = 0 do
        (setf (neighbours (aref my-array x y))
              ((aref my-array (- x 1) y)
                nil
                nil
                nil
               (aref my-array (+ x 1) y)
               (aref my-array (+ x 1) (+ y 1))
               (aref my-array x (+ y 1))
               (aref my-array (- x 1) (+ y 1))))

(fakt, że chyba by się tu przydało 'list' na początku owej listy, zaraz sprawdzę, czy o to chodzi)
Nie da się tak? W każdym razie kod ma być tłumaczeniem na Lispa tego kodu napisanego w C++:

template<typename CELL, int WIDTH, int HEIGHT>
class CellularEngine
{
 public:
    CellularEngine();
    ~CellularEngine();
    
    void run();
    template<typename Func>
    Func traverse(Func);

    Array2D<CELL, WIDTH, HEIGHT>& getCells() {return cells;}
    
 private:
    Array2D<CELL, WIDTH, HEIGHT> cells;
};

template<typename CELL, int WIDTH, int HEIGHT>
CellularEngine<CELL, WIDTH, HEIGHT>::CellularEngine()
{
    // xoox
    // oooo
    // oooo
    // xoox
    cells.at(0, 0).neighbours[0] = 0;
    cells.at(0, 0).neighbours[1] = 0;
    cells.at(0, 0).neighbours[2] = 0;
    cells.at(0, 0).neighbours[3] = 0;
    cells.at(0, 0).neighbours[4] = &(cells.at(1, 0));
    cells.at(0, 0).neighbours[5] = &(cells.at(1, 1)); 
    cells.at(0, 0).neighbours[6] = &(cells.at(0, 1));
    cells.at(0, 0).neighbours[7] = 0;
    
    cells.at(WIDTH-1, 0).neighbours[0] = &(cells.at(WIDTH-2, 0));
    cells.at(WIDTH-1, 0).neighbours[1] = 0;
    cells.at(WIDTH-1, 0).neighbours[2] = 0;
    cells.at(WIDTH-1, 0).neighbours[3] = 0;
    cells.at(WIDTH-1, 0).neighbours[4] = 0;
    cells.at(WIDTH-1, 0).neighbours[5] = 0;
    cells.at(WIDTH-1, 0).neighbours[6] = &(cells.at(WIDTH-1, 1));
    cells.at(WIDTH-1, 0).neighbours[7] = &(cells.at(WIDTH-2, 1));
    
    cells.at(0, HEIGHT-1).neighbours[0] = 0;
    cells.at(0, HEIGHT-1).neighbours[1] = 0;
    cells.at(0, HEIGHT-1).neighbours[2] = &(cells.at(0, HEIGHT-2));
    cells.at(0, HEIGHT-1).neighbours[3] = &(cells.at(1, HEIGHT-2));
    cells.at(0, HEIGHT-1).neighbours[4] = &(cells.at(1, HEIGHT-1));
    cells.at(0, HEIGHT-1).neighbours[5] = 0;
    cells.at(0, HEIGHT-1).neighbours[6] = 0;
    cells.at(0, HEIGHT-1).neighbours[7] = 0;
    
    cells.at(WIDTH-1, HEIGHT-1).neighbours[0] = &(cells.at(WIDTH-2, HEIGHT-1));
    cells.at(WIDTH-1, HEIGHT-1).neighbours[1] = &(cells.at(WIDTH-2, HEIGHT-2));
    cells.at(WIDTH-1, HEIGHT-1).neighbours[2] = &(cells.at(WIDTH-1, HEIGHT-2));
    cells.at(WIDTH-1, HEIGHT-1).neighbours[3] = 0;
    cells.at(WIDTH-1, HEIGHT-1).neighbours[4] = 0;
    cells.at(WIDTH-1, HEIGHT-1).neighbours[5] = 0;
    cells.at(WIDTH-1, HEIGHT-1).neighbours[6] = 0;
    cells.at(WIDTH-1, HEIGHT-1).neighbours[7] = 0;
    
    // oxxo
    // xoox
    // xoox
    // oxxo
    for(int i=1; i<WIDTH-1; i++)
    {
        cells.at(i, 0).neighbours[0] = &(cells.at(i-1, 0));
        cells.at(i, 0).neighbours[1] = 0;
        cells.at(i, 0).neighbours[2] = 0;
        cells.at(i, 0).neighbours[3] = 0;
        cells.at(i, 0).neighbours[4] = &(cells.at(i+1, 0));
        cells.at(i, 0).neighbours[5] = &(cells.at(i+1, 1));
        cells.at(i, 0).neighbours[6] = &(cells.at(i, 1));
        cells.at(i, 0).neighbours[7] = &(cells.at(i-1, 1));
        
        cells.at(i, HEIGHT-1).neighbours[0] = &(cells.at(i-1, HEIGHT-1));
        cells.at(i, HEIGHT-1).neighbours[1] = &(cells.at(i-1, HEIGHT-2));
        cells.at(i, HEIGHT-1).neighbours[2] = &(cells.at(i+1, HEIGHT-2));
        cells.at(i, HEIGHT-1).neighbours[3] = &(cells.at(i+1, HEIGHT-2));
        cells.at(i, HEIGHT-1).neighbours[4] = &(cells.at(i+1, HEIGHT-1));
        cells.at(i, HEIGHT-1).neighbours[5] = 0;
        cells.at(i, HEIGHT-1).neighbours[6] = 0;
        cells.at(i, HEIGHT-1).neighbours[7] = 0;
    }
    
    for(int i=1; i<HEIGHT-1; i++)
    {
        cells.at(0, i).neighbours[0] = 0;
        cells.at(0, i).neighbours[1] = 0;
        cells.at(0, i).neighbours[2] = &(cells.at(0, i-1));
        cells.at(0, i).neighbours[3] = &(cells.at(1, i-1));
        cells.at(0, i).neighbours[4] = &(cells.at(1, i));
        cells.at(0, i).neighbours[5] = &(cells.at(1, i+1));
        cells.at(0, i).neighbours[6] = &(cells.at(0, i+1));
        cells.at(0, i).neighbours[7] = 0;
        
        cells.at(WIDTH-1, i).neighbours[0] = &(cells.at(WIDTH-2, i));
        cells.at(WIDTH-1, i).neighbours[1] = &(cells.at(WIDTH-2, i-1));
        cells.at(WIDTH-1, i).neighbours[2] = &(cells.at(WIDTH-1, i-1));
        cells.at(WIDTH-1, i).neighbours[3] = 0;
        cells.at(WIDTH-1, i).neighbours[4] = 0;
        cells.at(WIDTH-1, i).neighbours[5] = 0;
        cells.at(WIDTH-1, i).neighbours[6] = &(cells.at(WIDTH-1, i+1));
        cells.at(WIDTH-1, i).neighbours[7] = &(cells.at(WIDTH-2, i+1));
    }
        
    // oooo
    // oxxo
    // oxxo
    // oooo
    for(int x=1; x<WIDTH-1; x++)
    {
        for(int y=1; y<HEIGHT-1; y++)
        {
            cells.at(x, y).neighbours[0] = &(cells.at(x-1, y));
            cells.at(x, y).neighbours[1] = &(cells.at(x-1, y-1));
            cells.at(x, y).neighbours[2] = &(cells.at(x, y-1));
            cells.at(x, y).neighbours[3] = &(cells.at(x+1, y-1));
            cells.at(x, y).neighbours[4] = &(cells.at(x+1, y));
            cells.at(x, y).neighbours[5] = &(cells.at(x+1, y+1));
            cells.at(x, y).neighbours[6] = &(cells.at(x, y+1));
            cells.at(x, y).neighbours[7] = &(cells.at(x-1, y+1));
        }
    }
}

(pominąłem tylko implementację funkcji traverse oraz, z oczywistych względów, destruktora CellularEngine ;) Za to Array2D to klasa emulująca tablicę dwuwymiarową za pomocą jednowymiarowej, z równie oczywistych względów również pominięta w lispowym kodzie)
Naszpikowałem kod makrami właśnie po to, by uniknąć tego całego powtarzania kodu widocznego w powyższym.

EDIT:

(fakt, że chyba by się tu przydało 'list' na początku owej listy, zaraz sprawdzę, czy o to chodzi)

Niestety nie, po zmianie wywołania preprocesora na:

(preprocessor ((neighbour-list
		  (list
		   (aref my-array (- x 1) y)
		   (aref my-array (- x 1) (- y 1))
		   (aref my-array x (- y 1))
		   (aref my-array (+ x 1) (- y 1))
		   (aref my-array (+ x 1) y)
		   (aref my-array (+ x 1) (+ y 1))
		   (aref my-array x (+ y 1))
		   (aref my-array (- x 1) (+ y 1)))))

kompilator zmienia jeno gadkę na:

-+  Errors (1)
 `-- (during macroexpansion)
     
     Error in function WALKER::GET-WALKER-TEMPLATE:
        Can't get template for
        (NEIGHBOUR-LIST
         (LIST (AREF MY-ARRAY (- X 1) Y)
               (AREF MY-ARRAY (- X 1) (- Y 1))
               (AREF MY-ARRAY X (- Y 1))
               (AREF MY-ARRAY (+ X 1) (- Y 1))
               (AREF MY-ARRAY (+ X 1) Y)
               (AREF MY-ARRAY (+ X 1) (+ Y 1))
               (AREF MY-ARRAY X (+ Y 1))
               (AREF MY-ARRAY (- X 1) (+ Y 1)))).
0
Ghostek napisał(a)

Dzięki za poprawione makra, zaraz potestuję ;) Tylko o jakie śmieci w moich makrach ci chodzi? selective-eval nie daje żadnych (te nile są potrzebne ;)) a preprocessor dowala tylko to nieszczęsne progn na początku bloku (i wymusza zastosowanie drugiego, ale to chyba dalej nic wielkiego ;))
A co do

;;WTF - gdzie sa ustawiane x i y?

Nigdzie. ;) Makra preprocessor i selectivce-eval miały za zadanie wstawienie tamtego kodu 'na chama' w odpowiednie miejsca pętli, np. pętla

To się odnosiło do zakomentowanych setfów (#| i |# - podświetlanie 4p tego nie rozumie) - tam loop nie ma i żadne x i y nie są widoczne (chyba że jakiegoś kodu nie pokazałeś).
(no, w pierwszym w komentarzu x i y nie jest używane, dałem komentarz trochę za wcześnie)

(fakt, że chyba by się tu przydało 'list' na początku owej listy, zaraz sprawdzę, czy o to chodzi)

Niestety nie, po zmianie wywołania preprocesora na:

(preprocessor ((neighbour-list
		  (list
		   (aref my-array (- x 1) y)
		   (aref my-array (- x 1) (- y 1))
		   (aref my-array x (- y 1))
		   (aref my-array (+ x 1) (- y 1))
		   (aref my-array (+ x 1) y)
		   (aref my-array (+ x 1) (+ y 1))
		   (aref my-array x (+ y 1))
		   (aref my-array (- x 1) (+ y 1)))))

kompilator zmienia jeno gadkę na:

-+  Errors (1)
 `-- (during macroexpansion)
     
     Error in function WALKER::GET-WALKER-TEMPLATE:
        Can't get template for
        (NEIGHBOUR-LIST
         (LIST (AREF MY-ARRAY (- X 1) Y)
               (AREF MY-ARRAY (- X 1) (- Y 1))
               (AREF MY-ARRAY X (- Y 1))
               (AREF MY-ARRAY (+ X 1) (- Y 1))
               (AREF MY-ARRAY (+ X 1) Y)
               (AREF MY-ARRAY (+ X 1) (+ Y 1))
               (AREF MY-ARRAY X (+ Y 1))
               (AREF MY-ARRAY (- X 1) (+ Y 1)))).
</quote> Selective eval dostaje dane przetworzone przez makro preprocesor, więc tam 'list' być nie może. ( Natomiast ostatni kod z loopem nie używa selective-eval - po to jest to makro w macrolet.

Proponowane rozwiązanie - wywal dodawanie list w selective-eval, zrób w macrolet dla konstruktora makro '%sel-eval-without-list', a to ostatnie (moje) makro w macrolet (%add-list) wywal.

%set-eval-without-list mógłby wyglądać tak:

(macrolet ((set-eval-without-list (source switcher)
	     `(list (selective-eval ,(cdr source) ,switcher))))
					;kod
  )

Wtedy 'list' w neighbour-list będzie działać.

P.S Setf przyjmuje argumenty w parach, więc:

(setf a x)
(setf y z)

=

(setf a x y z)

Można w ten sposób te wszystkie setfy na początku zmieścic w jeden - mniej powtarzania = mniej kodu = czytelniejszy kod ;)

P.S.S Tak samo z loop - wszystkie loopy można połączyć w jeden. Ale czy to będzie czytelniejsze to już nie jestem do końca pewny.

P.S.S.S Czy ten program ma jakaś nazwę? (mam na myśli nazwę algorytmu).

0
fr3 napisał(a)

To się odnosiło do zakomentowanych setfów (#| i |# - podświetlanie 4p tego nie rozumie) - tam loop nie ma i żadne x i y nie są widoczne (chyba że jakiegoś kodu nie pokazałeś).
(no, w pierwszym w komentarzu x i y nie jest używane, dałem komentarz trochę za wcześnie)

Aaa no tak, zapomniałem o tym. Trzeba by dać:

  (let ((x 0) (y 0))
    (setf 
     (neighbours (aref my-array 0 0))
     (selective-eval neighbour-list (nil nil nil nil t t t nil))))

  (let ((x (- size 1)) (y 0))
    (setf	    
     (neighbours (aref my-array (- size 1) 0))
     (selective-eval neighbour-list (t nil nil nil nil nil t t))))

  (let ((x 0) (y (- size 1)))
    (setf
     (neighbours (aref my-array (- size 1) (- size 1)))
     (selective-eval neighbour-list (t t t nil nil nil nil nil))))

  (let ((x (- size 1)) (y (- size 1)))
    (setf
     (neighbours (aref my-array 0 (- size 1)))
     (selective-eval neighbour-list (nil nil t t t nil nil nil))))

Selective eval dostaje dane przetworzone przez makro preprocesor, więc tam 'list' być nie może.
(
Natomiast ostatni kod z loopem nie używa selective-eval - po to jest to makro w macrolet.

Proponowane rozwiązanie (...) Wtedy 'list' w neighbour-list będzie działać.

OK, chyba jednak wrócę do wersji bez list w neighbour-list i z twoim makrem, prościej będzie ;)

P.S Setf przyjmuje argumenty w parach, więc:

(setf a x)
(setf y z)

=

(setf a x y z)

Można w ten sposób te wszystkie setfy na początku zmieścic w jeden - mniej powtarzania = mniej kodu = czytelniejszy kod ;)

Teraz już niestety nie ;)

P.S.S Tak samo z loop - wszystkie loopy można połączyć w jeden. Ale czy to będzie czytelniejsze to już nie jestem do końca pewny.

Szczerze mówiąc, wątpię ;)

P.S.S.S Czy ten program ma jakaś nazwę? (mam na myśli nazwę algorytmu).

Chyba nie, w każdym razie kod w C++ również jest mój. Po wstępnej nauce Lispa przerabiam nań stare projekty napisane w innych językach (głównie C++). A tak poza tym, to od strony algorytmicznej program jest banalny, przypisuje on po prostu każdej komórce od 3 do 8 sąsiadów w zależności od położenia. W C++ wszystko sprowadzało się do powtórzenia kilkakrotnie jednego kawałka kodu, w Lispie z kolei do napisanie nieco dłuższego kodu dzięki któremu obędzie się bez powtórzeń ;)

0

BUMP

Wiem, ze to podpada juz prawie pod niekromancje tematowa, ale jakis czas temu przypadkiem natknalem sie na lepsze rozwiazanie, totez postanowilem sie nim tu podzielic bo 'a nuz sie komus przyda' ;)

Zeby stala (czy cokolwiek co jest znane w czasie odczytu, ale dla stalych jest to chyba gwarantowane ;)) byla ewaluowana 'na miejscu' wystarczy dodac przed jej nazwa ' #. '. (nie wiem od czego jest to skrocona forma ale to chyba nie wazne). Na przyklad:

(defmacro frob (a b) (+ a b))

(defconstant +foo+ 42)

(frob +foo+ 2)    ; blad, 'Argument X is not a NUMBER: +foo+
(frob #.+foo+ 2) ; ok
0

Nie bardzo rozumiem, o co chodzi (odczyt stałych przy macroexpand?), ale mam wrażenie, że przydałoby Ci się define-symbol-macro.

0
dodekam napisał(a)

Nie bardzo rozumiem, o co chodzi (odczyt stałych przy macroexpand?), ale mam wrażenie, że przydałoby Ci się define-symbol-macro.

http://www.lisp.org/HyperSpec/Body/sec_2-4-8-6.html

@Ghostek - ciekawe, nie znałem, dzięki.

1 użytkowników online, w tym zalogowanych: 0, gości: 1