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

时间:2016-04-28 05:04:02

标签: variables scope global-variables common-lisp local-variables

eg1 - 使用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)

输出:

1 
2 *** - EVAL: variable A has no value

eg2 - 没有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)

输出:

1 
2 
1 
2 

你怎么能让他们逃脱?
我在setqdefvar左右徘徊 (通过查找关于"局部变量"的文档,我可以找到的唯一结果中提到了这一点) 但根本没有快乐

eg3 - 尝试使用宏


这是我想要解决的实际问题 -
let的语法强迫你使用额外的parens层,
并将整个函数体包裹在最外层,
这使得没有理由更难阅读和写作 (因为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)

输出:

*** - PROGN: variable A has no value

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

[
[hooks]

  

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

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

EDIT-TO-ADD eg4

考虑loop宏的工作方式 (从某人调用的角度来看,而不了解其内部,我的意思是)...... 好吧,看:

(loop for i from 1 to 5 
 do (print i))

我还不知道loop的定义是什么,
但它抽象地是这样的,对吧?:

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

(我只关注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)

我是在正确的轨道吗?

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

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

3 个答案:

答案 0 :(得分:9)

简单规则:SETFSETQ不会创建变量。无论是本地还是全球。他们只是设置变量。

永远不要使用SETQSETF设置未定义的变量。这是Common Lisp,而不是Ruby

使用宏创建SETF表单也无济于事。为什么这会产生影响?

定义局部变量

如果你研究Common Lisp标准,有很多结构允许定义局部变量:DEFUNDEFMETHODLETLET*,{{ 1}},DODOTIMESFLETLABELSLAMBDADESTRUCTURING-BIND,...

函数中的局部变量

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

  • 必要参数
  • 可选参数
  • keyword argument
  • 辅助论证
  • 休息参数

您可以使用MULTIPLE-VALUE-BINDLAMBDA,...

定义功能

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! 的示例:

&aux

LOOP中的变量

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

((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!

正如您所见,它会扩展为一个表单,其中变量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))))) i引入。它还扩展为一个表单,它使用 goto 构造。

正如您所看到的,您可以在Lisp中实现一种非常精美的语法 - 如LOOP语法。但是LOOP的实现很大而且非常重要。对初学者来说没什么。

答案 1 :(得分:1)

我碰巧喜欢在Lisp中引入绑定,我想念其他语言。 但是,我不会尝试根据自己的喜好改变它们。 相反,我遵循其他语言的特定习语。 在评论中,你问:

  

你是说我应该能够使用你游戏我的信息做到这一点,或者你是说它不可能?

事实上,Rainer Joswig告诉你,你不应该这样做,原因与你没有在C代码中引入BEGINEND宏的原因相同替换大括号:只需使用现有语言,而不是直接尝试"修复"它

顺便提一下,请理解LET的现有设计是一个意外,并按预期工作。事实上,从现代的角度来看,你看起来很奇怪" (参见OCaml / F#)并不意味着它是错误的或设计不当。我不会通过以下评论知道你的意思:

  

它可以正确地为传递给函数的参数的变量定义设置阴影和解除阴影

...但我可以告诉你,它没有多大意义。 在尝试修改Lisp之前,请先咨询Online Tutorials for programming Common Lisp以获得更好的Lisp视图。

答案 2 :(得分:0)

[编辑:
我才意识到我搞砸了争论处理, 所以我的解决方案"被打破
如果在参数列表之后没有给my-defun至少三个列表。

我把(我认为)实际工作解决方案放在最后。
]

我实际想出来了!

它根本不太难,
即使是新手。


我的意思是, 如果一个人真的试图在#34;真实的代码中使用它会发生可怕的事情,我不会感到惊讶,#34;,
因为边缘情况或其他东西,
但它是一个有效的概念验证 )

无论如何,这里是my-defun宏的定义,
正如我在eg4中所描述的那样工作:


请没人编辑奇怪的格式 -
我意识到这是非标准的, 但它真的有助于新手阅读困难的新东西 我的意思是, 如果像我这样的另一个新手读过这个,
我认为它会显着地帮助他们 )

