你可以在没有'let`的情况下创建局部变量吗?

Owe*_*n_R 0 variables scope global-variables common-lisp local-variables

eg1 - 使用a let

(defun demo1 ()
 (let ((a 1)
       (b 2))
  ; these print fine
  (print a)
  (print b)))

(demo1)
; these get "no value" errors, as expected
(print a)
(print b)
Run Code Online (Sandbox Code Playgroud)

输出:

1 
2 *** - EVAL: variable A has no value
Run Code Online (Sandbox Code Playgroud)

eg2 - 没有a let,变量逃脱

(对于习惯于更现代的范围规则的人来说,这是非常令人惊讶的,例如ruby's)

(defun demo2 ()
 (setf a 1)
 (setf b 2)
 ; these print fine
 (print a)
 (print b))

(demo2)
; these escape, and also print with no error!
(print a)
(print b)
Run Code Online (Sandbox Code Playgroud)

输出:

1 
2 
1 
2 
Run Code Online (Sandbox Code Playgroud)

你怎么可以让他们无法逃脱呢?
我monkey'd周围setqdefvar
(这是在唯一的结果,我可以通过查找文档上的"局部变量"找到提到)
,但没有喜悦可言

eg3 - 尝试使用宏

(
这是我想要首先解决的实际问题 - 强制你使用额外的parens层
的语法let,
并将整个函数体包裹在最外层,
这使得它更难以读取和写入没有理由
(因为绝大多数最常见的用例let
总是包含整个函数体而没有别的),
所以我想制作一个with宏而不是
)

(defmacro with (&rest list-of-pairs)
 (loop for (name value) in list-of-pairs
    do  `(setf ,name ,value)    ;A
  ; do  (setf name value)       ;B
  ; (i tried both A and B)
 ))

(defun demo3 ()
 (with (a 1)
       (b 2))
 ; this gets a "no value" error when called
 (print a)
 (print b))

(demo3)
; it never even gets to this point cuz of the above error
(print a)
(print b)
Run Code Online (Sandbox Code Playgroud)

输出:

*** - PROGN: variable A has no value
Run Code Online (Sandbox Code Playgroud)

如何让变量转移到函数范围而不是超出?

[
这个问题问道

任何人都可以告诉我如何在lisp中定义局部变量而不是let?

但没有一个答案对我有帮助
]

EDIT-TO-ADD eg4

思考方式的loop宏观作品
(但从人的角度呼唤它不理解它的内部,我的意思)......
好了,请看:

(loop for i from 1 to 5 
 do (print i))
Run Code Online (Sandbox Code Playgroud)

我还不知道loop看起来是什么样的定义,
但它是抽象的类似的东西,对吧?:

(defmacro loop ([args somehow,
                 including the `sexpr`
                 which goes after the `do` keyword in the macro call])
 [other stuff]
 do ([sexpr])
 [other stuff])
Run Code Online (Sandbox Code Playgroud)

(我只关注do关键字作为一个例子,因为调用的语法相对简单.)

所以我真正需要做的是制作我自己的my-defun
并包含一个with关键字,
对吗?

这样的事情:

(defmacro my-defun ([args somehow,
                     including a `paired-list-of-names-and-values`
                     to go after a `with` keyword in the macro call])
 (let
  ([paired-list-of-names-and-values])
  ([function body])))

(my-defun demo4 ()
 with (
 (a 1)
 (b 2)
 )
 ; this should print
 (print a)
 (print b))

(demo4)
; this should get a "no value" error
(print a)
(print b)
Run Code Online (Sandbox Code Playgroud)

我在这里走在正确的轨道上吗?

如果是的话,我从哪里开始?

比如,什么是一些简单,直接的宏定义,我可以看看它们是如何工作的?
或类似的东西

Rai*_*wig 9

简单的规则:SETF或者SETQ不创建变量.无论是本地还是全球.他们只是设置变量.

永远不要使用SETQ和设置未定义的变量SETF.这是Common Lisp,不是Ruby.

SETF使用宏创建表单也没有用.为什么这会有所作为?

定义局部变量

如果你看看Common Lisp的标准,还有数不胜数的结构允许定义局部变量:DEFUN,DEFMETHOD,LET,LET*,DO,DOTIMES,FLET,LABELS,LAMBDA,DESTRUCTURING-BIND,MULTIPLE-VALUE-BIND,...

函数中的局部变量

如果查看函数,函数的参数列表允许您定义局部变量:

  • 必要的论点
  • 可选参数
  • 关键字参数
  • 辅助论证
  • 休息论点

您可以定义功能LAMBDA,DEFUN...

示例&optional:

((lambda (a &optional (b 20))
   ... ; a and b are known here inside the function
 )
 10)   ; we don't need to pass an arg for `b`, optional!
Run Code Online (Sandbox Code Playgroud)

示例&aux:

((lambda (a &aux (b (+ a 20)))
   ... ; a and b are known here inside the function
 )
 10)   ; we CAN't pass an arg for `b`, auxiliary!
Run Code Online (Sandbox Code Playgroud)

LOOP中的变量

你不需要猜测LOOP宏的作用,你可以让Lisp向你展示 - 这里使用LispWorks:

CL-USER 27 > (pprint (macroexpand '(loop for i from 1 to 5 
                                         do (print i))))

(BLOCK NIL
  (MACROLET ((LOOP-FINISH () '(GO #:|end-loop-82961|)))
    (LET ((I 1)
          (#:|to-82964| 5)
          (#:|by-82965| 1))
      (TAGBODY (PROGN (SYSTEM::INTERNAL-IF (OR (> I #:|to-82964|))
                        (GO #:|end-loop-82961|)))
       #:|begin-loop-82960|
       NIL
       (PRINT I)
       (PROGN
         (LET ((#:|temp-82966| (+ I #:|by-82965|)))
           (SETQ I #:|temp-82966|))
         (SYSTEM::INTERNAL-IF (OR (> I #:|to-82964|))
           (GO #:|end-loop-82961|)))
       (GO #:|begin-loop-82960|)
       #:|end-loop-82961|
       (RETURN-FROM NIL NIL)))))
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,它扩展为一个表单,其中变量i由a引入LET.它还扩展为一个使用goto构造的形式.

如您所见,您可以在Lisp中实现一种非常精美的语法 - 如LOOP语法.但是LOOP的实现很大而且非平凡.对于初学者来说没什么.

  • @Owen_R:您可以在 Lisp 之上定义自己的个人编程语言。但这不是初学者的主题。 (2认同)
  • @Owen_R:Common Lisp专为高效编译而设计,具有快速执行的可能性.它还被设计为只有一小组核心的最小内置语言元素(特殊运算符).另一个设计规则:语言元素应该最好地包含它们具有效果的代码.这使得处理更容易.在一系列表单中间引入变量并不是很有趣.它使代码更难以阅读,处理和编译. (2认同)