patternMinor
Creating a repetitive string in Common Lisp
Viewed 0 times
lispcreatingrepetitivestringcommon
Problem
I needed to have a Lisp function that could produce a string of a certain length, created by repeated concatenations of a another string given as argument (so for example by giving 10 and "abc" I could obtain "abcabcabca"). I came up with a perverse idea of using a circular list of characters in combination with the
loop macro. I'm not really aiming at great performance, but I'm wondering what are the disadvantages of this approach and what could be improved. I also added a switch indicating at which end to start filling the string.(defun pad-with-string (n str &key (from-end nil))
"Create a string of length `n` filled with repeating instances of `str`.
If `from-end` is T the string is filled from the end."
(labels ((circular-list (&rest elements)
(let ((cycle (copy-list elements)))
(nconc cycle cycle))))
(loop :repeat n
:for c
:in (apply #'circular-list (coerce (if from-end (nreverse str) str) 'list))
:collect c
:into chars
:finally (return (coerce (if from-end (nreverse chars) chars) 'string)))))Solution
Not a bad approach.
I can see one arguable bug, and have a few style notes on your code though. Lets start with the bug:
Is that what you were expecting? It's happening because you call
You're using
You've got the
thanks to Rainer for pointing this out
The result of the
Not a hard and fast rule, but people typically use symbols rather than keywords for
Again, there's not really a standard for this, but I tend to like putting conditions and their effects/modifiers on the same line.
So,
I can see one arguable bug, and have a few style notes on your code though. Lets start with the bug:
CL-USER> (defparameter a "test")
A
CL-USER> (pad-with-string 6 a :from-end t)
"tsetts"
CL-USER> a
"tset"
CL-USER>Is that what you were expecting? It's happening because you call
nreverse on your input argument if from-end is passed. When I've got a situation where I have to mutate parameters, I follow the Scheme convention and name the procedure with a trailing bang (so, like pad-with-string!). Your comments state that you don't care about efficiency though, which leads me to believe you could just use the standard reverse rather than nreverse.You're using
apply on circular list, but you're defining it locally and only calling it in one place. In this situation, I'd kill the &rest and just call the function...
(labels ((circular-list (elements)
(let ((cycle (copy-list elements)))
(nconc cycle cycle))))
(loop :repeat n
:for c
:in (circular-list (coerce (if from-end (nreverse str) str) 'list))
...You've got the
(if from-end (reverse a) a) pattern in a couple of places. I'd pull that out into an additional local definition....
(maybe-reverse (thing)
(if from-end (reverse thing) thing)))
(loop :repeat n
:for c
:in (circular-list (coerce (maybe-reverse str) 'list))
:collect c
:into chars
:finally (return (coerce (maybe-reverse chars) 'string)))))thanks to Rainer for pointing this out
The result of the
collect call is returned implicitly at the end of the loop unless you give it an explicit name. Which means you can avoid finally (return ...) by wrapping those transformations around the loop itself:...
(coerce
(maybe-reverse
(loop :repeat n
:for c
:in (circular-list (coerce (maybe-reverse str) 'list))
:collect c))
'string)))Not a hard and fast rule, but people typically use symbols rather than keywords for
loop words. So, ...
(coerce
(maybe-reverse
(loop repeat n
for c
in (circular-list (coerce (maybe-reverse str) 'list))
collect c))
'string)))Again, there's not really a standard for this, but I tend to like putting conditions and their effects/modifiers on the same line.
...
(coerce
(maybe-reverse
(loop repeat n
for c in (circular-list (coerce (maybe-reverse str) 'list))
collect c))
'string)))So,
(defun pad-with-string (n str &key (from-end nil))
(labels ((circular-list (elements)
(let ((cycle (copy-list elements)))
(nconc cycle cycle)))
(maybe-reverse (thing)
(if from-end (reverse thing) thing)))
(coerce (maybe-reverse
(loop repeat n
for c in (circular-list (coerce (maybe-reverse str) 'list))
collect c))
'string)))Code Snippets
CL-USER> (defparameter a "test")
A
CL-USER> (pad-with-string 6 a :from-end t)
"tsetts"
CL-USER> a
"tset"
CL-USER>...
(labels ((circular-list (elements)
(let ((cycle (copy-list elements)))
(nconc cycle cycle))))
(loop :repeat n
:for c
:in (circular-list (coerce (if from-end (nreverse str) str) 'list))
......
(maybe-reverse (thing)
(if from-end (reverse thing) thing)))
(loop :repeat n
:for c
:in (circular-list (coerce (maybe-reverse str) 'list))
:collect c
:into chars
:finally (return (coerce (maybe-reverse chars) 'string)))))...
(coerce
(maybe-reverse
(loop :repeat n
:for c
:in (circular-list (coerce (maybe-reverse str) 'list))
:collect c))
'string)))...
(coerce
(maybe-reverse
(loop repeat n
for c
in (circular-list (coerce (maybe-reverse str) 'list))
collect c))
'string)))Context
StackExchange Code Review Q#48580, answer score: 3
Revisions (0)
No revisions yet.