N-королева-и-лошадка

21

Существует вариант хорошо известной проблемы N-ферзей, которая включает в себя королев и рыцарей и, как говорят, «значительно сложнее» 1 . Постановка проблемы заключается в следующем:

Вы должны разместить на шахматной доске равное количество рыцарей que и королев board, чтобы ни одна фигура не атаковала любую другую фигуру. Какое максимальное количество фигур вы можете разместить на доске, и сколько разных способов вы можете сделать это?

В этом вызов, вам будет предоставлен входной п от 3 до 32 (в пути , который является наиболее подходящим для вашего языка). Для данного n может быть ноль или более решений вышеуказанной проблемы. Если решения не существует, вы должны ничего не выводить / не возвращать ( ноль , пустая строка , ложь , ...). В противном случае вы должны дать два результата:

  1. Доска решений (см. Ниже) для размера n, где невозможно добавить шахматную фигуру ферзя или рыцаря, не подвергая атаке какую-либо фигуру. Там должно быть равное количество королев и рыцарей .
  2. Источник запускаемой программы, который не принимает входные данные и дает (i) другое решение (или ничего ) для того же размера n в том же формате, а также (ii) другую программу для следующего решения (и т. Д. ...).

Обратите внимание, что:

  • Последовательность программ никогда не должна возвращать одну и ту же плату дважды, она должна охватывать все возможные решения проблемы размера n и, в конечном итоге, должна завершаться (без вывода).
  • Вы можете либо вернуть два значения, вернуть одно и напечатать другое, либо распечатать два возвращаемых значения.
  • Однако , если вы печатаете и доску, и следующую программу, доску не следует рассматривать как часть следующей программы (я рекомендую печатать доску в комментарии или использовать как стандартный вывод, так и потоки ошибок).
  • Возвращаемое значение должно быть строкой, а не замыканием.

Формат платы

  • Доска - это квадрат размера n .
  • Ячейка доски может быть пустой, королева или рыцарь.
  • Вы должны выбрать различные значения для каждого типа ячеек (т.е. вы можете использовать другие символы, кроме Q, N при печати доски).
  • Если вы возвращаете нестроковую доску, это должна быть упорядоченная коллекция из n 2 значений платы (например, матрица, вектор или список в мажорном порядке строки / столбца, ...).
  • Если вы печатаете доску, вы можете распечатать ее в квадрате или в виде линии. Например, доска решений размером 4 может быть напечатана следующим образом (пробелы не требуются; символы на ваше усмотрение):

    Q - - -
    - - - -
    - - - -
    - - N -
    

    Если вы чувствуете это, вы также можете вывести:

    ♛ · · ·
    · · · ·
    · · · ·
    · · ♞ ·
    

    ... но этого достаточно

    Q-------------N-
    

    Не имеет значения, выполняете ли вы итерацию по ячейкам в мажорном порядке строк или столбцов, потому что существуют симметричные решения. Например, решения для n = 4:

    Q------N--------
    Q----------N----
    Q------------N--
    Q-------------N-
    -Q----------N---
    -Q------------N-
    -Q-------------N
    --Q---------N---
    --Q----------N--
    --Q------------N
    ---QN-----------
    ---Q----N-------
    ---Q---------N--
    ---Q----------N-
    ---NQ-----------
    ----Q------N----
    ----Q----------N
    N------Q--------
    -------QN-------
    -------Q----N---
    ---N----Q-------
    -------NQ-------
    --------Q------N
    N----------Q----
    ----N------Q----
    -----------QN---
    -N----------Q---
    --N---------Q---
    -------N----Q---
    -----------NQ---
    N------------Q--
    --N----------Q--
    ---N---------Q--
    N-------------Q-
    -N------------Q-
    ---N----------Q-
    -N-------------Q
    --N------------Q
    ----N----------Q
    --------N------Q
    

Вы также можете посмотреть на решения для n = 5 как матрицы ; доски содержит #, qи nсимволы, которые пустые клетки разных видов (см мой ответ ниже). Я считаю 2836 досок для n = 6 , как и в ответе Sleafar (я внес ошибку при уменьшении количества байтов, но теперь это исправлено).

Большое спасибо Sleafar за то, что он нашел не один, а две ошибки в моем коде.

Гол

Самый короткий код в байтах побеждает.

Мы измеряем размер первой программы, которая принимает n .


