LINUX.ORG.RU

defun в ЛИСП


0

0

Возник вопрос про локальные объявления функций.
Есть следующий скрипт и его вывод:

(defun f1 (f com)

 (format t "~&fi. com: ~S" com)

 (defun f2 (n)
  (if (= n 0)
   (format t "~&if. com: ~S" com)
   (f2 (- n 1)))
  1)

 (f2 (funcall f)))


(defun f2 (n)

 (defun f ()
  (f1 #'(lambda () n) "x"))
 (f1 #'f "y"))

(f3 1)

# clisp ./a.lisp
fi. com: "y"
fi. com: "x"
if. com: "x"
if. com: "x"

Однако вывод при его выполнении с небольшими изменениями:

(defun f1 (f com)

 (format t "~&fi. com: ~S" com)

 (defun f2 (n)
  (format t "~&if. com: ~S" com))
  1)

 (f2 (funcall f)))


(defun f2 (n)

 (defun f ()
  (f1 #'(lambda () n) "x"))
 (f1 #'f "y"))

(f3 1)

# clisp ./a.lisp
fi. com: "y"
fi. com: "x"
if. com: "x"
if. com: "y"
★★★

Уважаемый ЛОР, прошу разъяснить, почему так получается. И как вообще работают локальные defun в ЛИСПе. Разве они не эквивалентны let+lambda?

balodja ★★★
() автор топика
Ответ на: Copy-paste error? от Dselect

Э-э-эм, а можно поподробнее? У меня при виде этих результатов складывается впечатление, что defun -- это по сути lambda+setf.

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

Второе определение f2 неправильное. Вернее, синтаксически правильное, но (f2 (funcall f)) от него оказалось оторвано и, естественно, компилятор выдаёт ошибку.

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

(defun f1 (f com)

 (format t "~&fi. com: ~S" com)

 (defun f2 (n)
  (if (= n 0)
   (format t "~&if. com: ~S" com)
   (f2 (- n 1)))
  1)

 (f2 (funcall f)))


(defun f3 (n)

 (defun f ()
  (f1 #'(lambda () n) "x"))
 (f1 #'f "y"))

(f3 1)

# clisp ./a.lisp
fi. com: "y"
fi. com: "x"
if. com: "x"
if. com: "x"

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

(defun f1 (f com)

 (format t "~&fi. com: ~S" com)

 (defun f2 (n)
  (format t "~&if. com: ~S" com))
  1)

 (f2 (funcall f)))


(defun f3 (n)

 (defun f ()
  (f1 #'(lambda () n) "x"))
 (f1 #'f "y"))

(f3 1)

# clisp ./a.lisp
fi. com: "y"
fi. com: "x"
if. com: "x"
if. com: "y"

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

> Уважаемый ЛОР, прошу разъяснить, почему так получается. И как вообще работают локальные defun в ЛИСПе. Разве они не эквивалентны let+lambda?

Область видимости ограничивается таким образом. Ни больше, не меньше.

trace добавил:

(defun f1 (f com)

  (format t "~&fi. com: ~S" com)

  (defun f2 (n)
    (if (= n 0)
	(format t "~&if. com: ~S" com)
	(f2 (- n 1)))
    1)
  (trace f2)
  (f2 (funcall f)))


(defun f3 (n)
  (defun f ()
    (f1 #'(lambda () n) "x"))
  (trace f)
  (f1 #'f "y"))

(trace f1 f3)

(f3 1)

Выводит:

  0: (F3 1)
STYLE-WARNING: redefining F in DEFUN
    1: (F1 #<CLOSURE F> "y")
fi. com: "y"
STYLE-WARNING: redefining F2 in DEFUN
      2: (F1 #<CLOSURE (LAMBDA #) {10033FB7C9}> "x")
fi. com: "x"
STYLE-WARNING: redefining F2 in DEFUN
        3: (F2 0)
          4: (F2 0)
            5: (F2 0)
              6: (F2 0)
                7: (F2 0)
                  8: (F2 0)
if. com: "x"                  8: F2 returned 1
                7: F2 returned 1
              6: F2 returned 1
            5: F2 returned 1
          4: F2 returned 1
        3: F2 returned 1
      2: F1 returned 1
      2: (F2 0)
        3: (F2 0)
          4: (F2 0)
            5: (F2 0)
              6: (F2 0)
                7: (F2 0)
if. com: "x"                7: F2 returned 1
              6: F2 returned 1
            5: F2 returned 1
          4: F2 returned 1
        3: F2 returned 1
      2: F2 returned 1
    1: F1 returned 1
  0: F3 returned 1
-----
Второй вариант (без if) выводит:
  0: (F3 1)
STYLE-WARNING: redefining F in DEFUN
    1: (F1 #<CLOSURE F> "y")
fi. com: "y"
STYLE-WARNING: redefining F2 in DEFUN
      2: (F1 #<CLOSURE (LAMBDA #) {1003536A39}> "x")
fi. com: "x"
STYLE-WARNING: redefining F2 in DEFUN
if. com: "x"      2: F1 returned 1
if. com: "y"    1: F1 returned 1
  0: F3 returned 1

mv ★★★★★
()

Возможно я чего-то не понимаю спросони, но вам нужно юзать lables.

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

Ура! Можно опять пофлудить про лисп! То, что Вы сделали - это не 
let+lambda. Происходит следующее:

во время выполнения (а не определения) функции f1 определяется 
глобальная (а не локальная) функция f2. Т.е., сколько раз вызовете 
f1, столько раз у вас заново переопределится f2, т.е. будет записано
 её новое определение. 

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

(defun f1 (f com)
  (format t "~&fi. com: ~S" com)
  (labels ((f2 (n)
             (if (= n 0)
                 (format t "~&if. com: ~S" com)
               (f2 (- n 1)))
             1))
    (f2 (funcall f))))

(defun f3 (n)
  (flet ((f ()
           (f1 (lambda () n) "x")))
    (f1 #'f "y")))

(trace f1 f3)

(f3 1)

(запускал в лиспворксе)
0 F3 > ...
  >> N : 1
  1 F1 > ...
    >> F   : #<closure (SUBFUNCTION F F3) 206914F2>
    >> COM : "y"
fi. com: "y"
    2 F1 > ...
      >> F   : #<closure (SUBFUNCTION 1 (SUBFUNCTION F F3)) 20691E12>
      >> COM : "x"
fi. com: "x"
if. com: "x"
    2 F1 < ...
      << VALUE-0 : 1
if. com: "y"
  1 F1 < ...
    << VALUE-0 : 1
0 F3 < ...
  << VALUE-0 : 1

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

Почему-то сделать аналогично с помощью lambda+let у меня не 
получилось. В какой-то момент n почему-то стал равен nil и породилась
 ошибка. Почему - не знаю, спать пора. Может, кто-нибудь это выяснит.

Код я запускал вот такой (на лиспворксе)

(defun g1 (g com)
  (format t "~&fi. com: ~S" com)
  (labels ((g2 (n) 
             (if (= n 0)
                 (format t "~&if. com: ~S" com)
               (g2 (- n 1)))))
    (g2 (funcall g))))
(defun g3 (nn)
  (let ((g (lambda () (g1 (lambda () nn) "x"))))
    (g1 g "y")))
(g3 1)
  
И получил:
fi. com: "y"
fi. com: "x"
if. com: "x"
Error: In ZEROP of (NIL) arguments should be of type NUMBER.

Кстати, строго не рекомендуется использовать лисп вот так, вызывая 
его каждый раз из командной строки. Нужно пользоваться EMACS. Даже 
при обучении это быстро окупится.

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