99 bottles of beer: Difference between revisions

→‎{{header|TXR}}: Eliminate external utility; use a lazy list instead.
(Add an ATS implementation. My own work.)
(→‎{{header|TXR}}: Eliminate external utility; use a lazy list instead.)
Line 4,419:
=={{header|TXR}}==
 
Previously, this used an external command to generate a text stream of numbers from 99 to -1, one per line. For fun, this is now replaced by a lazy list, keeping the rest of the program intact: processes a stream of numbers and transforms then into the song. Lazy lists are based on lazy conses. A lazy cons is a special object which contains a function. When the object is accessed using the usual cons accessors, the function is called to fill in the car and cdr slots of the object. To terminate the list, the function writes a nil into the cdr. If the list is to be continued, the function can put a continuation tail into cdr, which can be a concrete list, or another lazy cons (which can re-use the same lazy cons function, using the trick shown in this example).
{{works with|seq|7.1 (GNU coreutils)}}
 
<lang txr>@(do (defun lazy-textual-number-list (min max)
Here we scan the output of the seq command counting down from 99 to -1, one number per line. The <code>@(trailer)</code> directive allows us to match a trailing context within a text stream without consuming it. So we have access to N-1 without advancing over it, allowing it to be captured as N in the next iteration of <code>@(collect)</code>.
(let ((counter min)
Iterating down to -1 ensures that the 0 case has a next number, otherwise the match would fail and that case would vaporize. That is what happens to -1.
(delta (if (<= min max) 1 -1)))
 
(make-lazy-cons (lambda (lcons)
<lang txr>@(next `!seq 99 -1 -1`)
(rplaca lcons (format nil "~a" counter))
(cond
((eql counter max)
(rplacd lcons nil) t)
(t
(inc counter delta)
(rplacd lcons (make-lazy-cons
(lcons-fun lcons))))))))))
@(next :list @(lazy-textual-number-list 99 -1))
@(collect)
@number
Anonymous user