Re: style issues




H. wrote:

No need to call APPEND in either branch. CONS is the right constructor for
adding one element to a list.

But why? CONS prepends, and APPEND appends, so why not have a choice
about it?

The types don't match. Suppose you have a list of integers that you
wished to extend. CONS would take a single integer and a list of
integers to extend the list, APPEND takes two lists of integers to
concatenate. Sure, you can create a one element list and then append
it, but that is more work and obfuscates what you are doing.

and the second APPEND call does nothing at all, slowly.

this I see now. But why not:

(define (my-keep-1 pred a-list)
(cond ( (null? a-list) '())
( (pred (car a-list))
(append (list (car a-list))
(my-keep-1 pred (cdr a-list))))
( else (my-keep-1 pred (cdr a-list)))))

In other words, I know you can say that CONS is the right constructor
for adding one element to a list, but that statement seems kind of
arbitrary to me at the moment in my un-enlightened state.

See above.


If you had to use APPEND anyway, then there'd be a case to be made for #2.
But I never like this idiom of (append (if ... ... '()) ...).

Is it just in append that you don't consider composable ifs a good
thing, or in general?

I can't answer for Professor Harvey, but in my opinion
(append (if ... ... '()) ...)
is a bit confusing. It's a lot like
(+ (if ... ... 0) ...)

The idea is that you want to conditionally perform an operation, not
unconditionally perform it but use an identity to fool into not
happening in the special case.

In either case, it you, or everyone? (my current instructor docks
points off for break statements in for loops, but allows multiple
returns in functions. A prior instructor said that the break statement
stuff was silliness but didn't allow multiple return statements from
the same function. A former manager at work was also a fan of the "only
one return statement" family, but scattered break statements
everywhere. Universal "good coding practices" seem not to be
universally agreed upon.)

Non-local exits (break statements *and* returns from the middle of
code) are both bad.
Unfortunately, many languages don't have tail recursion and therefore
don't let you roll your own looping constructs. In these languages you
often *have* to use break or continue because there is no other way to
do it.

Languages with `return' statements tend to have the bizarre notion that
you *must* explicitly call return even if the return continuation is
the implicit one. I dislike code that blows its way out of deeply
nested constructs with a return, but I don't see a problem with
multiple return statements in code where there is no deep nesting. In
other words, if the implicit continuation *is* the return continuation,
there is no reason not to invoke it.

(Making arbitrary rules about use of break or return without
understanding the underlying semantics is suspect, anyway.)

P.S. "a-list" is a more-or-less standard abbreviation for "association list,"
so it's a dubious formal parameter for a plain old list. :-)

Noted.

First I called it lst, but in the font I was using it looked too much
like 1st. So I switched it to "the-list," but the article didn't look
right, so it got switched to "a-list." Alas, a-list is now off the
list, at least when I publicize code. :-)

So just call it `list'.



P.P.S. Sadly, you can't use CONS to avoid
(append old-list (list new-element))
where the new element goes at the end. (Well, you could say
(cons new-element '())
instead of calling LIST, but you can't avoid the call to APPEND unless you
write your own append under a different name.)

Exactly. Which is why it seems to me like you should be able to build
up any arbitrary list from the front with append, or from the back with
cons, and have both be equally stylistically okay. But again, I don't
know anything.

Lists are not symmetric structures. It is helpful to think of a list
as something like a rope where the tail end is far away from you. To
add something to the front, you can simply tie some more rope on. To
add to the back involves going hand-over-hand pulling the tail end
closer until you get to it, and then adding the extra element. This is
a *lot* more work.

.



Relevant Pages

  • Re: SETF and variable issues in Self Similar program.
    ... APPEND is always a warning sign. ... ;;automatically - these lists contain all numbers that are equivalent ... Global variables should always be written as *foo*. ... (defun ordered-union (x y) ...
    (comp.lang.lisp)
  • Re: SETF and variable issues in Self Similar program.
    ... Most people just use CONS. ... I was somehow too fixated on discussing append. ... Although CL doesn't promise tail call optimization, ... calling APPEND on lists in recursive functions ...
    (comp.lang.lisp)
  • Predicate lists -- Request for Comments
    ... (let ((current-position element-list) ... revised (append revised rev)) ... It is also a pleasure now to write a function that would split a list into two lists, first containing the elements that satisfy the predicate list, and the second containing the remaining elements: ...
    (comp.lang.scheme)
  • Re: append queries
    ... > the end offers blank fields for data entry. ... > include all those drop down lists, ... > Do append queries automatically empty out after appending ... An append query will not affect the source data at all. ...
    (microsoft.public.access.tablesdbdesign)
  • Re: cons discussion from a newbie
    ... >to keep in mind that the elements of a list can themselves be lists. ... Abelson and Sussman use no data types except numbers in chapter 1, ... things as the asymmetry of CONS. ... I prefer to answer the question about CONS vs LIST vs APPEND ...
    (comp.lang.scheme)