Prev: What do you recommend for documentation generation?
Next: Utility to swap Caps Lock as Control key, or swap Control withAlt
From: floaiza on 10 Mar 2010 10:50 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 10 Mar 2010 15:18 > > 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 10 Mar 2010 15:46 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 doesnt 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 10 Mar 2010 19:36 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 10 Mar 2010 21:00
"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 |