История изменений
Исправление 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'е?