Scheme 如何使用当前作用域打开racket REPL?
假设我有一个这样的程序:Scheme 如何使用当前作用域打开racket REPL?,scheme,racket,Scheme,Racket,假设我有一个这样的程序: (define (foo x) (local ((define y (- x 1))) (* x y))) (foo 3) 我希望能够在第3行和第4行之间打开一个REPL,这样我就可以通过执行任意语句来探索(并可能修改)x和y的值 要在Ruby中实现这一点,我将使用等效的程序: def foo(x) lambda { y = x - 1 x * y }.call end put
(define (foo x)
(local
((define y (- x 1)))
(* x y)))
(foo 3)
我希望能够在第3行和第4行之间打开一个REPL,这样我就可以通过执行任意语句来探索(并可能修改)x和y的值
要在Ruby中实现这一点,我将使用等效的程序:
def foo(x)
lambda {
y = x - 1
x * y
}.call
end
puts (foo 3)
并通过添加对pry的调用对其进行修改,以在需要的地方提供一个范围很好的repl:
require 'pry'
def foo(x)
lambda {
y = x - 1
binding.pry
x * y
}.call
end
puts (foo 3)
为了在js中实现这一点,我将在Firebug下运行这个程序,并在第4行设置一个断点:
foo = function(x) {
return (function(){
var y = x - 1;
return x * y;
})();
};
console.log(foo(3));
然后,我可以在评估窗口中浏览内容
我能做些什么把这个放到球拍里吗?我找到的最接近的是DrScheme的调试器,但它只显示了当前作用域的所有值,据我所知,它不允许您在REPL中探索它们。在DrRacked IDE中,您有一个DEBUG Q>|按钮。您可以单步执行您的程序,也可以按其他语言中所说的操作,在要研究的表达式处按鼠标右键,然后选择“继续到此点”仅一次,或者选择“暂停到此点”以获得断点,然后按“转到>运行程序” 要检查或更改
x
,请将鼠标指针放在其上并使用鼠标右键。要更改,请在菜单中选择(设置!x…
)
至于in-language repl,您可以在那里创建自己的
(pry)
来启动repl,而在Common Lisp中,您可以发出错误信号以访问nice调试器。这不是回答您最初的问题,而是回应您关于创建自己的调试器的评论。我认为这是一个非常有趣的想法,所以我对它进行了探索。我能想到的是:
比如说,你想让它起作用:
(define top-x 10)
(define (f)
(for ([i 10])
(displayln i)
(when (= i 5)
(pry)))) ; <= drop into a REPL here, resume after exiting REPL
> (f)
0
1
2
PRY> (+ 10 10)
20
PRY> ,exit
3
4
>
这似乎有效:
(define top-x 10)
(define (f)
(for ([i 10])
(displayln i)
(when (= i 5)
(pry)))) ; <= drop into a REPL here, resume after exiting REPL
> (f)
0
1
2
PRY> (+ 10 10)
20
PRY> ,exit
3
4
>
但是,尽管它允许您访问Racket函数,如+
,但您甚至无法访问顶级变量,如top-x
:
> (f)
0
1
2
PRY> top-x
; top-x: undefined;
; cannot reference undefined identifier
> (f)
0
1
2
PRY> top-x
10
PRY> (set! top-x 20)
#<void>
PRY> top-x
20
PRY> ,exit
3
4
>
如前所述,您可以通过提供对当前名称空间的eval
访问权来获取顶级内容。因此,pry
需要一个名称空间参数:
(define (pry ns)
(let loop ()
(display "PRY> ")
(define x (read))
(unless (or (eof-object? x) (equal? x '(unquote exit)))
(pretty-print (eval x ns)) ; <---
(loop))))
酷!但是它不能改变局部变量,i
:
> (f)
0
1
2
PRY> i
; i: undefined;
; cannot reference an identifier before its definition
开枪。解释了原因
您可能会想象,即使eval无法在破损的eval公式中看到本地绑定,但实际上必须有一个数据结构将x映射到2,y映射到3,并且您需要一种获得该数据结构的方法。事实上,不存在这样的数据结构;编译器可以在编译时自由地将x的每次使用替换为2,这样x的本地绑定在运行时就不存在了。即使不能通过常数折叠消除变量,通常也可以消除变量的名称,并且保存局部值的数据结构不类似于从名称到值的映射
你可能会说,好吧,但是那样的话
DrRacket如何提供调试器?
据我所知,DrRacket是通过在评估程序之前注释语法来实现的。发件人:
因此,如果你想解决这个问题,我认为这将是一个起点。我认为这是一个很好的问题。几年前我来到了球拍网,我很困惑,几乎没有人使用像我一样的使用C++的调试器。结果证明没那么重要。1.我偶尔使用
日志调试
或跟踪
。2.我在REPL中玩一些小功能,调整并观察它们。3.这些函数大多是“功能性的”(不依赖于读取或写入外部状态)。有了这些东西,我几乎再也不需要传统的调试器了。说了这么多,像pry
这样的东西听起来确实很有趣。谢谢-我知道这些调试器功能。我特别想要REPL,因为这是我最喜欢的调试方式——特别是当x
可能是一个复杂的值,并且只需将其打印到屏幕上就可以了。很高兴听到我可能只是想做我自己的窥探。如果它还没有内置的话,我正在考虑这样做。一个有趣的练习-可能是call/cc的工作…Greg,我终于有时间跟进你的建议了,我遇到了一个障碍,无法确定我可以传递给annotate函数的确切语法。如果您感兴趣,请查看以下问题:
;; annotate-stx inserts annotations around each expression that introduces a
;; new scope: let, lambda, and function calls. These annotations reify the
;; call stack, and allows to list the current variable in scope, look up
;; their value, as well as change their value. The reified stack is accessed
;; via the CURRENT-CONTINUATION-MARKS using the key DEBUG-KEY