我想为Scheme编写一个简单的分析器,它可以计算程序中每个函数的调用次数。我试图像这样重新定义define
命令(最终我将添加其他形式的define,但是现在我只是想编写概念验证代码):
(define-syntax define
(syntax-rules ()
((define (name args ...) body ...)
(set! name
(lambda (args ...)
(begin
(set! *profile* (cons name *profile*))
body ...))))))
我的想法是在列表中*profile*
每次调用一个函数,然后再检查列表并确定函数计数。这可以工作,但是存储函数本身(即函数名的可打印表示,在Chez Scheme中#<procedure f>
用于名为f
的函数,但是我无法计算或排序或以其他方式处理函数名称。
如何为Scheme编写简单的探查器?
编辑:这是我的简单探查器(uniq-c
函数计算列表中的相邻重复项来自我的Standard Prelude):
(define *profile* (list))
(define (reset-profile)
(set! *profile* (list)))
(define-syntax define-profiling
(syntax-rules ()
((_ (name args ...) body ...)
(define (name args ...)
(begin
(set! *profile*
(cons 'name *profile*))
body ...)))))
(define (profile)
(uniq-c string=?
(sort string<?
(map symbol->string *profile*)))))
作为一个简单的演示,这里是一个通过试验分区识别素数的函数。函数divides?
分开,因为探查器只计算函数调用,而不是单个语句。
(define-profiling (divides? d n)
(zero? (modulo n d)))
(define-profiling (prime? n)
(let loop ((d 2))
(cond ((= d n) #t)
((divides? d n) #f)
(else (loop (+ d 1))))))
(define-profiling (prime-pi n)
(let loop ((k 2) (pi 0))
(cond ((< n k) pi)
((prime? k) (loop (+ k 1) (+ pi 1)))
(else (loop (+ k 1) pi)))))
> (prime-pi 1000)
168
> (profile)
(("divides?" . 78022) ("prime-pi" . 1) ("prime?" . 999))
这是该函数的改进版本,它在 n 的平方根处停止试验分割:
(define-profiling (prime? n)
(let loop ((d 2))
(cond ((< (sqrt n) d) #t)
((divides? d n) #f)
(else (loop (+ d 1))))))
> (reset-profile)
> (prime-pi 1000)
168
> (profile)
(("divides?" . 5288) ("prime-pi" . 1) ("prime?" . 999))
我将在my blog进行有关分析的更多内容。感谢@uselpa和@GoZoner的回答。
答案 0 :(得分:2)
这是实现它的一种示例方式。它用Racket编写,但很容易转换为你的Scheme方言。
没有语法
让我们先尝试不使用宏。
这是个人资料程序:
(define profile
(let ((cache (make-hash))) ; the cache memorizing call info
(lambda (cmd . pargs) ; parameters of profile procedure
(case cmd
((def) (lambda args ; the function returned for 'def
(hash-update! cache (car pargs) add1 0) ; prepend cache update
(apply (cadr pargs) args))) ; call original procedure
((dmp) (hash-ref cache (car pargs))) ; return cache info for one procedure
((all) cache) ; return all cache info
((res) (set! cache (make-hash))) ; reset cache
(else (error "wot?")))))) ; unknown parameter
以及如何使用它:
(define test1 (profile 'def 'test1 (lambda (x) (+ x 1))))
(for/list ((i 3)) (test1 i))
=> '(1 2 3)
(profile 'dmp 'test1)
=> 3
添加语法
(define-syntax define!
(syntax-rules ()
((_ (name args ...) body ...)
(define name (profile 'def 'name (lambda (args ...) body ...))))))
(define! (test2 x) (* x 2))
(for/list ((i 4)) (test2 i))
=> '(0 2 4 6)
(profile 'dmp 'test2)
=> 4
转储全部:
(profile 'all)
=> '#hash((test2 . 4) (test1 . 3))
编辑应用于您的上一个示例:
(define! (divides? d n) (zero? (modulo n d)))
(define! (prime? n)
(let loop ((d 2))
(cond ((< (sqrt n) d) #t)
((divides? d n) #f)
(else (loop (+ d 1))))))
(define! (prime-pi n)
(let loop ((k 2) (pi 0))
(cond ((< n k) pi)
((prime? k) (loop (+ k 1) (+ pi 1)))
(else (loop (+ k 1) pi)))))
(prime-pi 1000)
=> 168
(profile 'all)
=> '#hash((divides? . 5288) (prime-pi . 1) (prime? . 999))
答案 1 :(得分:2)
更改您的行:
(set! *profile* (cons name *profile*))
到
(set! *profile* (cons 'name *profile*))
name
函数正文中name
的评估是name
的过程。通过引用来避免评估,并留下符号/标识符。正如您所希望的那样,您的*profile*
变量将成为一个不断增长的列表,每个函数调用都有一个符号。您可以计算给定名称的出现次数。