LINUX.ORG.RU

История изменений

Исправление korvin_, (текущая версия) :

Вот пример, например:

(defpackage control-flow
  (:nicknames cf)
  (:use common-lisp)
  (:export define-control-variable begin def))

(in-package control-flow)

(defvar *control-flow-dict* (make-hash-table))

(defmacro define-control-variable (var)
  (progn
    (setf (gethash var *control-flow-dict*) nil)
    `(defvar ,var nil)))

(defmacro begin (&rest body)
  (let ((bindings (maphash #'(lambda (k v) (list k v)) *control-flow-dict*)))
    `(let ,bindings
       ,@body)))

(defmacro def (name arglist docstring &rest body) ; simple case
  (unless (stringp docstring)
    (error "missing docstring"))
  (unless body
    (error "empty body"))
  `(defun ,name ,arglist
     ,docstring
     (begin
      ,@body)))

; ----------------------------------------------------------------

(defpackage control-flow-else
  (:nicknames cf-else)
  (:use cl control-flow)
  (:export *python-else-condition* py-else))

(in-package control-flow-else)

(define-control-variable *python-else-condition*)

(defmacro py-else (&rest body)
  (let ((cnd '*python-else-condition*))
    `(if ,cnd
         (progn
           (setq ,cnd nil)
           (let ((,cnd nil))
             ,@body))
       nil)))

; ----------------------------------------------------------------

(defpackage control-flow-if
  (:nicknames cf-if)
  (:use cl cf cf-else)
  (:export py-if py-elsif test-cond))

(in-package control-flow-if)