(defmacro my-defun (name params withsymbol withlist &body body)
 (cond
   ((equal withsymbol 'with)    
                                ; (print 'withsymbol_set_to_)   ;diagnostic
                                ; (print withsymbol)            ;diagnostic
                                ; (print 'withlist_set_to_)     ;diagnostic
                                ; (print withlist)              ;diagnostic
                                ; (print                        ;diagnostic
                                 `(defun ,name ,params          
                                   (let ,withlist               
                                    (progn ,@body)              
                                   )                            
                                  )                             
                                ; )                             ;diagnostic
   )                            
   (t                           
                                ; (print 'withsymbol_not_set_to_with_but_)  ;diagnostic
                                ; (print withsymbol)                        ;diagnostic
                                ; (print 'withlist_set_to_)                 ;diagnostic
                                ; (print withlist)                          ;diagnostic
                                ; (print                                    ;diagnostic
                                 `(defun ,name ,params                      
                                    (progn ,withsymbol ,withlist ,@body)    
                                  )                                         
                                ; )                                         ;diagnostic

   )
 )
)

首次测试,with

(my-defun demo4 (x)
 with (
  (a 1)
  (b 2)
 )
 ; this prints!
 (print a)
 (print b)
 (print x)
)
(demo4 "hi")
; this correctly gets a "no value" error!
(print a)
(print b)
(print x)

输出:

1 
2 
"hi" 

输出诊断线未注释:

WITHSYMBOL_SET_TO_ 
WITH 
WITHLIST_SET_TO_ 
((A 1) (B 2)) 
(DEFUN DEMO4 (X) (LET ((A 1) (B 2)) (PROGN (PRINT A) (PRINT B) (PRINT X)))) 
1 
2 
"hi" 

第二次测试,没有with

(因此它的行为与普通defun完全相同)

(my-defun demo4 (x)
 ; (this stuff also prints)
 (print "i am not the withsymbol")
 (print "this is not the withlist")
 ; this prints!
 (print "symbol 'a would have no value")
 (print "symbol 'b would have no value")
 (print x)
)

(demo4 "hi")
; this correctly gets a "no value" error!
'(print a)
'(print b)
'(print x)

输出:

"i am not the withsymbol" 
"this is not the withlist" 
"symbol 'a would have no value" 
"symbol 'b would have no value" 
"hi" 

输出诊断线未注释:

WITHSYMBOL_NOT_SET_TO_WITH_BUT_ 
(PRINT "i am not the withsymbol") 
WITHLIST_SET_TO_ 
(PRINT "this is not the withlist") 
(DEFUN DEMO4 (X)
 (PROGN (PRINT "i am not the withsymbol") (PRINT "this is not the withlist") (PRINT "symbol 'a would have no value")
  (PRINT "symbol 'b would have no value") (PRINT X))) 
"i am not the withsymbol" 
"this is not the withlist" 
"symbol 'a would have no value" 
"symbol 'b would have no value" 
"hi" 

最不同的例子:

defunlet一起使用 并将my-defunwith一起使用 (只是想看看结果在多大程度上看起来值得麻烦xD)

(   defun demo (x)
 (let (
       (a 1)
       (b 2)
      )
  (print a)
  (print b)
  (print x)
 )
)

(my-defun demo (x)
 with (
       (a 1)
       (b 2)
      )
 (print a)
 (print b)
 (print x)
)

实际工作解决方案(I HOPE):

(defmacro fun (name params &rest rest)
 (let (
       (withsymbol  (car rest))
       (withlist    (car (cdr rest)))
       (body        (cdr (cdr rest)))
      )
  ; (p withsymbol   )   ;;debug
  ; (p withlist     )   ;;debug
  ; (p body         )   ;;debug
  (cond
   ((equal withsymbol 'with)    
                                ; (print 'BRANCH_A)     ;;debug
                                ; (print                ;;debug
                                 `(defun ,name ,params  
                                   (let* ,withlist      
                                    (progn ,@body)      
                                   )                    
                                  )                     
                                ; )                     ;;debug
   )                            
   (t                           
                                ; (print 'BRANCH_B)     ;;debug
                                ; (print                ;;debug
                                 `(defun ,name ,params  
                                    (progn ,@rest)      
                                  )                     
                                ; )                     ;;debug

   )
  )
 )
)
;; for debugging
(defmacro p (symbol)
 `(format t "~A ~A~%" ',symbol ,symbol)
)

虽然这是代码最早的工作版本, 所以也许我搞砸了它没有注意到通过不完全重命名或其他东西。

我刚刚测试的最新代码更复杂:

;; used in debug
(defmacro p (symbol)
 `(format t "~A ~A~%" ',symbol ,symbol))

(defmacro mac-or-fun (which-one name params rest)
 (let (
       (withsymbol  (car rest))
       (withlist    (car (cdr rest)))
       (body        (cdr (cdr rest)))
      )
  ; (p withsymbol   )   ;;debug
  ; (p withlist     )   ;;debug
  ; (p body         )   ;;debug
  (cond
   ((equal withsymbol 'with)    
                                ; (print 'BRANCH_A)         ;;debug
                                ; (print                    ;;debug
                                 `(,which-one ,name ,params 
                                   (let* ,withlist          
                                    (progn ,@body)          
                                   )                        
                                  )                         
                                ; )                         ;;debug
   )                            
   ((equal withsymbol 'omwith)  
                                ; (print 'BRANCH_A)         ;;debug
                                ; (print                    ;;debug
                                 `(,which-one ,name ,params 
                                   (omlet ,withlist         
                                    (progn ,@body)          
                                   )                        
                                  )                         
                                ; )                         ;;debug
   )                            
   (t                           
                                ; (print 'BRANCH_B)         ;;debug
                                ; (print                    ;;debug
                                 `(,which-one ,name ,params 
                                    (progn ,@rest)          
                                  )                         
                                ; )                         ;;debug

   )
  )
 )
)
(defmacro fun (name params &rest rest)
  `(mac-or-fun defun ,name ,params ,rest))
(defmacro mac (name params &rest rest)
  `(mac-or-fun defmacro ,name ,params ,rest))


;; for use in tests
(defun ps (&rest stringlist)
     (format t "~A~%" (eval `(concatenate 'string ,@stringlist))))
(defparameter *vs-use-count* 0)
(defmacro vs (&rest title)
 (setf *vs-use-count* (+ 1 *vs-use-count*))
 (ps "

SECTION " (write-to-string *vs-use-count*) " " (write-to-string title)  " -"
 )
)

;;;tests
(progn

(vs fun works with "with")
(fun f ()
     with ((a 1))
     (print a)
     )
(f)
(vs fun works with "nil")
(fun f ()
     ()
     )
(print(f))

(vs original fun test with "with")
(fun demo4 (x)
 with (
  (a 1)
  (b 2)
 )
 ; this prints!
 (print a)
 (print b)
 (print x)
)
(demo4 "hi")
; these would correctly gets a "no value" error!
'(print a)
'(print b)
'(print x)

(vs original fun test with no "with")
(fun demo4 (x)
 ; (this stuff also prints)
 (print "i am not the withsymbol")
 (print "this is not the withlist")
 ; this prints!
 (print "symbol 'a would have no value")
 (print "symbol 'b would have no value")
 (print x)
)

(demo4 "hi")
; these would correctly gets a "no value" error!
'(print a)
'(print b)
'(print x)




(vs mac works with "with")
(mac m ()
     with ((a 1))
     (print a)
     )
(m)

(vs mac works with "nil")
(mac m ()
     ()
     )
(print(m))
)


;;; more stuff,
;;; leading up to the macro `omlet`,
;;; which is used in `mac-or-fun`
(fun pair-up (l)
 with (
  (a        (car l)         )
  (b        (car (cdr l))   )
  (l-past-b (cdr (cdr l))   )
 )
 (cond
  (
   (equal 2 (length l))
   (list l)
  )
  (t
   (cons (list a b) (pair-up l-past-b))
  )
 )
)

(fun crack-1 (eggs)
    with (
        (paired-list    (pair-up eggs))
        (paired-list    (loop for (k v) in paired-list collect `(,k ',v)))
    )
    paired-list
)
(fun crack-2 (eggs)
    with (
        (key-name           (car eggs))
        (value-name         (car (cdr eggs)))
        (eggs               (cdr (cdr eggs)))
        (paired-list        (pair-up eggs))
        (keys-list          (loop for pair in paired-list collect (first pair)))
        (values-list        (loop for pair in paired-list collect (second pair)))
        (key-name-keys      (list key-name keys-list))
        (value-name-values  (list value-name values-list))
        (paired-list        (append paired-list (list key-name-keys value-name-values)))
        (paired-list        (loop for (k v) in paired-list collect `(,k ',v)))
    )
    paired-list
)
(fun crack (eggs)
    (if
        (and
            (equal '- (car eggs))
            (oddp (length eggs))
        )
        (crack-2 (cdr eggs))
        (crack-1 eggs)
    )
)
(mac omlet (eggs &body body)
    with ((to-let (crack eggs)))
    `(let ,to-let (progn ,@body))
)
(mac lemego (&rest eggs)
    with ((to-set (crack eggs)))
    (loop for (name value) in to-set
        do (eval `(setf ,name ,value))
    )
)


;;; more tests
(progn

(vs omlet 1)
(omlet (
    x   a
    y   b
    z   c
)
    (print  x   )
    (print  y   )
    (print  z   )
)

(vs omlet 2)
(omlet (
    - names digits
    one     1
    two     2
    three   3
)
    (print  one     )
    (print  two     )
    (print  three   )
    (print  names   )
    (print  digits  )
)


(vs fun with omwith 1)
(fun f ()
omwith (
    x   a
    y   b
    z   c
)
    (print  x   )
    (print  y   )
    (print  z   )
)
(f)

(vs fun with omwith 2)
(fun f ()
omwith (
    - names digits
    one     1
    two     2
    three   3
)
    (print  one     )
    (print  two     )
    (print  three   )
    (print  names   )
    (print  digits  )
)
(f)


(vs lemego 1)
(lemego
x   a
y   b
z   c
)
(print  x   )
(print  y   )
(print  z   )


(vs lemego 2)
(lemego
- names digits
one     1
two     2
three   3
)
(print  one     )
(print  two     )
(print  three   )
(print  names   )
(print  digits  )
)