Steven E. Harris <seh@panix.com> wrote:
+---------------
| rpw3@rpw3.org (Rob Warnock) writes:
| > (loop for line = (read-line s nil nil)
| > while line
| > collect line into lines
| > counting t into line-count
| > finally (return (values lines line-count)))
|
| I looked at the macro expansion for "counting t into line-count" and
| noticed that each increment operation is wrapped in a conditional
| "(when t ...)". Does this conditional check get optimized away?
+---------------
I suspect so, in any decent compiler at least. In CMUCL, for example:
> (macroexpand '(when t (foo)))
(IF T (PROGN NIL (FOO)) (COND))
>
Now that's a *lot* of needless noise, eh? But when we compile it,
it all seems to disappear, and the whole thing gets converted into
an unconditional tail-call of FOO:
> (disassemble (compile nil (lambda () (when t (foo)))))
...
48910FF8: .ENTRY "LAMBDA NIL"() ; (FUNCTION NIL *)
1010: POP DWORD PTR [EBP-8]
1013: LEA ESP, [EBP-32]
1016: TEST ECX, ECX ; [:NON-LOCAL-ENTRY]
1018: JNE L0
101A: MOV EAX, [#x48910FF0] ; #<FDEFINITION object for FOO>
; No-arg-parsing entry point
; [:NON-LOCAL-ENTRY]
1020: XOR ECX, ECX
1022: PUSH DWORD PTR [EBP-8]
;;; [3] (FOO)
1025: JMP DWORD PTR [EAX+5] ; [:CALL-SITE]
1028: L0: BREAK 10 ; Error trap
102A: BYTE #x02
102B: BYTE #x19 ; INVALID-ARGUMENT-COUNT-ERROR
102C: BYTE #x4D ; ECX
>
+---------------
| I'm just wondering why you wouldn't prefer the following:
| (loop for line = (read-line s nil nil)
| for line-count upfrom 0
| while line
| collect line into lines
| finally (return (values lines line-count)))
+---------------
(*blush!*) No special reason. Of course you're right, "LINE-COUNT
UPFROM 0" is fine, and I'll probably even change my library routine
accordingly. The "COUNTING T" is there probably because I learned
COUNTING before I learned multiple FORs. On the other hand, it
might have been because initially I was unsure about the order of
side-effects with multiple loop variable in the presence of a WHILE,
so I put the COUNTING *after* the WHILE to make sure it only counted
when a line was read. But now that I know LOOP a little better, I
know that all FORs must be before any WHILEs, and are executed in
order.
In CL, but *especially* in LOOP, follow the "there's more than one
way to do it" rule. I suspect I'm not the only one who tends to
get locked into whatever idiom one discovers first. I've only been
seriuoaly using CL for 4-5 years, and even code I wrote just a year
ago now looks horribly non-idiomatic. [But who has time for style-only
rewrites...?]
By the way, since there are no serial dependencies between the two
loop variables, I would prefer to use AND instead of the second FOR,
which might generate slightly better code in some cases:
(loop for line = (read-line s nil nil)
and line-count upfrom 0
while line
collect line into lines
finally (return (values lines line-count)))
Using FOR/AND/... except when you *need* FOR/FOR/... is, to me,
analogous to always using LET except when you *need* LET*.
Some people feel that matters; some don't.
-Rob
-----
Rob Warnock <rpw3@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607