Solução na linguagem TXR .
@(next :args)
@(bind rangelim nil)
@(block)
@ (cases)
@filename
@ (maybe)
@rlim
@ (set rangelim @(int-str rlim))
@ (end)
@ (eof)
@ (or)
@ (output)
arguments are: filename [ range-limit ]
@ (end)
@ (fail)
@ (end)
@(end)
@(do
(defun prefix-match (list0 list1)
(let ((c 0))
(each ((l0 list0)
(l1 list1))
(if (not (equal l0 l1))
(return c))
(inc c))
c))
(defun line-stream (s)
(let (li) (gen (set li (get-line s)) li)))
(let* ((s (line-stream (open-file filename "r")))
(lim rangelim)
(s* (if lim s nil))
(h (hash :equal-based))
(max-len 0)
(max-line nil))
(for ((ln 1)) (s) ((set s (rest s)) (inc ln))
(let ((li (first s)))
(let ((po (gethash h li))) ;; prior occurences
(each ((line [mapcar car po])
(pos [mapcar cdr po]))
(let ((ml (prefix-match pos s)))
(cond ((and
(= ml (- ln line))
(> ml max-len))
(set max-len ml)
(set max-line line))))))
(pushhash h li (cons ln s))
(if (and lim (> ln lim))
(let* ((oldli (first s*))
(po (gethash h oldli))
(po* (remove-if (op eq s* (cdr @1)) po)))
(if po*
(sethash h oldli po*)
(remhash h oldli))
(set s* (cdr s*))))))
(if max-line
(format t "~a line(s) starting at line ~a\n" max-len max-line)
(format t "no repeated blocks\n"))))
O programa consiste quase inteiramente no dialeto incorporado do TXT. A abordagem aqui é manter cada linha do arquivo em uma tabela de hash. Em qualquer posição no arquivo, podemos perguntar à tabela de hash "em que posições já vimos essa linha exata antes, se houver alguma?". Se assim for, podemos comparar o arquivo a partir daquela posição para as linhas começando na posição atual. Se a partida se estender desde a posição anterior a posição atual, significa que temos uma partida consecutiva: todas as N linhas da posição anterior até logo antes da linha atual correspondem às N linhas que começam na linha atual. Tudo o que temos então é encontrar entre todos esses lugares candidatos aquele que produz a correspondência mais longa. (Se houver laços, apenas o primeiro é relatado).
Ei, olhe, há uma sequência repetida de duas linhas em um arquivo de log do Xorg:
$ txr longseq.txr /var/log/Xorg.0.log
2 line(s) starting at line 168
O que está na linha 168? Estas quatro linhas:
[ 19.286] (**) VBoxVideo(0): Built-in mode "VBoxDynamicMode": 56.9 MHz (scaled from 0.0 MHz), 44.3 kHz, 60.0 Hz
[ 19.286] (II) VBoxVideo(0): Modeline "VBoxDynamicMode"x0.0 56.94 1280 1282 1284 1286 732 734 736 738 (44.3 kHz)
[ 19.286] (**) VBoxVideo(0): Built-in mode "VBoxDynamicMode": 56.9 MHz (scaled from 0.0 MHz), 44.3 kHz, 60.0 Hz
[ 19.286] (II) VBoxVideo(0): Modeline "VBoxDynamicMode"x0.0 56.94 1280 1282 1284 1286 732 734 736 738 (44.3 kHz)
Por outro lado, o arquivo de senha é único:
$ txr longseq.txr /etc/passwd
no repeated blocks
O segundo argumento adicional pode ser usado para acelerar o programa. Se sabemos que a seqüência de repetição mais longa é, digamos, não mais de 50 linhas, então podemos especificar isso. O programa não irá olhar para mais de 50 linhas. Além disso, o uso da memória é proporcional ao tamanho do intervalo, não ao tamanho do arquivo, então ganhamos de outra maneira.