1 . Королевы и рыцари , Роджер К. Уи (будьте осторожны! Содержит решение)

CoreDump
источник
4
Может быть, вы должны положить за это награду. Честно говоря, проблема достаточно сложна без квин-части.
mbomb007
Можем ли мы использовать какие-либо символы, кроме Q, N и -, для обозначения Королев и Рыцарей и пустые, если они различны?
Роковая
@Fatalize Да, конечно
coredump
1
@coredump Я имел в виду чтение содержимого функции. И я приму это как «да, вам разрешено читать ваш собственный исходный код и / или содержимое функций». (Мое решение опирается на это, так что ...)
wizzwizz4
1
@coredump Если я правильно понимаю задачу, то ваше эталонное решение для n = 6 содержит недопустимые записи (например -------------------------N--------Q-, недопустимо, потому что можно добавить больше частей :) Q--------N---------------N--------Q-.
Sleafar

Ответы:

2

Groovy, 515 байтов

X=0;Y="N="+args[0]+";M=N*N;S=[];def f(b,i,j,v){(i..<j).findAll{k->!(0..<M).any{l->w=b[l];r=(k.intdiv(N)-l.intdiv(N)).abs();c=(k%N-l%N).abs();s=v+w;w>0&&(k==l||(r==0||c==0||r==c?s<4:r<3&&c<3&&s>2))}}.collect{a=b.clone();a[it]=v;[it,a]}};def r(b,q,n){f(b,q,M,1).each{i->f(i[1],n,M,2).each{j->if(f(j[1],0,M,1).any{f(it[1],0,M,2)}){r(j[1],i[0],j[0])}else{S.add(j[1])}}}};r(new int[M],0,0);if(x<S.size()){sprintf('//%s%cX=%d;Y=%c%s%c;print(Eval.xy(X,Y,Y))',S[x].toString(),10,x+1,34,y,34)}else{''}";print(Eval.xy(X,Y,Y))

тестирование

Введите n в качестве аргумента командной строки:

groovy qak.groovy 4

Первая строка вывода всегда является решением в виде комментария (0 = пусто, 1 = королева, 2 = рыцарь), за которым следует код во второй строке:

//[1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0]
X=1;Y="N=4;M=N*N;S=[];def f(b,i,j,v){(i..<j).findAll{k->!(0..<M).any{l->w=b[l];r=(k.intdiv(N)-l.intdiv(N)).abs();c=(k%N-l%N).abs();s=v+w;w>0&&(k==l||(r==0||c==0||r==c?s<4:r<3&&c<3&&s>2))}}.collect{a=b.clone();a[it]=v;[it,a]}};def r(b,q,n){f(b,q,M,1).each{i->f(i[1],n,M,2).each{j->if(f(j[1],0,M,1).any{f(it[1],0,M,2)}){r(j[1],i[0],j[0])}else{S.add(j[1])}}}};r(new int[M],0,0);if(x<S.size()){sprintf('//%s%cX=%d;Y=%c%s%c;print(Eval.xy(X,Y,Y))',S[x].toString(),10,x+1,34,y,34)}else{''}";print(Eval.xy(X,Y,Y))

Следующий скрипт может быть использован для автоматического тестирования ( снова введите n в качестве аргумента):

#!/bin/bash
set -e
test -n "$1"
groovy qak.groovy "$1" > t
while test -s t; do
    head -n1 t
    groovy t > t2
    mv t2 t
done

Поскольку я пытался сделать решение как можно меньшим, оно очень медленное (подробности см. Ниже). Я протестировал только n = 4 с этой версией, чтобы увидеть, работает ли квинификация.

Полученные результаты

n = 4: 40 решенияпреобразованном формате )
n = 5: 172 решенияпреобразованном формате )
n = 6: 2836 решенийпреобразованном формате) )

Алгоритм

Это немного неутешительный вариант решения, не относящийся к квине:

N=args[0] as int
M=N*N
S=[]

/**
 * Generate a list of valid posibilities to place a new piece.
 * @param b Starting board.
 * @param i Start of the index range to check (inclusive).
 * @param j End of the index range to check (exclusive).
 * @param v Value of the new piece (1=queen, 2=knight).
 * @return A pair with the index of the new piece and a corresponding board for each possibility.
 */
