Para quem não conhece, um doctest é basicamente uma longa string em
Python que reproduz as entradas e saídas uma sessão interativa que
demonstra a funcionalidade uma API. Elem de ser um mecanismo de testes
automatizados, o doctest serve para assegurar que exemplos embutidos
na documentação são válidos.
Como Lisp sempre teve seu REPL, eu imagino que alguém alguma vez já
teve essa idéia no mundo Lisp. Alguém conhece uma implementação desse
tipo de teste em Lisp ou Scheme?
Ultimamente tenho usado doctests não só como programador mas também
como instrutor: apresento o mecanismo para os alunos logo na primeira
aula, e a partir daí eu uso este formato para os exercícios. Vejam
dois exemplos de exercícios feitos na base de doctest:
http://bitbucket.org/ramalho/propython/src/tip/fundamentos/exercicios/sequencias.py
http://bit.ly/kGfJh
http://bitbucket.org/ramalho/propython/src/tip/fundamentos/exercicios/fun_dict.py
http://bit.ly/cWvox
[ ]s
Luciano
On Fri, 10 Jul 2009 00:00:57 -0300 Luciano Ramalho <luc...@ramalho.org> wrote:
> Hoje eu comecei a fazer os exercícios do livro e senti falta de ter em
> Scheme um arcabouço de testes automatizados bem simples que existe em
> Python, chamado doctest.
>
> Para quem não conhece, um doctest é basicamente uma longa string em
> Python que reproduz as entradas e saídas uma sessão interativa que
> demonstra a funcionalidade uma API. Elem de ser um mecanismo de testes
> automatizados, o doctest serve para assegurar que exemplos embutidos
> na documentação são válidos.
Se entendi direito como funciona o doctest de Python, a implementação em
Scheme ficaria um pouco confusa.
Exemplo:
(define (proc)
"(+ 3 4)
> 7
")
A string no corpo de `proc' é o teste ou o que será produzido por `proc'
quando for avaliado?
Um abraço.
Mario
Vamos por enquanto deixar de lado como o doctest se integra ao
código-fonte. Essa questão não é essencial porque também é possível
executar doctests em um arquivo de texto arbitrário, que não é um
arquivo .py válido. Ou seja, um arquivo que é do começo ao fim um log
de interações.
O que é essencial é o mecanismo de analisar uma string onde algumas
linhas começam com um sinal especial (tipo ">", que é o prompt do REPL
do DrScheme, por exemplo). Cada linha de entrada marcada com ">" é
avaliada como se fosse digitada no prompt do REPL, e o resultado da
avaliação é comparado com o conteúdo da(s) proxima(s) linha(s) do
doctest.
Para dar um exemplo concreto, um doctest equivalente ao exercício 1.1
poderia ser assim:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> 10
10
> (+ 5 3 4)
12
> (- 9 1)
8
> (/ 6 2)
3
> (+ (* 2 4) (- 4 6))
6
> (define a 3)
> (define b (+ a 1)
> (+ a b (* a b))
19
> (= a b)
#f
> (if (and (> b a) (< b (* a b)))
b
a) ; multiline sexps are OK, result follows last right-paren
4
> (cond ((= a 4) 6)
((= b 4) (+ 6 7 a))
(else 25))
16
> (+ 2 (if (> b a) b a))
6
> (* (cond ((> a b) a)
((< a b) b)
(else -1))
(+ a 1))
16
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[ ]s
Luciano
(:> (foo bar baz)
result)
?
Fica um pouquinho menos limpo, mas dá pra implementar direto no
scheme, não? Pelo pouco que eu sei o T> teria q ser uma macro ou então
a expressão avaliada teria que ser passada como um quote, i.e. '(foo
bar baz).
Bom, escolhi o :> só pq o > já existe, mas poderia ser outro. Uma
implementação tosquíssima usando quote pra passar a expressão seria
assim:
(define (:> expr result)
(if (equal? (eval expr) result)
(print "OK")
(error "Test failed!")))
Testando no cli:
#;3> (:> '(+ 4 5) 9)
OK
#;4> (:> '(+ 4 5) 8)
Error: Test failed!
Call history:
<syntax> (:> (quote (+ 4 5)) 8)
<syntax> (quote (+ 4 5))
<eval> (:> (quote (+ 4 5)) 8)
<eval> [:>] (equal? (eval expr) result)
<eval> [:>] (eval expr)
<syntax> (+ 4 5)
<eval> (+ 4 5)
<eval> [:>] (error "Test failed!") <--
--
Kao Cardoso Félix
Página pessoal: http://www.inf.ufrgs.br/~kcfelix
Blog: http://kaofelix.blogspot.com
On Fri, 10 Jul 2009 14:07:01 -0300 Kao Cardoso Felix <kcf...@gmail.com> wrote:
> E se fizesse algo do tipo
>
> (:> (foo bar baz)
> result)
>
> ?
>
> Fica um pouquinho menos limpo, mas dá pra implementar direto no
> scheme, não? Pelo pouco que eu sei o T> teria q ser uma macro ou então
> a expressão avaliada teria que ser passada como um quote, i.e. '(foo
> bar baz).
Algo semelhante a isso já existe (é o que costumo usar):
http://chicken.wiki.br/eggref/4/test
Um abraço.
Mario
Duvidava mesmo que minha idéia fosse super original :)
Eu acho legal essa sintaxe pra criar testes. Talvez não seja tão sexy
qto os doctests que realmente parecem exemplos de uso, mas já é bem
limpa.
Bom, e tendo essas macros de teste disponíveis é só questão de fazer
um parsing da sintaxe sugerida pelo Ramalho e criar testes como esse,
não? Parece razoavelmente fácil, mas meu scheme-fu ainda é mto baixo
pra tentar algo assim...
Tentei ver tb se o scheme tinha docstrings, mas pelas minhas buscas
rápidas parece q não... Alguém confirma?
Eu também tenho usado algo semelhante no PLT-Scheme (linguagem PLAI),
veja este código:
http://code.google.com/p/eipc/source/browse/pessoal/lr/exerc/exc_1_1.scm
Este outro é um exemplo de uso mais típico:
http://code.google.com/p/eipc/source/browse/pessoal/lr/exerc/exc_1_3.scm
Mas acho que valeria a pena o esforço de tirar a sintaxe adicional
para deixar a aparência do teste idêntica ao log de uma interação com
o REPL, ou se não idêndica, mais próxima. Usar >> seria OK, por
exemplo.
[ ]s
Luciano
Eu também fiz umas buscas rápidas e não achei.
Mas achei esta página [1] que dá uma forte pista de que pelo menos no
início de 2008 não existia algo semelhante.
[1] http://4.flowsnake.org/archives/45
Para quem não conhece, o Ian Bicking, que sugere "Write doctest for
Scheme!" é um programador extremamente prolífico e influente na
comunidade Python internacional. Ele diz que lá que insiste nisso
frequentemente, e até explica porque seria muito mais fácil fazer
doctests funcionar em Scheme do que em Python.
[ ]s
Luciano
> 2009/7/10 Mario Domenech Goulart <mario....@gmail.com>:
>> Algo semelhante a isso já existe (é o que costumo usar):
>> http://chicken.wiki.br/eggref/4/test
>
> Duvidava mesmo que minha idéia fosse super original :)
> Eu acho legal essa sintaxe pra criar testes. Talvez não seja tão sexy
> qto os doctests que realmente parecem exemplos de uso, mas já é bem
> limpa.
Há também http://chicken.wiki.br/eggref/3/test-infrastructure
> Bom, e tendo essas macros de teste disponíveis é só questão de fazer
> um parsing da sintaxe sugerida pelo Ramalho e criar testes como esse,
> não? Parece razoavelmente fácil, mas meu scheme-fu ainda é mto baixo
> pra tentar algo assim...
>
> Tentei ver tb se o scheme tinha docstrings, mas pelas minhas buscas
> rápidas parece q não... Alguém confirma?
Para Scheme "puro" não existe mas, obviamente, há hacks não muito
bonitos para isso (exemplo ao final de
http://chicken.wiki.br/eggref/3/procedure-decoration).
Assim como no caso de doctests, docstrings seriam ambíguas.
(define (proc)
"Isto eh a string que representa o valor
da avaliacao de proc ou uma docstring?")
Em http://www.cs.aau.dk/~normark/schemedoc/ há uma implementação que
retira documentação de comentários.
Um abraço.
Mario
Só por curiosidade testei no emacs lisp que tem docstring e ele trata
como se fosse o valor de avaliação:
(defun foo ()
"bar")
(foo)
-> "bar"
Já nesse caso ele entende que a primeira string é a docstring:
(defun foo ()
"bar"
"baz")
(foo)
-> "baz"
Não existe essa ambiguidade no doctest em Python, e não precisaria
existir no doctest em Scheme, Mario. Acho que você não entendeu que a
docstring é primariamente texto para humanos, e não representa
qualquer tipo de interação com a linguagem de programação. Agora,
dentro da docstring podem existir trechos que seguem uma sintaxe
específica. A sintaxe é que quando uma linha começa com o marcador >>>
então ela pode ser avaliada como se fosse uma interação com no REPL, e
quando ela é avaliada o resultado é comparado com as linhas seguintes,
até o próximo >>> ou até o final do trecho de igual endentação. Vamos
deixar de lado por hora a questão da endentação. Caso hipoteticamente
existissem doctests embutidos em código Scheme, eles poderiam ser
assim:
(define (proc)
"Este proc elimina a fome no mundo. Veja como se usa:
> (proc)
451415256 pratos de feijoada distribuidos
"
(corpo-do-proc)
)
Para entender melhor, dá uma lida neste resumo que eu fiz para os meus alunos:
http://bitbucket.org/ramalho/propython/src/tip/fundamentos/exercicios/intro_doctest.py
ou http://bit.ly/RAPjS
Porém, eu volto a frisar: nem acho que seja uma característica
essencial dos doctests eles serem embutidos no código-fonte, pois
muitas vezes em grandes projetos Python (como o Zope) muitos se não a
maioria dos doctests ficam em arquivos à parte, que são doctest do
início ao fim, e que são lidos por um test-runner especial.
Então eu não me importaria de fazer
(doctest "meus-testes.txt")
para executar os testes do arquivo meus-testes.txt e gerar um relatório.
[ ]s
Luciano
On Fri, 10 Jul 2009 14:50:56 -0300 Luciano Ramalho <ram...@gmail.com> wrote:
> 2009/7/10 Mario Domenech Goulart <mario....@gmail.com>:
>> Para Scheme "puro" não existe mas, obviamente, há hacks não muito
>> bonitos para isso (exemplo ao final de
>> http://chicken.wiki.br/eggref/3/procedure-decoration).
>>
>> Assim como no caso de doctests, docstrings seriam ambíguas.
>>
>> (define (proc)
>> "Isto eh a string que representa o valor
>> da avaliacao de proc ou uma docstring?")
>
> Não existe essa ambiguidade no doctest em Python, e não precisaria
> existir no doctest em Scheme, Mario.
Em Python realmente não existe ambiguidade, pois Python exige um
`return' explícito para que uma função retorne um valor. Em Scheme não
há return explícito (*), então, se o primeiro valor do corpo de um
procedimento também for o último, ele será o resultado da avaliação.
Neste caso, não há como distiguir entre uma docstring e algo relevante
para o código "executável" do programa.
(*) a menos que se use um procedimento de escape de uma continuação --
mas isso é outra história (exemplo em
http://call-with-hopeless-continuation.blogspot.com/2009/03/definicao-de-procedimentos-e-return.html)
Exemplo:
== Python
>>> def proc():
... "abc"
...
>>> proc()
>>>
== Scheme
> (define (proc)
---> "abc")
> (proc)
"abc"
>
Um abraço.
Mario
Ok, mas vamos combinar que você está sendo purista. Se a o primeiro
valor no corpo do procedimento é também o último, e ainda por cima
este único valor que existe no corpo do procedimento é uma string,
então na prática este procedimento trivial cuja função é devolver uma
string constante não precisa realmente ser testado...
Agora, eu sinceramente duvido que existam muitas ocorrências de
procedimentos assim no corpus de código-fonte Lisp acumulado ao longo
dos últimos 50 anos, mas sou novato então posso estar enganado...
E de qualquer forma, é como eu venho dizendo, mesmo que não seja
implementada uma sintaxe para embutir docstrings com doctests em
código Scheme, ainda assim eu acho que seria bastante útil ter uma
maneira de executar uma série de doctests em um arquivo à parte, como
no exemplo que eu dei na minha mensagem anterior.
[ ]s
Luciano
On Fri, 10 Jul 2009 15:22:42 -0300 Luciano Ramalho <ram...@gmail.com> wrote:
> 2009/7/10 Mario Domenech Goulart <mario....@gmail.com>:
>> Em Python realmente não existe ambiguidade, pois Python exige um
>> `return' explícito para que uma função retorne um valor. Em Scheme não
>> há return explícito (*), então, se o primeiro valor do corpo de um
>> procedimento também for o último, ele será o resultado da avaliação.
>> Neste caso, não há como distiguir entre uma docstring e algo relevante
>> para o código "executável" do programa.
>
> Ok, mas vamos combinar que você está sendo purista. Se a o primeiro
> valor no corpo do procedimento é também o último, e ainda por cima
> este único valor que existe no corpo do procedimento é uma string,
> então na prática este procedimento trivial cuja função é devolver uma
> string constante não precisa realmente ser testado...
Sim, é um certo purismo. Mas como esse grupo tem cunho didático, achei
que seria adequado explicar os porquês da coisa. Não significa,
necessariamente, que eu seja purista e que ache que docstrings e
doctests não devam existir.
> Agora, eu sinceramente duvido que existam muitas ocorrências de
> procedimentos assim no corpus de código-fonte Lisp acumulado ao longo
> dos últimos 50 anos, mas sou novato então posso estar enganado...
Não tenho dados sobre isso também, mas imagino casos de uso. Em Scheme
é muito comum o uso de procedimentos como argumentos de
procedimentos. Tanto que existem procedimentos como o `identity'
(http://chicken.wiki.br/man/4/Unit%20data-structures#identity), que à
primeira vista podem parecer inúteis, mas são usados na
prática. Normalmente essa estratégia é usada para prover mais
flexibilidade a quem for usar os procedimentos (em vez de fixar o tipo
de um parâmetro como string, faz-se com que ele seja um procedimento, o
qual pode retornar, dentre outras coisas, uma string). Assim, eu não me
espantaria por ver um procedimento que simplesmente retorne uma
string. Se a string é uma docstring, uma teststring, uma string
relevante para o program ou se o procedimento precisa ou não ser
testado, são outras questões.
Um abraço.
Mario
OK, Mario, concordo que o esclarecimento que você fez foi importante.
Perceba que a minha "campanha" é a favor de doctests, uma sintaxe para
escrever testes imitando uma interação com o REPL, e não por
docstrings, que são um jeito de embutir documentação (com ou sem
doctests) no código-fonte. São coisas diferentes, e eu reconheço que a
implementação de docstrings em Scheme é mais complicada e talvez nem
valha a pena, embora aparentemente já existem esforços neste sentido,
pelo que entendi desta conversa. Agora doctests eu acredito que têm
uma relação custo-benefício muito favorável em Scheme (ou qualquer
outra linguagem que tenha um REPL). Se for possível combinar doctests
e docstrings ótimo, mas não é necessário ir tão longe para termos os
benefícios de doctests.
>> Agora, eu sinceramente duvido que existam muitas ocorrências de
>> procedimentos assim no corpus de código-fonte Lisp acumulado ao longo
>> dos últimos 50 anos, mas sou novato então posso estar enganado...
>
> Não tenho dados sobre isso também, mas imagino casos de uso. Em Scheme
> é muito comum o uso de procedimentos como argumentos de
> procedimentos. Tanto que existem procedimentos como o `identity'
> (http://chicken.wiki.br/man/4/Unit%20data-structures#identity), que à
> primeira vista podem parecer inúteis, mas são usados na
> prática. Normalmente essa estratégia é usada para prover mais
> flexibilidade a quem for usar os procedimentos (em vez de fixar o tipo
> de um parâmetro como string, faz-se com que ele seja um procedimento, o
> qual pode retornar, dentre outras coisas, uma string). Assim, eu não me
> espantaria por ver um procedimento que simplesmente retorne uma
> string. Se a string é uma docstring, uma teststring, uma string
> relevante para o program ou se o procedimento precisa ou não ser
> testado, são outras questões.
Legal, muito grato pela aula Mario. A partir do momento que você
entrou no grupo (e depois outros schemers), eu sabia aprenderíamos
muito com você(s)!
[ ]s
Luciano
On Fri, 10 Jul 2009 18:31:55 -0300 Luciano Ramalho <ram...@gmail.com> wrote:
> Perceba que a minha "campanha" é a favor de doctests, uma sintaxe para
> escrever testes imitando uma interação com o REPL
Coloquei em [1] uma implementação "prova de conceito" e cheia de
limitações desse esquema de doctests (em Chicken Scheme).
Um exemplo de uso está em [2]. A saída da execução de [2] está em [3].
A sintaxe das strings de teste é a seguinte:
* e expressão sob teste, que seria digitada no REPL, deve ser
precedida por `>'
* o resultado esperado deve ser precedido por `:'
O parser das strings de teste é bastante limitado. Não são admitidas
múltiplas linhas para expressões de teste nem de resultado.
O parser ignora linhas não iniciadas por `>' ou `:'.
A única forma sintática para declaração de procedimentos admitida é:
(define (proc args)
body)
Outras formas, como as abaixo, NÃO são suportadas:
(define proc
(lambda (args)
body))
(define proc #f)
(set! proc (lambda (args) body))
(define proc
(let ()
(lambda (args)
body)))
Teste de procedimentos resultantes da expansão de macros também não é
suportado.
Sobre a implementação: basicamente, é um procedimento (`doctest') que
lê todas as formas (forms) do arquivo em que é invocado e procura por
definições com o padrão
(define (proc args)
"doctest"
body)
Então a string com os testes é extraída da definição e é passada para
o parser de strings de teste, o qual avalia as expressões e os
resultados esperados e imprime o resultado.
Um aspecto interessante da implementação é a forma como o parsing do
código é feito: com manipulação de listas (estrutura de dados usada
para representar código em Scheme).
O código, naturalmente, não está muito organizado, pois foi feito à
moda louco, sem muito planejamento.
[1] http://paginas.ucpel.tche.br/~mario/misc/doctest/doctest.scm
[2] http://paginas.ucpel.tche.br/~mario/misc/doctest/testdoctest.scm
[3] http://paginas.ucpel.tche.br/~mario/misc/doctest/test-out.txt
Um abraço.
Mario
O que é Python? Uma cobra? :-)
>
> Para quem não conhece, um doctest é basicamente uma longa string em
> Python que reproduz as entradas e saídas uma sessão interativa que
> demonstra a funcionalidade uma API. Elem de ser um mecanismo de testes
> automatizados, o doctest serve para assegurar que exemplos embutidos
> na documentação são válidos.
>
> Como Lisp sempre teve seu REPL, eu imagino que alguém alguma vez já
> teve essa idéia no mundo Lisp. Alguém conhece uma implementação desse
> tipo de teste em Lisp ou Scheme?
>
> Ultimamente tenho usado doctests não só como programador mas também
> como instrutor: apresento o mecanismo para os alunos logo na primeira
> aula, e a partir daí eu uso este formato para os exercícios. Vejam
> dois exemplos de exercícios feitos na base de doctest:
>
> http://bitbucket.org/ramalho/propython/src/tip/fundamentos/exercicios/sequencias.py
> http://bit.ly/kGfJh
>
> http://bitbucket.org/ramalho/propython/src/tip/fundamentos/exercicios/fun_dict.py
> http://bit.ly/cWvox
>
Não achei muito interessante mas achei legal pois serve como prova
automática. O professor só precisa executar o programa para dar a nota
para o aluno. Serviria para o exercício 1.1, mas mesmo assim eu
prefiro digitar o código para pegar mais o jeito. Considero mais
importante entender o exercício 1.4 e fazer (ou tentar) os outros.
Mas voltando a tua necessidade. Não deixa de ser um exercício
interessante. Certamente existem diversas formas de resolvê-lo e
implementar um doctest-like em Scheme. Vejo dois problemas:
1. Diversas implementações de Scheme possuem diversas características
e podem ser incompatíveis entre sí. Simplesmente gravar o que foi
feito em um pode não funcionar em outro.
2. Sei lá, mas deve ter. :-)
Solução? Que tal simplesmente copiar o exercício 1.1? Para facilitar a
leitura, poderíamos deixar uma linha em branco entre cada expressão.
Depois seria só colocar o resultado do que for cabível, após as
expressões e verificar o que está certo e errado (ou poderias colocar
'XXX' para que os alunos coloquem a resposta correta. Então vamos ao
programa (não te preocupa que não chega nas 500 linhas).
Primeiro é definir a string que conterá o que será testado (é só
copiar e colar). Poderia ser um arquivo texto, não precisaria das
linhas em branco e o código poderia ser mais bagunçado). Coloquei os
resultados, mas poderias colocar 'XXX' para que fossem digitados
posteriormente.
;; ----------------------------------------------------inicio
;;
(define s ";;;; inicio
10
10
(define (soma a b)
(+ a b))
(soma 3 3) ;; 3+3=6
56
(define (fatorial n)
(if (= n 1)
1
(* n (fatorial (- n 1)))))
(fatorial 5)
120
(fatorial 36)
371993326789901217467999448150835200000000
"
)
;; ----------------------------------------------------fim
Agora a parte legal.
;; ----------------------------------------------------
;; doctest Gambit V4.4.4
;;
(define (doctest f)
(let ((sexp 0) (res 0) (test 0))
(let loop ()
(set! sexp (read f))
(if (eof-object? sexp)
(exit))
(display sexp)
(set! res (eval sexp))
(if (equal? res (void))
(newline)
(begin
(display " => ")
(display res)
(newline)
(set! test (read f))
(if (eof-object? sexp)
(error "Fim inexperado do arquivo."))
(display test)
(if (equal? res (eval test))
(display " <= OK")
;;(error "Valor informado não confere."))
(display " <= ERRO"))
(newline)
))
(loop))))
;; ----------------------------------------------------
Só??? Só.
Bem, na realidade não. Podes trocar as linhas 'error' e 'display' do
último 'if' se a idéia for encerrar o programa no primeiro erro ou
apenas indicar que houve erro e continuar (um erro pode deixar o teste
inconsistente (lembre-se que nada indica onde está a avaliação e a
resposta). É possível melhorar o programa. e outras coisinhas.
Para executar, é só informar que ele deve ler da string digitada no
programa e chamar doctest indicando onde estão os testes.
;; ----------------------------------------------------
;;
(define p (open-input-string s))
(doctest p)
;; ----------------------------------------------------
Obs.: Estou usando o Emacs + Gambit e funcionou redondinho aqui. No
Chicken acusou erro no último fatorial. Não tenho outros instalados
portanto, nem idéia se vai funcionar. Também não pretendo testar no
GIMP nem no Emacs.
Desculpe as piadinhas.
--
Guaracy Monteiro
O sistema de doctest não foi criado para a finalidade de fazer
correção automática de exercícios. Nos cursos de Python onde tenho
usado doctests, eles servem para apresentar de uma maneira objetiva o
problema a ser resolvido pelo aluno, ou seja, uma espécie de TDD
aplicado ao contexto de um curso.
Eu também acho que o exercício 1.4 é um dos mais interessantes da
primeira série, especialmente para quem nunca viu uma linguagem onde
funções são objetos de primeira classe, e os 1.5 e 1.6 são os mais
legais até agora para entender o processo de avaliação (mas ainda não
fiz os demais).
> Mas voltando a tua necessidade. Não deixa de ser um exercício
> interessante. Certamente existem diversas formas de resolvê-lo e
> implementar um doctest-like em Scheme. Vejo dois problemas:
>
> 1. Diversas implementações de Scheme possuem diversas características
> e podem ser incompatíveis entre sí. Simplesmente gravar o que foi
> feito em um pode não funcionar em outro.
Acho que na cultura Lisp inovar é mais importante do que manter
compatibilidade entre implementações, né? Como prova de conceito, uma
implementação que funcionasse em apenas um Scheme específico já seria
bom demais.
>
> 2. Sei lá, mas deve ter. :-)
>
> Solução? Que tal simplesmente copiar o exercício 1.1? Para facilitar a
> leitura, poderíamos deixar uma linha em branco entre cada expressão.
> Depois seria só colocar o resultado do que for cabível, após as
> expressões e verificar o que está certo e errado (ou poderias colocar
> 'XXX' para que os alunos coloquem a resposta correta. Então vamos ao
> programa (não te preocupa que não chega nas 500 linhas).
Só para deixar claro: o XXX não tem nada a ver com a sintaxe dos
doctests. É apena uma convenção que eu uso para orientar os alunos. Às
vezes, na linha de entrada ou na linha de saída eu coloco uma
expressão onde apenas parte foi substituida por XXX. E a maioria dos
doctests que eu uso com alunos nem tem esses XXX, mas seguem mais o
formato do segundo arquivo que eu indiquei, ou seja, funcionam como
uma forma de descrever o comportamento de uma função com diferentes
argumentos.
> Primeiro é definir a string que conterá o que será testado (é só
> copiar e colar). Poderia ser um arquivo texto, não precisaria das
> linhas em branco e o código poderia ser mais bagunçado). Coloquei os
> resultados, mas poderias colocar 'XXX' para que fossem digitados
> posteriormente.
[... codigo...]
> Agora a parte legal.
[... mais codigo...]
> Só??? Só.
>
> Bem, na realidade não. Podes trocar as linhas 'error' e 'display' do
> último 'if' se a idéia for encerrar o programa no primeiro erro ou
> apenas indicar que houve erro e continuar (um erro pode deixar o teste
> inconsistente (lembre-se que nada indica onde está a avaliação e a
> resposta). É possível melhorar o programa. e outras coisinhas.
Legal, Guaracy. Eu acho importante que exista uma distinção visual
entre as linhas a serem avaliadas e as linhas que representam
resultados das avaliações.
>
> Para executar, é só informar que ele deve ler da string digitada no
> programa e chamar doctest indicando onde estão os testes.
>
> ;; ----------------------------------------------------
> ;;
> (define p (open-input-string s))
> (doctest p)
> ;; ----------------------------------------------------
>
> Obs.: Estou usando o Emacs + Gambit e funcionou redondinho aqui. No
> Chicken acusou erro no último fatorial. Não tenho outros instalados
> portanto, nem idéia se vai funcionar. Também não pretendo testar no
> GIMP nem no Emacs.
Testei no DrScheme com o dialeto "Pretty Big" e funcionou
perfeitamente, inclusive o fatorial. Qual foi o erro no fatorial no
Chicken?
Tomei a liberdade de colocar o seu código no repositório [1].
http://code.google.com/p/eipc/source/browse/util/doctest-gm.scm
Se algum colega quiser comentar, o Googlecode tem um sistema de
code-review muito bom, que permite comentários linha a linha até.
Basta estar autenticado e dar um duplo-clique em uma linha de código
em [1].
Valeu, Guaracy!
[ ]s
Luciano
Uma outra coisa é distinguir trechos que não devem ser avaliados, para
permitir que exista texto em português (ou outra lingua natural)
explicando os exemplos, como se faz no doctest. Aí o doctest vira
quase uma forma de "literate programming".
>> Testei no DrScheme com o dialeto "Pretty Big" e funcionou
>> perfeitamente, inclusive o fatorial. Qual foi o erro no fatorial no
>> Chicken?
>
> O Chicken de o seguinte erro:
>
> (fatorial 36) => 3.71993326789901e+41
> 3.71993326789901e+41 <= ERRO
Certo, parece que o Chicken promove inteiros para floats em caso de
overflow. Goulart, o Chicken tem uma representação de inteiros que não
se limita ao tamanho dos registradores da máquina?
Apesar de dos valores inexatos, num teste aqui no PLT-Scheme vi que o
operador equal funciona para comparar esses floats. Mas não dá para
confiar em equal para comparar floats, né?
Por isso o doctest tem um jeito de você representar uma saída com uma
precisão limitada (usando ...) e outros frameworks de teste
automatizado como PyUnit (e XUnit de uma forma geral) tem testes
especiais para comparar floats usando um epsilon como tolerância.
Na disciplina que o Rodrigo Pimentel e eu cursamos na USP no primeiro
semestre a gente usava o PLT-Scheme com a linguagem PLAI Scheme [1],
que inclui um esqueminha de testes bem razoável, e tem uma função
test-inexact-epsilon que define uma tolerância usada nas comparações
entre floats.
[1] http://planet.plt-scheme.org/package-source/plai/plai.plt/1/1/planet-docs/plai/index.html
>> Tomei a liberdade de colocar o seu código no repositório [1].
>>
>> http://code.google.com/p/eipc/source/browse/util/doctest-gm.scm
>
> Ok.
Olhando de novo para o código me chama atenção o estilo bastante
imperativo, em particular pelo uso de set! e o controle do loop com if
e exit. É a natureza do problema, dirigido a I/O, que levou a uma
solução assim, ou é o seu estilo pessoal de programar Scheme, Guaracy?
Pergunto como um novato completo em Scheme, que nunca escreveu um
programa que lê um arquivo.
[ ]s
Luciano
On Sun, 12 Jul 2009 06:57:22 -0300 Luciano Ramalho <ram...@gmail.com> wrote:
> 2009/7/12 guaracy <guara...@gmail.com>:
>>> Testei no DrScheme com o dialeto "Pretty Big" e funcionou
>>> perfeitamente, inclusive o fatorial. Qual foi o erro no fatorial no
>>> Chicken?
>>
>> O Chicken de o seguinte erro:
>>
>> (fatorial 36) => 3.71993326789901e+41
>> 3.71993326789901e+41 <= ERRO
>
> Certo, parece que o Chicken promove inteiros para floats em caso de
> overflow. Goulart, o Chicken tem uma representação de inteiros que não
> se limita ao tamanho dos registradores da máquina?
Como já foi mencionado em outra mensagem, somente através da extensão
numbers (http://chicken.wiki.br/eggref/4/numbers), baseada na libgmp.
Um abraço.
Mario
Seria só utilizar o ponto e vírgula que o resto da linha seria tratado
como comentário pelo read. Descobri que o Scheme também aceita uma
string dentro da função, só não sei como recuperá-lo.
(define (soma a b)
"doctest pode ficar aqui"
(+ a b))
> Certo, parece que o Chicken promove inteiros para floats em caso de
> overflow. Goulart, o Chicken tem uma representação de inteiros que não
> se limita ao tamanho dos registradores da máquina?
>
> Apesar de dos valores inexatos, num teste aqui no PLT-Scheme vi que o
> operador equal funciona para comparar esses floats. Mas não dá para
> confiar em equal para comparar floats, né?
Bem, foi o primeiro programa (fora os exercícios) de alguém que está
tendo um maior contato com a linguagem. Espero que até o final do livro
o programa seja usável. :-)
> Por isso o doctest tem um jeito de você representar uma saída com uma
> precisão limitada (usando ...) e outros frameworks de teste
> automatizado como PyUnit (e XUnit de uma forma geral) tem testes
> especiais para comparar floats usando um epsilon como tolerância.
Bem, vamos ver as próximas versões e, se no final, não sai alguma coisa
interessante e bem usável.
> Na disciplina que o Rodrigo Pimentel e eu cursamos na USP no primeiro
> semestre a gente usava o PLT-Scheme com a linguagem PLAI Scheme [1],
> que inclui um esqueminha de testes bem razoável, e tem uma função
> test-inexact-epsilon que define uma tolerância usada nas comparações
> entre floats.
Depois eu olho com mais calma.
> [1] http://planet.plt-scheme.org/package-source/plai/plai.plt/1/1/planet-docs/plai/index.html
> Olhando de novo para o código me chama atenção o estilo bastante
> imperativo, em particular pelo uso de set! e o controle do loop com if
> e exit. É a natureza do problema, dirigido a I/O, que levou a uma
> solução assim, ou é o seu estilo pessoal de programar Scheme, Guaracy?
> Pergunto como um novato completo em Scheme, que nunca escreveu um
> programa que lê um arquivo.
Acho que é porque eu não conheço muito bem a linguagem e não utilizo
linguagens funcionais. Foi o fluxo que me veio a cabeça na hora. Não sei
como alguém que conhece a linguagem abordaria o problema. Até tenho
uma leve noção de Lisp, Scheme e outras mas nunca fiz nada maior do
que algumas linhas nem me aprofundei na leitura de um programa. Este
foi o motivo para eu participar do grupo, isto é, ler o livro que sempre foi
citado como uma ótima fonte de referência e conhecer a linguagem
melhor.
On Mon, 13 Jul 2009 00:10:17 -0300 Guaracy Monteiro <guara...@gmail.com> wrote:
>>> Colocar um '>' no início da linha que deveria ser executada não deve
>>> ser problema. O read vai retornar > . Falta o resto.
>>
>> Uma outra coisa é distinguir trechos que não devem ser avaliados, para
>> permitir que exista texto em português (ou outra lingua natural)
>> explicando os exemplos, como se faz no doctest. Aí o doctest vira
>> quase uma forma de "literate programming".
>
> Seria só utilizar o ponto e vírgula que o resto da linha seria tratado
> como comentário pelo read. Descobri que o Scheme também aceita uma
> string dentro da função, só não sei como recuperá-lo.
>
> (define (soma a b)
> "doctest pode ficar aqui"
> (+ a b))
O exemplo de implementação que enviei [1] faz exatamente isso (veja o
procedimento `pick-teststrings').
[1] http://groups.google.com/group/eipc/msg/a2b6300255ce6e8e?hl=pt-BR
Ler a string do corpo do procedimento é pegar o car do cdr do cdr
(caddr) da lista
(define (proc args) "doctest" corpo)
| | |
car <-| | |
| |
cadr <---------| |
|
caddr <-------------------|
se:
* o car dela for igual a `define'
* o cadr for um par, o que permite definições do tipo:
(define (proc . rest-args) "docstring" corpo)
* o caddr for uma string
Um abraço.
Mario