3

假设我有一个这样的程序:

(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 中探索它们。

4

2 回答 2

2

这不是在回答您最初的问题,而是在回应您关于制作自己的问题的评论。我认为这是一个非常有趣的想法,所以我进行了探索。我能够弄清楚:

假设您希望它起作用:

(define top-x 10)
(define (f)
  (for ([i 10])
    (displayln i)
    (when (= i 5)
      (pry)))) ; <= drop into a REPL here, resume after exiting REPL

第一次尝试pry

(define (pry)
  (let loop ()
    (display "PRY> ")
    (define x (read))
    (unless (or (eof-object? x) (equal? x '(unquote exit)))
      (pretty-print (eval x))
      (loop))))

这似乎有效:

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

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))))

为了得到这个参数,你需要这个咒语到你的被调试文件:

(define-namespace-anchor a)                  ; <---
(define ns (namespace-anchor->namespace a))  ; <---
(define top-x 10)
(define (f)
  (for ([i 5])
    (displayln i)
    (when (= i 2)
      (pry ns)))) ; <---

现在 REPL 可以看到并改变top-x

> (f)
0
1
2
PRY> top-x
10
PRY> (set! top-x 20)
#<void>
PRY> top-x
20
PRY> ,exit
3
4
> 

凉爽的!但它不能改变局部变量,i

> (f)
0
1
2
PRY> i
; i: undefined;
; cannot reference an identifier before its definition

射击。原因在这里解释。

您可能会想象,即使 eval 无法看到损坏的 eval-formula 中的本地绑定,但实际上必须有一个数据结构将 x 映射到 2 并将 y 映射到 3,并且您想要一种获取该数​​据结构的方法。事实上,不存在这样的数据结构;编译器可以在编译时自由地将 x 的每次使用替换为 2,因此 x 的本地绑定在运行时不存在任何具体意义。即使不能通过常量折叠消除变量,通常也可以消除变量的名称,并且保存局部值的数据结构不像从名称到值的映射。

你可能会说,好吧,但在那种情况下......

DrRacket 如何提供调试器?

据我所知,DrRacket 通过在评估程序之前注释语法来做到这一点。从drracket/gui-debugger/annotator.rkt

  ;; 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

所以我认为如果你想解决这个问题,这将是一个起点。

于 2013-11-10T03:35:19.587 回答
1

在 DrRacked IDE 中,您有一个DEBUG Q >|按钮。您可以单步执行您的程序,也可以按照您在其他语言中所说的那样执行,在您要调查的表达式处按鼠标右键,然后选择仅继续到该点一次或在该点暂停以获得断点,然后GO >按运行程序。

要检查或更改x,请将鼠标指针放在它上面并使用鼠标右键。要更改您(set! x ...)在菜单中选择。

至于语言中的 repl,您可以在其中创建自己(pry)的 repl,而在 Common Lisp 中,您可能只是发出错误信号以访问漂亮的调试器。

于 2013-11-09T15:56:03.363 回答