def f(b,i,j,v){
    (i..<j).findAll{k->
        !(0..<M).any{l->
            w=b[l]
            r=(k.intdiv(N)-l.intdiv(N)).abs()
            c=(k%N-l%N).abs()
            s=v+w
            w>0&&(k==l||(r==0||c==0||r==c?s<4:r<3&&c<3&&s>2))
        }
    }.collect{
        a=b.clone();a[it]=v;[it,a]
    }
}

/**
 * Recursively look for solutions.
 * @param b Starting board.
 * @param q Start of the index range to check for queens.
 * @param n Start of the index range to check for knights.
 */
def r(b,q,n){
    f(b,q,M,1).each{i->
        f(i[1],n,M,2).each{j->
            if(f(j[1],0,M,1).any{f(it[1],0,M,2)}){
                r(j[1],i[0],j[0])
            }else{
                S.add(j[1])
            }
        }
    }
}

r(new int[M],0,0)
S.each{println(it)}

Quineification

Я использовал очень простой подход, чтобы сохранить небольшой размер кода.

X=0;Y="...";print(Eval.xy(X,Y,Y))

Переменная X содержит индекс решения для печати далее. Y содержит измененную копию алгоритма, описанного выше, который используется для вычисления всех решений, а затем выбирает только одно из них, что является причиной его медлительности. Преимущество этого решения в том, что оно не требует большого количества дополнительного кода. Код, хранящийся в Y , выполняется с помощью Eval класса (истинный квин не требуется).

Модифицированный код печатает решение, на которое указывает X , увеличивает X и добавляет копию самого себя:

//[...]
X=1;Y="...";print(Eval.xy(X,Y,Y))

Я также попытался вывести все решения в виде кода для второго шага, но для n = 6 он выдавал слишком много кода, чтобы Groovy мог его обработать.

Sleafar
источник
Хороший ответ, хорошая работа.
coredump
6

Common Lisp, 737

самостоятельно ответ

