O shell é lento: use um idioma diferente. Se compararmos o script original do KSH (modificado para stdin e stdout usados), algo muito semelhante ao código Perl do steeldriver ( um script em vez de um one-liner que mostra velocidades similares à versão KSH nativa de glenn jackman), e uma implementação LISP com 10.000 linhas de entrada em um sistema de teste Centos 7:
base62.ksh 93.29s user 143.48s system 109% cpu 3:36.73 total
base62.perl 1.32s user 0.00s system 99% cpu 1.326 total
base62.sbcl 0.22s user 0.03s system 99% cpu 0.243 total
Obviamente, o código original rapidamente se torna impraticável à medida que as linhas de entrada aumentam, assim como as linguagens de script comparadas ao LISP com quantidades significativas de entrada. O tempo base62.sbcl
é proveniente de uma implementação recursiva de chamada final:
#|
eval 'exec sbcl --script "$0" ${1+"$@"}'
|#
(defun divvy-r (n b l)
(if (< n b) (cons (truncate n) l)
(let ((rem (truncate (mod n b))) (quo (/ n b)))
(divvy-r quo b (cons rem l)))))
(defun divvy (n b)
(let ((rem (mod n b)) (quo (/ n b)))
(if (< quo 2)
(list (truncate quo) (truncate rem))
(divvy-r n b nil))))
(loop for line = (read-line *standard-input* nil) while line do
(let ((n (parse-integer (subseq line (1+ (position #\| line))))))
(let ((out (divvy n 62)))
(format t "~a|~{~a~^ ~}~&" line out))))
Lendo "Common Lisp: Uma introdução delicada à computação simbólica" e fazendo todos os exercícios nela é como eu aprendi isso. Um pouco mais rápido (e sempre tão sucinto) é uma implementação do*
baseada no código KSH de Glenn Jackman:
#|
eval 'exec sbcl --script "$0" ${1+"$@"}'
|#
(defun remainders (n base)
(do* ((rem (mod n base) (mod quo base))
(quo (/ n base) (/ quo base))
(out (cons (truncate rem) nil) (cons (truncate rem) out)))
((< quo base) (cons (truncate quo) out))))
(loop for line = (read-line *standard-input* nil) while line do
(let ((n (parse-integer (subseq line (1+ (position #\| line))))))
(format t "~a|~{~a~^ ~}~&" line (remainders n 62))))