From: floaiza on

Five possible ways have been suggested to address the question of how
to generate a string that is N times some component string, e.g., "-".

;;;
;;; Ariel Badichi
;;;
(defun repeat-string (n string)
(with-output-to-string (out)
(loop repeat n do (write-string string out))))
;;;
;;; Ariel Badichi
;;;
(defun repeat-string (n string)
(let* ((length (length string))
(result (make-string (* n length))))
(dotimes (i n)
(setf (subseq result (* i length)) string))
result))
;;;
;;; Thomas A. Russ
;;;
(make-string n :initial-element character)
;;;
;;; Thomas A. Russ
;;;
(format nil "~10{abc~}" '(t))
;;;
;;; Thomas A. Russ
;;;
(format nil "~v{abc~}" 10 '(t))
;;;
;;; Thomas A. Russ
;;;
(defun repeat-string (n string)
(format nil (format nil "~~~D{~A~~}" n string) '(t)))
;;;
;;;
;;;

QUESTION: Is there one or more criteria to decide which one to use?

Speed of execution is mentioned as something to keep in mind; is this
the only criterion?

Should one time each solution and then go with the fastest one?

QUESTION: How does an expert CL programmer go about deciding how to
put together the code for an application?

As suggested in some of the responses one should avoid writing long
functions, and choose instead a style where each 'result' is
controlled by a small function with little or no 'side effects' (the
functional programming paradigm).

However, many string manipulations can be accomplished by using the
format macro. I have read that purist Lispers don't like to use that
macro (ditto for the Loop one).

In the end, does it matter?

=========

Ariel Badichi recommends not to couple the application that does the
display of the data returned by an SQL SELECT query with the database
access proper.

Is security the main reason for such a recommendation? Or is there
something else that supports it?

=========

I recognize that some of questions above are "open ended", but given
the plethora of choices, I am trying to figure out what I should look
for generally when making a decision regarding which solution to use.

Thanks,

Francisco


On Mar 8, 1:59 pm, t...(a)sevak.isi.edu (Thomas A. Russ) wrote:
> Ariel Badichi <vermilionr...(a)gmail.com> writes:
> > floaiza <floai...(a)gmail.com> writes:
>
> > > First a simple function to repeat a short string n-times:
>
> > > CL-USER>(defun repeat-string (n str)
> > >               (setq retstr "")
> > >               (dotimes (var1 n retstr)
> > >                 (setq retstr (concatenate 'string str retstr))))
>
> > Since you use SETQ to modify RETSTR (apparently a variable you have
> > not defined), the behavior of this function is undefined by the
> > Standard; use LET to introduce such a variable.  To repeat an
> > operation a number of times, it's not necessary to explicitly
> > introduce a variable to keep count; use LOOP REPEAT n DO instead of
> > DOTIMES.  Whenever CONCATENATE is called, a new string is created, so
> > if you repeat a string 3 times, 3 new strings will be created, two of
> > which are simply garbage when the function returns.  Here's one
> > approach to writing this function clearly and efficiently:
>
> >   (defun repeat-string (n string)
> >     (with-output-to-string (out)
> >       (loop repeat n do (write-string string out))))
>
> > We might also choose to make use of the fact that we know the length
> > of the resulting string before actually constructing it:
>
> >   (defun repeat-string (n string)
> >     (let* ((length (length string))
> >            (result (make-string (* n length))))
> >       (dotimes (i n)
> >         (setf (subseq result (* i length)) string))
> >       result))
>
> This is a nice tutorial on how to fix up the original function and is a
> nice general set of techniques to use.
>
> This would be necessary for the case of an actual short string.
>
> For a more specialized case, where there is only a single repeated
> character, one can use a very simple idioms using the MAKE-STRING
> function:
>
>    (make-string n :initial-element character)
>    (make-string 10 :initial-element #\- )  => "----------"
>
> For the string case, one can also make use of some fancy FORMAT options.
> For the case of a fixed string and count, you can get the repeat by
> (mis)using the ~{...~} iteration construct:
>
>    (format nil "~10{abc~}" '(t))
>
> for a variable number but fixed string, it is also easy.
>
>    (format nil "~v{abc~}" 10 '(t))
>
> For both a variable string and count, you would have to construct the
> format string to use, but it wouldn't be that hard:
>
>    (defun repeat-string (n string)
>      (format nil (format nil "~~~D{~A~~}" n string) '(t)))
>
> Note that using FORMAT, although rather compact, will not be nearly as
> fast as the second solution Ariel Badichi gave.
>
> --
> Thomas A. Russ,  USC/Information Sciences Institute

From: floaiza on
> > Is security the main reason for such a recommendation? Or is there
> > something else that supports it?
>
> No, the main reason is separation of concerns. What does table
> formatting have to do with database access?

OK. I get your point. However, in my case I am primarily interested in
formatting the results of SQL SELECT queries. If I separate the
formatting form the db access I would then have to write extra code to
grab the result and pass it to the formatting function. That's an
extra step which, in my case, I can obviate by coupling the formatting
and the db access, isn't it?

BTW, thank you for addressing the other points and providing links to
pertinent materials.

Regards,

Francisco

On Mar 10, 2:04 pm, Ariel Badichi <vermilionr...(a)gmail.com> wrote:
> floaiza <floai...(a)gmail.com> writes:
> > Five possible ways have been suggested to address the question of how
> > to generate a string that is N times some component string, e.g., "-".
>
> > ;;;
> > ;;; Ariel Badichi
> > ;;;
> > (defun repeat-string (n string)
> >      (with-output-to-string (out)
> >        (loop repeat n do (write-string string out))))
> > ;;;
> > ;;; Ariel Badichi
> > ;;;
> > (defun repeat-string (n string)
> >    (let* ((length (length string))
> >           (result (make-string (* n length))))
> >          (dotimes (i n)
> >            (setf (subseq result (* i length)) string))
> >          result))
> > ;;;
> > ;;; Thomas A. Russ
> > ;;;
> > (make-string n :initial-element character)
> > ;;;
> > ;;; Thomas A. Russ
> > ;;;
> > (format nil "~10{abc~}" '(t))
> > ;;;
> > ;;; Thomas A. Russ
> > ;;;
> > (format nil "~v{abc~}" 10 '(t))
> > ;;;
> > ;;; Thomas A. Russ
> > ;;;
> > (defun repeat-string (n string)
> >  (format nil (format nil "~~~D{~A~~}" n string) '(t)))
> > ;;;
> > ;;;
> > ;;;
>
> > QUESTION: Is there one or more criteria to decide which one to use?
>
> Certainly, there are many criteria useful in judging code, and their
> order of significance varies depending on the case.  E.g.:
>
>   1. Correctness - does the code satisfy the functional requirements?
>
>      The third solution is not correct if you are talking about
>      replicating _strings_.
>
>   2. Clarity - is the code easy to follow and understand?
>
>      I find FORMAT distasteful.  It has a cryptic language for
>      formatting descriptions that end up being stuffed into an opaque
>      control string, thereby making it hard to work with their
>      structure.  The semantics are sometimes plain weird, e.g., the
>      nesting behavior of ~(.  With some effort, a Lisp programmer can
>      come up with a nice extensible macro for formatting and never
>      look back. [0] [1]  Of course, that is no excuse for not being
>      familiar with FORMAT, since it is part of the language, and as
>      long as people don't go overboard with it (in real code - not
>      "show-off" posts :) it's a minor annoyance.  This is certainly
>      not to slight Thomas A. Russ's contribution: it's interesting to
>      see different approaches to solving a problem.
>
>   3. Efficiency - does the code satisfy requirements of performance?
>
>      You did not specify any such requirements, but it's a good idea
>      to avoid wanton waste of resources.  Considerations about
>      efficiency may include the time it takes to run, the time it
>      takes to develop, the storage required by the process, the amount
>      of power consumed by the machine, etc.
>
> The first solution I provided was correct, easy to understand, and
> moderately efficient.  It is the one that I would actually use by
> default.
>
> > Speed of execution is mentioned as something to keep in mind; is this
> > the only criterion?
>
> > Should one time each solution and then go with the fastest one?
>
> No, that is the wrong way going about such decisions.  You should have
> a set of desiderata, and an idea about their relative order of
> significance.  In the Lisp world, correctness usually is an absolute
> requirement and comes first.  Clarity is also regarded highly.  To get
> an idea of desiderata commonly valued in the Lisp world, see such
> papers as "Lisp: Good News, Bad News, How to Win Big" [2] (especially
> section 2.1, "The Rise of /Worse is Better/") and the "Tutorial on
> Good Lisp Programming Style" [3].
>
> In cases where efficiency matters, you should have a concrete
> performance requirement for a chunk of computation.  It is then
> possible to judge in a particular case (code running in a certain
> environment) whether the actual requirement is satisfied or not.  If
> the requirement is not satisfied, the code, environment, the
> requirement itself, or a combination thereof need to be adjusted.
>
> You don't just try a few pieces of code and choose the fastest one.
>
> > QUESTION: How does an expert CL programmer go about deciding how to
> > put together the code for an application?
>
> > As suggested in some of the responses one should avoid writing long
> > functions, and choose instead a style where each 'result' is
> > controlled by a small function with little or no 'side effects' (the
> > functional programming paradigm).
>
> Functional programming is only one of various styles Lisp programmers
> may choose to think in.  Unfortunately, there isn't an easy answer to
> your question.  Hopefully, a programmer uses his common sense,
> intuition, ingenuity, and background knowledge when designing a
> solution for a problem.
>
> > However, many string manipulations can be accomplished by using the
> > format macro. I have read that purist Lispers don't like to use that
> > macro (ditto for the Loop one).
>
> > In the end, does it matter?
>
> FORMAT is not a macro; it is a function.  (A FORMAT compiler macro may
> also be present, of course.)  I already wrote about FORMAT.  Some
> people do like it.  I know how to read and write its language.  I use
> it when I don't want to bring in a dependency on some other formatting
> facility.
>
> I think LOOP, despite its flaws, is a good and usable operator.  I
> have no qualms about using it.
>
> > =========
>
> > Ariel Badichi recommends not to couple the application that does the
> > display of the data returned by an SQL SELECT query with the database
> > access proper.
>
> > Is security the main reason for such a recommendation? Or is there
> > something else that supports it?
>
> No, the main reason is separation of concerns.  What does table
> formatting have to do with database access?
>
> > =========
>
> > I recognize that some of questions above are "open ended", but given
> > the plethora of choices, I am trying to figure out what I should look
> > for generally when making a decision regarding which solution to use.
>
> > Thanks,
>
> > Francisco
>
> Ariel
>
> [0] "Eliminating FORMAT from Lisp"
>    http://cs-www.cs.yale.edu/homes/dvm/format-stinks.html
>
> [1] CONSTANTIA:OUT, a convenient way to print stuff (on top of FORMAT)
>    http://github.com/death/constantia/blob/master/out.lisp
>
> [2] "Lisp: Good News, Bad News, How to Win Big"
>    http://www.dreamsongs.com/WIB.html
>
> [3] "Tutorial on Good Lisp Programming Style"
>    http://norvig.com/luv-slides.ps

From: Slobodan Blazeski on
On Mar 10, 4:50 pm, floaiza <floai...(a)gmail.com> wrote:
> Five possible ways have been suggested to address the question of how
> to generate a string that is N times some component string, e.g., "-".
[snip]
> QUESTION: Is there one or more criteria to decide which one to use?
>
> Speed of execution is mentioned as something to keep in mind; is this
> the only criterion?
>
> Should one time each solution and then go with the fastest one?
I have very simple algorithm for deciding what solution to use:
1st Make it work
2nd Refactor it until its clean
If and only of the clean solution is slow
3rd Profile it
4rth Rmove the bottleneck(s)


>
> QUESTION: How does an expert CL programmer go about deciding how to
> put together the code for an application?

>
>
> As suggested in some of the responses one should avoid writing long
> functions, and choose instead a style where each 'result' is
> controlled by a small function with little or no 'side effects' (the
> functional programming paradigm).
The best approach is to understand what kind of style fits the problem
best. Sometimes mutation and objects fits much better then functional
approach. Sometimes its relational style that yields best results. If
you have many tools in your toolbox you could choose the right one for
the job. Common Lisp is a multiparadigm language the only constraints
are you and the problem. I see this community growing and many lispers
coming from mainstream languages just like I did that at 2003 and
many of them are writing c-ish code in lisp and it sounds like the old
saying that Fortran programmer could write Fortran in any language.
Functional style and metaprogramming can't be learned until you are
ready to approach the language with a clean slate. Best advise given
to me came from Paul Graham:

Having functional programming as an ideal doesn’t imply that programs
should never have side effects. It just means that they should have no
more than necessary. It may take time to develop this habit. One way
to start is to treat the following operators as if there were a tax on
their use:
set setq setf psetf psetq incf decf push pop pushnew
rplaca rplacd rotatef shiftf remf remprop remhash
and also let*, in which imperative programs often lie concealed.
Treating these operators as taxable is only proposed as a help toward,
not a criterion for, good Lisp style. However, this alone can get you
surprisingly far.

Functional style is an arcane art, if you master it you've mastered
the full power of lambda. And to show you the difference take a look
at the programs at the bottom of this article:
http://www.vector.org.uk/?vol=24&no=2&art=blazeski


> However, many string manipulations can be accomplished by using the
> format macro. I have read that purist Lispers don't like to use that
> macro (ditto for the Loop one).
Its best to use them for a while and make your own mind on this
matter. I'm not a fan of loop or format but use them now and then when
they're best tool for the job.
>
> In the end, does it matter?

The question that only you could answer, that is. - Yoda
From: vanekl on
floaiza wrote:
> Five possible ways have been suggested to address the question of how
> to generate a string that is N times some component string, e.g., "-".
> ...
> QUESTION: Is there one or more criteria to decide which one to use?

Picking the algorithm that CONSes the least usually pays off, assuming you
need to optimize. More on that below.


> Speed of execution is mentioned as something to keep in mind; is this
> the only criterion?

No. Don't create garbage (memory allocations) if you can help it. Write a
long-running program and you'll figure out why.


> Should one time each solution and then go with the fastest one?

No. There lies madness. Micro-optimization should be delayed as long as
possible unless there is a black-and-white project goal that isn't being
met, and benchmarks prove it out. Remember Amdahl's law. Don't guess --
measure. Fix the root problem and move on.


> QUESTION: How does an expert CL programmer go about deciding how to
> put together the code for an application?

I'm not an expert CL programmer, but I code. Concern yourself with
high-level architecture and algorithms. Don't worry too much about
implementation details. CL is one of the easier languages to refactor,
especially if you have been writing decoupled code. Don't expect to get it
right the first time. Don't beat yourself up because you didn't write the
"perfect" solution. Real artists ship, to quote The Steve.


> As suggested in some of the responses one should avoid writing long
> functions, and choose instead a style where each 'result' is
> controlled by a small function with little or no 'side effects' (the
> functional programming paradigm).

Good advice.


> However, many string manipulations can be accomplished by using the
> format macro. I have read that purist Lispers don't like to use that
> macro (ditto for the Loop one).
>
> In the end, does it matter?

Some people don't like LOOP because it isn't Lispy. I think ITER is more
powerful and more lispy so I lean towards that. I think DO is archaic. Pick
a style that seems logical to you and don't constantly second-guess
yourself. Iteration is essential and often used. Having a number of
iteration constructs in your toolbox will serve you well. CL is the opposite
to Python in this respect. When Pythonists find a new hammer they throw it
back because they already have one in their toolbox. When a CLer finds
something intriguing they don't have any reservations about putting it in
the toolbox until a situation presents itself where it becomes useful.

There is no one correct answer, and you can always move to something that
works better as your experience grows. Just take a look at the CL language;
it's obvious it was designed by committee yet it still works. That indicates
to me that programmers shouldn't get hung up on perfecting the 3rd and 4th
principal components -- perfect the first-order principal components. That's
what the CL designers did. All the major design decisions are sound. There
are warts but they recede in significance. Attack your projects the same
way.

Be wary of adding unnecessary macros; you will end up writing your own
language that nobody but yourself can understand. However, if you are trying
to create a domain-specific language, a liberal use of macros may be
necessary. There are always trade-offs; experience will guide you when you
should bend the rules to fit the problem.


> =========
>
> Ariel Badichi recommends not to couple the application that does the
> display of the data returned by an SQL SELECT query with the database
> access proper.

Good call.


> Is security the main reason for such a recommendation? Or is there
> something else that supports it?

Security has nothing to do with that recommendation (data should always be
cleansed before sending it to the database). Always strive to decouple your
code from your persistence mechanism. Always try to write decoupled code.
Your code will be easier to maintain, fix, change, read, and re-use.
But don't get hung-up on it, either. It's not an end in itself. If you are
just doing exploratory programming, writing the perfect function shouldn't
be high on your priorities. You may be throwing the code away anyway, or
using it for only a short period.

> =========
>
> I recognize that some of questions above are "open ended", but given
> the plethora of choices, I am trying to figure out what I should look
> for generally when making a decision regarding which solution to use.

Experience is the best teacher. Get your hands dirty and do a variety of
work that forces you to learn new things. Read code. Don't be afraid to
throw old code out and rewrite it. Take everything you hear with a grain of
salt, including this. Try to solve problems yourself before asking
questions, but don't be afraid of looking stupid if you need to ask. People
who give you advice have agendas and motivations that may not coincide with
yours.

One last thing. Try to relax and have some fun. The people who die with
perfect code do not win a prize, no matter what your manager tells you.

> Thanks,
>
> Francisco


From: Thomas A. Russ on
"vanekl" <vanek(a)acd.net> writes:

> floaiza wrote:
> > Five possible ways have been suggested to address the question of how
> > to generate a string that is N times some component string, e.g., "-".
> > ...
> > QUESTION: Is there one or more criteria to decide which one to use?
>
> Picking the algorithm that CONSes the least usually pays off, assuming you
> need to optimize. More on that below.
>
>
> > Speed of execution is mentioned as something to keep in mind; is this
> > the only criterion?
>
> No. Don't create garbage (memory allocations) if you can help it. Write a
> long-running program and you'll figure out why.

Well, these garbage collection concerns were once a lot more important
than they are today. Short-lived garbage is often no longer a problem
with the use of a good generational garbage collector.

Perhaps the most important consideration is to make sure that you don't
end up using a hopelessly inefficient algorithm. Lisp can often lead
new programmers astray because there are a lot of convenient constructs
that hide their true complexity. No amount of micro-optimization will
save you from algorithms with terrible asymptotic performance.

Consider the classic case of adding things to the end of a list.

(defun square-list (list-of-numbers)
"Don't write programs like this one!"
(let ((result nil))
(dolist (n list-of-numbers)
(setq result (append result (list (* n n)))))
result))

It doesn't matter what you do to the compuation of (* n n) because you
will be killed by the N^2 behavior of the repeated calls to APPEND.

--
Thomas A. Russ, USC/Information Sciences Institute