(lambda(n &aux(d 1))#2=(catch'$(let((s(* n n))(c d))(labels((R(w % @ b ! &aux r h v a)(loop for u from % below s do(setf h(mod u n)v(floor u n)a #4=(aref b u))(when(< 0(logand a w)4)(and(= 6 w)!(throw'! t))(let((b(copy-seq b))(o 5))(loop for(K D)on'(-1 -2 -1 2 1 -2 1 2)for y =(+ K v)for x =(+(or D -1)h)for u =(and(< -1 y n)(< -1 x n)(+(* y n)x))if u do #1=(if(< #4#4)(setf #4#(logand #4#o(if(= w o)3 0)))))(#8=dotimes(y N)(#8#(x N)(let((u(+(* y n)x))(o 6))(if(or(= x h)(= y v)(=(abs(- h x))(abs(- v y))))#1#))))(setf #4#w r(or(cond((= w 5)(R 6 @ U b !))((R 5 @ U b())t)((catch'!(R 5 0 0 b t))t)(t(and(=(decf c)0)(incf d)(or(format t"~%(lambda(&aux(n ~A)(d ~A))~%~S)"n d'#2#)(throw'$ B)))t))r)))))r))(R 5 0 0(fill(make-array s)3)())))))

пример

Вставьте вышеупомянутое в REPL, который возвращает объект функции:

#<FUNCTION (LAMBDA (N &AUX (D 1))) {1006D1010B}>

Назовите его (звезда привязана к последнему возвращенному значению):

QN> (funcall * 4)

Это выводит следующее на стандартный вывод:

(lambda(&aux(n 4)(d 2))
#1=(CATCH '$
 (LET ((S (* N N)) (C D))
   (LABELS ((R (W % @ B ! &AUX R H V A)
              (LOOP FOR U FROM % BELOW S
                    DO (SETF H (MOD U N)
                             V (FLOOR U N)
                             A #2=(AREF B U)) (WHEN (< 0 (LOGAND A W) 4)
                                                (AND (= 6 W) !
                                                     (THROW '! T))
                                                (LET ((B (COPY-SEQ B))
                                                      (O 5))
                                                  (LOOP FOR (K D) ON '(-1
                                                                       -2
                                                                       -1 2
                                                                       1 -2
                                                                       1 2)
                                                        FOR Y = (+ K V)
                                                        FOR X = (+
                                                                 (OR D -1)
                                                                 H)
                                                        FOR U = (AND
                                                                 (< -1 Y N)
                                                                 (< -1 X N)
                                                                 (+ (* Y N)
                                                                    X))
                                                        IF U
                                                        DO #3=(IF (< #2# 4)
                                                                  (SETF #2#
                                                                          (LOGAND
                                                                           #2#
                                                                           O
                                                                           (IF (=
                                                                                W
                                                                                O)
                                                                               3
                                                                               0)))))
                                                  (DOTIMES (Y N)
                                                    (DOTIMES (X N)
                                                      (LET ((U
                                                             (+ (* Y N) X))
                                                            (O 6))
                                                        (IF (OR (= X H)
                                                                (= Y V)
                                                                (=
                                                                 (ABS
                                                                  (- H X))
                                                                 (ABS
                                                                  (- V
                                                                     Y))))
                                                            #3#))))
                                                  (SETF #2# W
                                                        R
                                                          (OR
                                                           (COND
                                                            ((= W 5)
                                                             (R 6 @ U B !))
                                                            ((R 5 @ U B
                                                                NIL)
                                                             T)
                                                            ((CATCH '!
                                                               (R 5 0 0 B
                                                                  T))
                                                             T)
                                                            (T
                                                             (AND
                                                              (= (DECF C)
                                                                 0)
                                                              (INCF D)
                                                              (OR
                                                               (FORMAT T
                                                                       "~%(lambda(&aux(n ~A)(d ~A))~%~S)"
                                                                       N D
                                                                       '#1#)
                                                               (THROW '$
                                                                 B)))
                                                             T))
                                                           R)))))
              R))
     (R 5 0 0 (FILL (MAKE-ARRAY S) 3) NIL)))))

Кроме того, значение, возвращаемое этой функцией:

#(5 0 0 0 0 0 0 6 0 0 0 2 0 2 0 0)

... который является литералом массива. Число 5 представляет королев, 6 - рыцарей, а все остальное обозначает пустую ячейку, за исключением того, что внутри хранится больше информации. Если мы скопируем и вставим возвращенную функцию в repl, мы получим новую функцию.

#<FUNCTION (LAMBDA (&AUX (N 4) (D 2))) {100819148B}>

И мы можем назвать это без аргументов:

QN> (funcall * )

Этот вызов возвращает новое решение #(5 0 0 0 0 0 0 2 0 0 0 6 0 0 2 0) и источник другой функции (здесь не показано). Если исходная функция или последняя сгенерированная не находит решения, ничего не печатается и ничего не возвращается.

Внутренние ценности

|----------+--------+---------+--------+-----------------|
|          | Binary | Decimal | Symbol | Meaning         |
|----------+--------+---------+--------+-----------------|
| Empty    |    000 |       0 | -      | safe for none   |
|          |    001 |       1 | q      | safe for queen  |
|          |    010 |       2 | n      | safe for knight |
|          |    011 |       3 | #      | safe for both   |
|----------+--------+---------+--------+-----------------|
| Occupied |    101 |       5 | Q      | a queen         |
|          |    110 |       6 | K      | a knight        |
|----------+--------+---------+--------+-----------------|

Раньше я генерировал слишком мало решений. Теперь я рассказываю, какая клетка безопасна для королевы и рыцаря, независимо. Например, вот вывод для n = 5 с pretty-printing:

Q - - - - 
- - - n N 
- q - n n 
- # n - n 
- n # # - 

Когда мы поместили королеву Q, позиции, которые находятся в шаге от рыцаря от этой королевы, все еще безопасны для королев и обозначены q. Аналогично, рыцари, которых достигают только королевы, безопасны для других рыцарей. Значения являются побитовыми и -ed для представления возможных ходов, а некоторые ячейки достижимы ни одним видом.

Точнее, вот последовательность плат, приводящая к следующему решению (слева направо), где свободные ячейки постепенно ограничиваются различными значениями:

# # # # # #     q - - - q #     - - - - - #     - - - - - #     - - - - - n
# # # # # #     - - Q - - -     - - Q - - -     - - Q - - -     - - Q - - -
# # # # # #     q - - - q #     q - - - - -     Q - - - - -     Q - - - - -
# # # # # #     - q - q - #     - q - - - n     - - - - - n     - - - - - n
# # # # # #     # # - # # -     n n - n N -     - - - n N -     - - - - N -
# # # # # #     # # - # # #     # # - n n n     - # - - n n     - n - - n N

Нехвайнский подход

Ungolfed, прокомментированная версия

(defun queens-and-knights
    (n    ; size of problem
     fn   ; function called for each solution

     ;; AUX parameters are like LET* bindings but shorter.
     &aux
       ;; total number of cells in a board
       (s (* n n)))

  (labels
      ;; Define recursive function R
      ((R (w      ; what piece to place: 5=queen, 6=knight 
           %      ; min position for piece of type W
           @      ; min position for the other kind of piece
           b      ; current board
           !      ; T iff we are in "check" mode (see below)
           &aux  
           r      ; result of this function: will be "true" iff we can
                  ; place at least one piece of type W on the board b
           h      ; current horizontal position 
           v      ; current vertical position
           a      ; current piece at position (h,v)
           )

         (loop
            ;; only consider position U starting from position %,
            ;; because any other position below % was already visited
            ;; at a higher level of recursion (e.g. the second queen
            ;; we place is being placed in a recursive call, and we
            ;; don't visit position before the first queen).
            for u from % below s

            do
              (setf h (mod u n)         ; Intialize H, V and A
                    v (floor u n)       ; 
                    a (aref b u))       ; 

            ;; Apply an AND mask to current value A in the board
            ;; with the type of chess piece W. In order to consider
            ;; position U as "safe", the result of the bitwise AND
            ;; must be below 4 (empty cell) and non-null.
              (when (< 0 (logand a w) 4)

                ;; WE FOUND A SAFE PLACE TO PUT PIECE W

                (when (and ! (= 6 w))
                  ;; In "check" mode, when we place a knight, we knwo
                  ;; that the check is successful. In other words, it
                  ;; is possible to place an additional queen and
                  ;; knight in some board up the call stack. Instead
                  ;; of updating the board we can directly exit from
                  ;; here (that gave a major speed improvement since
                  ;; we do this a lot). Here we do a non-local exit to
                  ;; the catch named "!".
                  (throw '! t))

                ;; We make a copy of current board b and bind it to the
                ;; same symbol b. This allocates a lot of memory
                ;; compared to the previous approach where I used a
                ;; single board and an "undo" list, but it is shorter
                ;; both in code size and in runtime.
                (let ((b (copy-seq b)))

                  ;; Propagate knights' constraints
                  (loop
                     ;; O is the other kind of piece, i.e. queen here
                     ;; because be propagate knights. This is used as
                     ;; a mask to remove knights pieces as possible
                     ;; choices.
                     with o = 5

                     ;; The list below is arranged so that two
                     ;; consecutive numbers form a knight-move. The ON
                     ;; iteration keyword descend sublist by sublist,
                     ;; i.e. (-1 -2), (-2 -1), (-1 2), ..., (2 NIL). We
                     ;; destructure each list being iterated as (K D),
                     ;; and when D is NIL, we use value -1.
                     for (K D) on '(-1 -2 -1 2 1 -2 1 2)

                     ;; Compute position X, Y and index U in board,
                     ;; while checking that the position is inside the
                     ;; board.
                     for y = (+ K v)
                     for x = (+ (or D -1) h)
                     for u = (and (< -1 y n)
                                  (< -1 x n)
                                  (+(* y n)x))

                     ;; if U is a valid position...
                     if u
                     do
                     ;; The reader variable #1# is affected to the
                     ;; following expression and reused below for
                     ;; queens. That's why the expression is not
                     ;; specific to knights. The trick here is to
                     ;; use the symbols with different lexical
                     ;; bindings.
                       #1=(when (< (aref b u) 4) ; empty?
                            (setf (aref b u)

                                  (logand
                                   ;; Bitwise AND of current value ...
                                   (aref b u)

                                   ;; ... with o: position U is not a
                                   ;; safe place for W (inverse of O)
                                   ;; anymore, because if we put a W
                                   ;; there, it would attack our
                                   ;; current cell (H,V).
                                   o

                                   ;; ... and with zero (unsafe for
                                   ;; all) if our piece W is also a
                                   ;; knight (resp. queen). Indeed, we
                                   ;; cannot put anything at position
                                   ;; U because we are attacking it.
                                   (if (= w o) 3 0)))))

                  ;; Propagate queens' constraints
                  (dotimes (y N)
                    (dotimes (x N)
                      (let ((u(+(* y n)x))(o 6))
                        (if (or (= x h)
                                (= y v)
                                (= (abs(- h x)) (abs(- v y))))

                            ;; Same code as above #1=(if ...)
                            #1#))))

                  (setf
                   ;; Place piece
                   (aref b u) w

                   ;; Set result value
                   r (or (cond
                           ;; Queen? Try to place a Knight and maybe
                           ;; other queens. The result is true only if
                           ;; the recursive call is.
                           ((= w 5) (R 6 @ U b !))

                           ;; Not a queen, so all below concern   
                           ;; knights: we always return T because
                           ;; we found a safe position.
                           ;; But we still need to know if
                           ;; board B is an actual solution and 
                           ;; call FN if it is.
                           ;; ------------------------------------

                           ;; Can be place a queen too? then current
                           ;; board is not a solution.
                           ((R 5 @ U b()) t)

                           ;; Try to place a queen and a knight
                           ;; without constraining the min positions
                           ;; (% and @); this is the "check" mode that
                           ;; is represented by the last argument to
                           ;; R, set to T here. If it throws true,
                           ;; then board B is a duplicate of a
                           ;; previous one, except that it is missing
                           ;; pieces due to constraints % and @. The
                           ;; "check" mode is a fix to a bug where we
                           ;; reported as solutions boards where there
                           ;; was still room for other pieces.
                           ((catch'!(R 5 0 0 b t)) t)

                           ;; Default case: we could not add one more
                           ;; layer of pieces, and so current board B
                           ;; is a solution. Call function FN.
                           (t (funcall fn b) t))

                         ;; R keeps being true if it already was for
                         ;; another position.
                         r)))))

         ;; Return result R
         r))

    ;; Start search with a queen and an empty board.
    (R 5 0 0 (fill (make-array s) 3)  nil)))

Дубликаты и ошибки

Мое самое первое решение дало дублирующие решения. Чтобы решить эту проблему, я ввел два счетчика для королев и рыцарей. Счетчик для ферзей (соответственно, рыцарей) отслеживает первую позицию на доске, где существует ферзь (соответственно, рыцарь): я добавляю ферзь (соответственно, рыцарь) только в положениях, которые следуют за этой минимальной позицией.

Этот метод не позволяет мне пересматривать решения, которые уже были найдены в предыдущих итерациях, потому что я выполняю итерацию с возрастающей позицией ферзя (или рыцаря).

Однако Слеафар заметил, что существуют решения, для которых могут быть места для королев и рыцарей, что противоречит правилам. Некоторое время мне приходилось возвращаться к обычному поиску и хранить все известные решения для предотвращения дубликатов, которые были слишком дорогостоящими (как с точки зрения байтов, так и использования памяти).

Вместо этого вот что я делаю сейчас: когда потенциальная доска решений найдена, я пытаюсь добавить ровно одну ферзь и одну рыцаря без учета счетчиков (то есть для всех ячеек на доске). Если это возможно, то текущая доска является дубликатом предыдущей, и я отвергаю решение.

тесты

|---+---------+------------+--------------|
| N |  boards |    seconds |        bytes |
|---+---------+------------+--------------|
| 3 |       0 |          0 |        32768 |
| 4 |      40 |          0 |       360416 |
| 5 |     172 |          0 |      3440016 |
| 6 |    2836 |   0.085907 |     61251584 |
| 7 |   23876 |   1.265178 |    869666288 |
| 8 |  383586 |  24.991300 |  17235142848 |
| 9 | 6064506 | 524.982987 | 359952648832 |
|---+---------+------------+--------------|

Куайн-кация

У меня были разные идеи, чтобы делать последовательные работы. Самый простой из них, вероятно, - сначала сгенерировать все решения в виде списка строк и написать последовательные кавычки, которые появляются из этого списка при каждом поколении. Однако это не казалось короче, чем нынешний подход. В качестве альтернативы я попытался переписать рекурсивный код с помощью собственного стека и сбросить все переменные состояния каждый раз, когда нахожу решение; цель состоит в том, чтобы следующий шаг мог быть обработан как продолжение текущего шага. Может быть, это было бы лучше подходит для стекового языка. Текущая довольно проста и основана на переменных читателя Common Lisp, которые всегда интересно использовать.

CoreDump
источник