LINUX.ORG.RU

Протащить результат через несколько вызовов функций

 ,


0

3

Приветствую!

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

Пример:

(defun bottom (x)
 (magic-shit (random x))
 x)

(defun middle (arg)
 (let ((x (random arg)))
  (if (= x 42)
      (bottom x)
      x)))

(defun top ()
 (magic-bind (value magic-arg) (middle 999)
  (if magic-arg
      (+ magic-arg value)
      (/ value 42))))

magic-shit - нечто, устанавливающее протаскиваемое значение, в данном случае это (random 42). magic-bind - нечто, позволяющее получить это значение. Форма записи мэджиков непринципиальна, принципиально не изменять middle-функцию.

Сперва была мысль воспользоваться механизмом возвращения нескольких значений, но это не взлетит в силу того, что в общем случае под это придется подгонять middle. Еще в голове вертится слово «замыкание», не знаю, может меня от перенапряжения коротнуло просто. Написать top и множество bottom-функций в одной let-форме и замкнуться на ее переменную не получится, как еще замкнуть я не знаю... Последний вариант - глобальная переменная, да.

Какие есть в лиспе механизмы решения такой задачи?

★★★★★
Ответ на: комментарий от staseg

Просто я хочу избежать потенциальных траходромов вокруг ручного управления глобальной переменной, может у меня фобия такая с С++ осталась, не знаю.

Нету там глобальной переменной. C++'ный аналог — статические методы.

В любом случае не вижу смысла писать дополнительный код, если рантайм это сам хорошо умеет, преимуществ с него ведь ноль, верно?

Преимущество — инкапсуляция.

anonymous
()

Я очень мало писал на CL, но на Кложуре бы решал эту проблему через динамическую область видимости + обещания. Уверен, что в CL можно сделать так же.

(defn bottom [x]
  (when (bound? #'*magic*) 
    (deliver *magic* (rand x))
  x)

;;;;;;;

(defn top []
  (binding [*magic* (promise)]
    (let [value (middle 999)]
      (+ value (deref *magic*)))

Но тут опасно, если не вызвать `middle` ПЕРЕД тем, как пробуешь дерефнуть *magic*, то зависнешь навечно.

unlog1c ★★★
()

Переменные с динамической областью видимости?

antares0 ★★★★
()
Ответ на: комментарий от anonymous

Ну со стеком возиться не надо, а в остальном, та же глобальная переменная. Кто угодно и как угодно бесконтрольно может ее использовать и в хвост и в гриву.

Ты, видимо не понимаешь, что такое динамические переменные в CL и что происходит в этом коде:

(defvar *bottom-top-transfer*)

(defun top ()
 (let* ((*bottom-top-transfer* nil)
        (value (middle 999)))
  (if *bottom-top-transfer*
      (+ *bottom-top-transfer* value)
      (/ value 42))))

korvin_ ★★★★★
()
Ответ на: комментарий от staseg

Можно и без defvar:

(defun bottom (x)
  (declare (special *context*))
  (setf *context* (random x))
  x)

(defun middle (x)
  (bottom x))

(defun top ()
  (let* ((*context* nil)
         (value (middle 999)))
    (declare (special *context*))
    (values value *context*)))
CL-USER 1 > *context*

Error: The variable *CONTEXT* is unbound.
  1 (continue) Try evaluating *CONTEXT* again.
  2 Specify a value to use this time instead of evaluating *CONTEXT*.
  3 Specify a value to set *CONTEXT* to.
  4 (abort) Return to level 0.
  5 Return to top loop level 0.

Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.

CL-USER 2 : 1 > :a

CL-USER 3 > (top)
999
102

CL-USER 4 > 

Но это может только больше запутать, наверное.

korvin_ ★★★★★
()
Ответ на: комментарий от korvin_

Ты, видимо не понимаешь, что такое динамические переменные в CL и что происходит в этом коде:

Я-то понимаю. Проблема в том, что протокол доступа к *bottom-top-transfer* может измениться, тогда придется скрывать логику работы с ней за интерфейсом, но обращаться к ней «по старинке» никто не запретит.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.