Na linguagem TXR , podemos expressar isso sem nenhuma variável de estado em mutação. Em qualquer posição dada no arquivo, podemos realizar uma combinação de padrão de várias linhas com duas alternativas de ramificação: ou combinamos uma ou mais linhas consecutivas que contêm a sequência de pesquisa e, em seguida, imprimimos a primeira ou então combinamos uma linha e a imprimimos. Uma maneira possível é esta:
@(repeat)
@ (cases)
@ (collect :gap 0 :mintimes 1)
@line
@ (require (search-str line "logical IO"))
@ (end)
@ (do (put-line (first line)))
@ (or)
@line
@ (do (put-line line))
@ (end)
@(end)
Executar:
$ txr first-log-IO.txr data
select * from test1 where 1=1
testing logical IO 24
select * from test2 where condition=4
parsing logical IO 45
select * from test5 where 1=1
testing logical IO 24
select * from test5 where condition=78
parsing logical IO 346
@(repeat)
estabelece um passeio pelos dados sem coletar ligações de variável; quando esta construção é vista, geralmente indica que algum efeito colateral ocorre na iteração. Neste caso, é a saída.
Dentro do @(repeat)
, temos uma combinação de @(cases)
: correspondência multidirecional que consiste em casos separados por @(or)
. A segunda ramificação, o caso de fallback, é apenas @line
que corresponde a uma linha. A diretiva @(do (put-line (first line)))
que segue imprime essa linha.
A ramificação principal do @(cases)
coleta material via @(collect)
. As correspondências devem ser consecutivas, exigidas por :gap 0
, e deve haver pelo menos uma, exigida por :mintimes 1
. O corpo da coleta corresponde a uma única linha, vinculada à variável line
. Em seguida, há uma asserção @(require ...)
que falha a menos que a linha contenha a substring "logical IO"
. A coleção, portanto, será interrompida quando encontrar uma linha não correspondente, porque o :gap 0
impede que ela seja ignorada. As linhas correspondentes são coletadas implicitamente em uma lista chamada line
, que aparece fora do collect
(uma variável vinculada dentro de uma coleta automaticamente se torna uma lista fora da coleta, de todos os valores vinculados às várias iterações). Acabamos de imprimir o primeiro, conforme necessário, suprimindo o resto.
Observe que as duas correspondências @line
não têm nada a ver uma com a outra; eles ligam a variável line
em escopos diferentes.
Outra maneira é fazer alguma programação funcional em relação a lazy lists no TXR Lisp:
[(opip (partition-by (do cond
((search-str @1 "logical IO") t)
(t @1)))
(mapcar* first)
put-lines)
(get-lines)]
$ txr first-log-IO.tl
O operador opip
é um açúcar sintático para construir um pipeline de funções. Seus argumentos são tratados como op
syntax: uma macro para gerar funções anônimas com argumentos numerados e implícitos.
A forma geral é [(opip ...) (get-lines)] que significa apenas "chamar a função produzida por opip
, com o resultado de (get-lines)
como argumento". Este (get-lines)
converte o fluxo de entrada padrão em uma lista lenta de cadeias de caracteres. (Seu "oposto" é put-lines
, que faz uma aparição).
Agora, no pipeline, usamos partition-by
para (preguiçosamente!) converter a lista de linhas em uma lista de listas que são suas partições. A condição de particionamento é tal que cada linha que contém logical IO
é mapeada para o símbolo t
e todas as outras linhas apenas mapeiam para si mesmas. Isso significa que as linhas consecutivas que contêm logical IO
aparecem como uma partição e todas as outras linhas aparecem isoladas como partições de comprimento um. Tudo o que temos a fazer com esses dados agora é mapear cada partição para seu primeiro item, via (mapcar* first)
e passá-la para put-lines
para despejar o resultado.
Usamos mapcar*
porque essa é a versão lenta de mapcar
. Queremos que tudo seja preguiçoso para que a ação seja realmente acionada por put-lines
. À medida que put-lines
percorre a lista de saída, ela extrai itens do preguiçoso mapcar*
, o que força os elementos produzidos pelo partition-by
, o que força a lista produzida por (get-lines)
, o que faz com que a E / S ocorra leia essas linhas.
Se usarmos o mapcar
regular por engano, causaremos o problema de que toda a saída seja construída na memória antes de ser descartada, o que é um mau presságio para arquivos grandes.