(defmacro py-if (test &rest body)
  (let ((cnd '*python-else-condition*))
    `(if ,test
         (progn
           (setq ,cnd nil)
           (let ((,cnd nil))
             ,@body))
       (setq ,cnd t))))

(defmacro py-elsif (test &rest body)
  (let ((cnd '*python-else-condition*))
    `(if ,cnd
         (if ,test
             (progn
               (setq ,cnd nil)
               (let ((,cnd nil))
                 ,@body))
           nil)
       nil)))



(def test-cond (n)
  "Pythony"
  (py-if (< n 0)
    (format t "Negative~%"))
  (py-elsif (> n 0)
    (format t "Positive~%"))
  (py-else
    (format t "Zero~%")))

; ----------------------------------------------------------------

(defpackage control-flow-while
  (:nicknames cf-while)
  (:use cl cf cf-else)
  (:export py-while test-while))

(in-package control-flow-while)

(defmacro py-while (test &rest body)
  (let ((cont  (gensym "WHILE-CONT"))
        (break (gensym "WHILE-BREAK"))
        (cnd   '*python-else-condition*))
    `(tagbody
      ,cont
      (unless ,test
        (setf ,cnd t)
        (go ,break))
      (let ((,cnd nil))
        (labels ((continue () (go ,cont))
                 (break    () (go ,break)))
          ,@body
          (go ,cont)))
      ,break)))

(defun test-while ()
  (let ((*python-else-condition* nil)
        (i 1))
    (py-while (< i 6)
      (format t "~a~%" i)
      (incf i))
    (py-else
      (format t "i is no longer less than 6~%"))))
     
; ----------------------------------------------------------------

(defpackage test
  (:use cl cf cf-else cf-if cf-while)
  (:export main))

(in-package test)

(def test-while+if ()
  "Testing while + if"
  (let ((i 1))
    (py-while (< i 6)
      (format t "~a~%" i)
      (py-if (= i 3)
        (break))
      (incf i))
    (py-else
      (format t "else. i = ~a~%" i))))

(defun main ()
  (format t "~%TEST COND:~%")
  (test-cond -3)
  (test-cond 42)
  (test-cond  0)
  (format t "~%TEST WHILE:~%")
  (test-while)
  (format t "~%TEST WHILE+IF:~%")
  (test-while+if))

Вывод:

CL-USER 6 > (test:main)

TEST COND:
Negative
Positive
Zero

TEST WHILE:
1
2
3
4
5
i is no longer less than 6

TEST WHILE+IF:
1
2
3
NIL

CL-USER 7 > 

Пробуем Питон (я смотрел онлайн тут: https://www.w3schools.com/python/python_while_loops.asp ) и видим:

1:

i = 1
while i < 6:
  print(i)
  i += 1
else:
  print("i is no longer less than 6")
=>
1
2
3
4
5
i is no longer less than 6

2:

i = 1
while i < 6:
  print(i)
  if i == 3:
  	break
  i += 1
else:
  print("i is no longer less than 6")
=>
1
2
3

Один-в-один, только без NIL.

Конечно, в этом примере на CL есть нюансы: простой if, без else оставит в переменной *python-else-condition* значение T, если условие будет ложным. И какой-нибудь одинокий else может потом выполниться, если его написать. Да и в целом, else можно где угодно написать.

Но я так понимаю, такое поведение как раз ближе к Forth, чем (к) Python. Собственно, что вам мешает в этой condition-переменной хранить стек, а функциями манипулировать с ним, то есть сделать этакий embedded-интерпретатор Forth и делать то, что вы хотели (могли) делать на Forth'е, но не могли на CL?

И заметьте, функция test-while определена через обычный defun и без begin.

Исправление korvin_, :

Вот пример, например:

(defpackage control-flow
  (:nicknames cf)
  (:use common-lisp)
  (:export define-control-variable begin def))

(in-package control-flow)

(defvar *control-flow-dict* (make-hash-table))

(defmacro define-control-variable (var)
  (progn
    (setf (gethash var *control-flow-dict*) nil)
    `(defvar ,var nil)))

(defmacro begin (&rest body)
  (let ((bindings (maphash #'(lambda (k v) (list k v)) *control-flow-dict*)))
    `(let ,bindings
       ,@body)))

(defmacro def (name arglist docstring &rest body) ; simple case
  (unless (stringp docstring)
    (error "missing docstring"))
  (unless body
    (error "empty body"))
  `(defun ,name ,arglist
     ,docstring
     (begin
      ,@body)))

; ----------------------------------------------------------------

(defpackage control-flow-else
  (:nicknames cf-else)
  (:use cl control-flow)
  (:export *python-else-condition* py-else))

(in-package control-flow-else)

(define-control-variable *python-else-condition*)

(defmacro py-else (&rest body)
  (let ((cnd '*python-else-condition*))
    `(if ,cnd
         (progn
           (setq ,cnd nil)
           (let ((,cnd nil))
             ,@body))
       nil)))

; ----------------------------------------------------------------

(defpackage control-flow-if
  (:nicknames cf-if)
  (:use cl cf cf-else)
  (:export py-if py-elsif test-cond))

(in-package control-flow-if)

(defmacro py-if (test &rest body)
  (let ((cnd '*python-else-condition*))
    `(if ,test
         (progn
           (setq ,cnd nil)
           (let ((,cnd nil))
             ,@body))
       (setq ,cnd t))))

(defmacro py-elsif (test &rest body)
  (let ((cnd '*python-else-condition*))
    `(if ,cnd
         (if ,test
             (progn
               (setq ,cnd nil)
               (let ((,cnd nil))
                 ,@body))
           nil)
       nil)))



(def test-cond (n)
  "Pythony"
  (py-if (< n 0)
    (format t "Negative~%"))
  (py-elsif (> n 0)
    (format t "Positive~%"))
  (py-else
    (format t "Zero~%")))

; ----------------------------------------------------------------

(defpackage control-flow-while
  (:nicknames cf-while)
  (:use cl cf cf-else)
  (:export py-while test-while))

(in-package control-flow-while)

(defmacro py-while (test &rest body)
  (let ((cont  (gensym "WHILE-CONT"))
        (break (gensym "WHILE-BREAK"))
        (cnd   '*python-else-condition*))
    `(tagbody
      ,cont
      (unless ,test
        (setf ,cnd t)
        (go ,break))
      (let ((,cnd nil))
        (labels ((continue () (go ,cont))
                 (break    () (go ,break)))
          ,@body
          (go ,cont)))
      ,break)))

(defun test-while ()
  (let ((*python-else-condition* nil)
        (i 1))
    (py-while (< i 6)
      (format t "~a~%" i)
      (incf i))
    (py-else
      (format t "i is no longer less than 6~%"))))
     
; ----------------------------------------------------------------

(defpackage test
  (:use cl cf cf-else cf-if cf-while)
  (:export main))

(in-package test)

(def test-while+if ()
  "Testing while + if"
  (let ((i 1))
    (py-while (< i 6)
      (format t "~a~%" i)
      (py-if (= i 3)
        (break))
      (incf i))
    (py-else
      (format t "else. i = ~a~%" i))))

(defun main ()
  (format t "~%TEST COND:~%")
  (test-cond -3)
  (test-cond 42)
  (test-cond  0)
  (format t "~%TEST WHILE:~%")
  (test-while)
  (format t "~%TEST WHILE+IF:~%")
  (test-while+if))

Вывод:

CL-USER 6 > (test:main)

TEST COND:
Negative
Positive
Zero

TEST WHILE:
1
2
3
4
5
i is no longer less than 6

TEST WHILE+IF:
1
2
3
NIL

CL-USER 7 > 

Пробуем Питон (я смотрел онлайн тут: https://www.w3schools.com/python/python_while_loops.asp ) и видим:

1:

i = 1
while i < 6:
  print(i)
  i += 1
else:
  print("i is no longer less than 6")
=>
1
2
3
4
5
i is no longer less than 6

2:

i = 1
while i < 6:
  print(i)
  if i == 3:
  	break
  i += 1
else:
  print("i is no longer less than 6")
=>
1
2
3

Один-в-один, только без NIL.

Конечно, в этом примере на CL есть нюансы: простой if, без else оставит в переменной *python-else-condition* значение T, если условие будет ложным. И какой-нибудь одинокий else может потом выполниться, если его написать. Да и в целом, else можно где угодно написать.

Но я так понимаю, такое поведение как раз ближе к Forth, чем (к) Python. Собственно, что вам мешает в этой condition-переменной хранить стек, а функциями манипулировать с ним, то есть сделать этакий embedded-интерпретатор Forth и делать то, что вы хотели (могли) делать на Forth'е?

И заметьте, функция test-while определена через обычный defun и без begin.

Исходная версия korvin_, :

Вот пример, например:

(defpackage control-flow
  (:nicknames cf)
  (:use common-lisp)
  (:export define-control-variable begin def))

(in-package control-flow)

(defvar *control-flow-dict* (make-hash-table))

(defmacro define-control-variable (var)
  (progn
    (setf (gethash var *control-flow-dict*) nil)
    `(defvar ,var nil)))

(defmacro begin (&rest body)
  (let ((bindings (maphash #'(lambda (k v) (list k v)) *control-flow-dict*)))
    `(let ,bindings
       ,@body)))

(defmacro def (name arglist docstring &rest body) ; simple case
  (unless (stringp docstring)
    (error "missing docstring"))
  (unless body
    (error "empty body"))
  `(defun ,name ,arglist
     ,docstring
     (begin
      ,@body)))

; ----------------------------------------------------------------

(defpackage control-flow-else
  (:nicknames cf-else)
  (:use cl control-flow)
  (:export *python-else-condition* py-else))

(in-package control-flow-else)

(define-control-variable *python-else-condition*)

(defmacro py-else (&rest body)
  (let ((cnd '*python-else-condition*))
    `(if ,cnd
         (progn
           (setq ,cnd nil)
           (let ((,cnd nil))
             ,@body))
       nil)))

; ----------------------------------------------------------------

(defpackage control-flow-if
  (:nicknames cf-if)
  (:use cl cf cf-else)
  (:export py-if py-elsif test-cond))

(in-package control-flow-if)

(defmacro py-if (test &rest body)
  (let ((cnd '*python-else-condition*))
    `(if ,test
         (progn
           (setq ,cnd nil)
           (let ((,cnd nil))
             ,@body))
       (setq ,cnd t))))

(defmacro py-elsif (test &rest body)
  (let ((cnd '*python-else-condition*))
    `(if ,cnd
         (if ,test
             (progn
               (setq ,cnd nil)
               (let ((,cnd nil))
                 ,@body))
           nil)
       nil)))



(def test-cond (n)
  "Pythony"
  (py-if (< n 0)
    (format t "Negative~%"))
  (py-elsif (> n 0)
    (format t "Positive~%"))
  (py-else
    (format t "Zero~%")))

; ----------------------------------------------------------------

(defpackage control-flow-while
  (:nicknames cf-while)
  (:use cl cf cf-else)
  (:export py-while test-while))

(in-package control-flow-while)

(defmacro py-while (test &rest body)
  (let ((cont  (gensym "WHILE-CONT"))
        (break (gensym "WHILE-BREAK"))
        (cnd   '*python-else-condition*))
    `(tagbody
      ,cont
      (unless ,test
        (setf ,cnd t)
        (go ,break))
      (let ((,cnd nil))
        (labels ((continue () (go ,cont))
                 (break    () (go ,break)))
          ,@body
          (go ,cont)))
      ,break)))

(defun test-while ()
  (let ((*python-else-condition* nil)
        (i 1))
    (py-while (< i 6)
      (format t "~a~%" i)
      (incf i))
    (py-else
      (format t "i is no longer less than 6~%"))))
     
; ----------------------------------------------------------------

(defpackage test
  (:use cl cf cf-else cf-if cf-while)
  (:export main))

(in-package test)

(def test-while+if ()
  "Testing while + if"
  (let ((i 1))
    (py-while (< i 6)
      (format t "~a~%" i)
      (py-if (= i 3)
        (break))
      (incf i))
    (py-else
      (format t "else. i = ~a~%" i))))

(defun main ()
  (format t "~%TEST COND:~%")
  (test-cond -3)
  (test-cond 42)
  (test-cond  0)
  (format t "~%TEST WHILE:~%")
  (test-while)
  (format t "~%TEST WHILE+IF:~%")
  (test-while+if))

Вывод:

CL-USER 6 > (test:main)

TEST COND:
Negative
Positive
Zero

TEST WHILE:
1
2
3
4
5
i is no longer less than 6

TEST WHILE+IF:
1
2
3
NIL

CL-USER 7 > 

Пробуем Питон (я смотрел онлайн тут: https://www.w3schools.com/python/python_while_loops.asp ) и видим:

1:

i = 1
while i < 6:
  print(i)
  i += 1
else:
  print("i is no longer less than 6")
=>
1
2
3
4
5
i is no longer less than 6

2:

i = 1
while i < 6:
  print(i)
  if i == 3:
  	break
  i += 1
else:
  print("i is no longer less than 6")
=>
1
2
3

Один-в-один, только без NIL.

Конечно, в этом примере на CL есть нюансы: простой if, без else оставит в переменной *python-else-condition* значение T, если условие будет ложным. И какой-нибудь одинокий else может потом выполниться, если его написать. Да и в целом, else можно где угодно написать.

Но я так понимаю, такое поведение как раз ближе к Forth, чем (к) Python. Собственно, что вам мешает в этой condition-переменной хранить стек, а функциями манипулировать с ним, то есть сделать этакий embedded-интерпретатор Forth и делать то, что вы хотели (могли) делать на Forth'е?