Я хотел бы написать простой профайлер для схемы, который дает подсчет количества раз, когда вызывается каждая функция в программе. Я попытался переопределить команду 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
), но тогда я не могу рассчитывать или сортировать или иным образом обрабатывать имена функций.
Как написать простой профайлер для схемы?
EDIT: Вот мой простой профайлер (функция 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))
А вот это улучшенная версия функции, которая останавливает пробное деление на корень квадратный из п:
(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 за их ответы.
также нельзя установить 'set!' Имя, которое еще не было 'define'd (хотя есть, например, ['(compile-allow-set! -Undefined allow?)'] (Http: // docs.racket-lang.org/reference/eval.html?q=set!#%28def._%28%28quote._~23~25kernel%29._compile-allow-set!-undefined%29%29) в Ракетка). –
@WillNess: В схеме RnRS вы правы. Различные реализации могут или не могут позволить вам «установить!» Неопределенное имя. Например, Chez Scheme, который я обычно использую, позволяет это. – user448810