Hi,
While I'm writing a simple textual spreadsheet and I've noticed that
several functions/methods have the same argument: the spreadsheet as 2
dimensional array:
...
(defun eval-spreadsheet (spreadsheet)
(let ((output (make-array (array-dimensions spreadsheet))))
(dotimes (row (array-dimension spreadsheet 0) output)
(dotimes (column (array-dimension spreadsheet 1))
(setf (aref output row column) (eval-cell (+ column 1) (+ row 1)
spreadsheet))))))
(defmethod eval-cell ((column string) row spreadsheet)
(eval-cell (string-number column) row spreadsheet))
(defmethod eval-cell ((column integer) row spreadsheet)
(let ((cell (aref spreadsheet (- row 1) (- column 1))))
(if (eq (aref cell 0) #\=)
(apply-operation '+
(mapcar #'(lambda (substr)
(apply-operation '-
(mapcar (eval-operand spreadsheet)
(extract-operands substr '-))))
(extract-operands (remove #\= cell) '+)))
(parse-integer cell))))
(defun apply-operation (operation operands)
(if (= (length operands) 1)
(identity (car operands))
(apply (symbol-function operation) operands)))
(defun eval-operand (spreadsheet)
#'(lambda (operand)
(cond ((alpha-char-p (aref operand 0))
(eval-cell (scan-to-strings "\\D+" operand)
(parse-integer (scan-to-strings "\\d+" operand))
spreadsheet))
((and (eql (aref operand 0) #\-) (alpha-char-p (aref operand 1)))
(- (eval-cell (scan-to-strings "\\D+" (remove #\- operand))
(parse-integer (scan-to-strings "\\d+" operand))
spreadsheet)))
(t (parse-integer operand)))))
...
Then I decided that perhaps it would be better to define a global
variable (*current-spreadsheet*) and then the outer function
(eval-spreadsheet) reallocate the same variable so that I can remove the
spreadsheet argument from the inner methods (sorry, in the code there
are also other small changes):
(defvar *spreadsheet*)
(defun eval-spreadsheet (spreadsheet)
(let ((*spreadsheet* spreadsheet)
(output (make-array (array-dimensions spreadsheet))))
(dotimes (row (array-dimension *spreadsheet* 0) output)
(dotimes (column (array-dimension *spreadsheet* 1))
(setf (aref output row column) (eval-cell (+ column 1) (+ row 1)))))))
(defmethod eval-cell ((column string) row)
(eval-cell (string-number column) row))
(defmethod eval-cell ((column integer) row)
(let ((cell (aref *spreadsheet* (- row 1) (- column 1))))
(if (eq (aref cell 0) #\=)
(let ((expr (remove #\= cell)))
(apply-operation '+
(mapcar #'(lambda (expr)
(apply-operation '-
(mapcar #'eval-operand
(extract-operands expr '-))))
(extract-operands expr '+))))
(parse-integer cell))))
(defun apply-operation (operation operands)
(if (= (length operands) 1)
(identity (car operands))
(apply (symbol-function operation) operands)))
(defun eval-operand (operand)
(cond ((alpha-char-p (aref operand 0))
(eval-cell (scan-to-strings "\\D+" operand)
(parse-integer (scan-to-strings "\\d+" operand))))
((and (eql (aref operand 0) #\-) (alpha-char-p (aref operand 1)))
(- (eval-operand (remove #\- operand))))
(t (parse-integer operand))))
I'm not a expert lisp and then I'd like to know how you consider this
use of dynamic variable. Is Good?
I've thought if CLOS can help me but I think that in these case I think
no, is right?
The last question: in this spreadsheet I have to evaluate cell with
in-fixed math epxressions (ex. =3*2+5*A1) so that I decided to use the
regular expressions to split it in subexpression (currently only + and
-) to evaluate single math operation. Is there a simpler solution? I've
thought I could parse the expression to build a syntax tree but I've
never done one and I think that for the +,-,* and / is too complex, is
right?
Thanks for the help.
Giannandrea
Giannandrea Castaldi wrote:
> Then I decided that perhaps it would be better to define a global
> variable (*current-spreadsheet*) and then the outer function
> (eval-spreadsheet) reallocate the same variable so that I can remove the
> spreadsheet argument from the inner methods (sorry, in the code there
> are also other small changes):
That technique is useful when you are sitting on top of a huge call tree
of many different functions, including sometimes intermediate level
utiltity functions which are not spreadsheet specific, but which will
call other functions that need to see the spreadsheet. Then it is
painful to pass the spreadsheet around as an argument.
But in your code, maybe I missed something, but I only see a few
functions and one place the global gets used. Do you imagine the
application growing a lot?
kt
--
Home? http://tilton-technology.com
Cells? http://www.common-lisp.net/project/cells/
Cello? http://www.common-lisp.net/project/cello/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
Your Project Here! http://alu.cliki.net/Industry%20Application
Kenny Tilton wrote:
>
>
> Giannandrea Castaldi wrote:
>
>
>> Then I decided that perhaps it would be better to define a global
>> variable (*current-spreadsheet*) and then the outer function
>> (eval-spreadsheet) reallocate the same variable so that I can remove
>> the spreadsheet argument from the inner methods (sorry, in the code
>> there are also other small changes):
>
>
> That technique is useful when you are sitting on top of a huge call tree
> of many different functions, including sometimes intermediate level
> utiltity functions which are not spreadsheet specific, but which will
> call other functions that need to see the spreadsheet. Then it is
> painful to pass the spreadsheet around as an argument.
>
> But in your code, maybe I missed something, but I only see a few
> functions and one place the global gets used. Do you imagine the
> application growing a lot?
Now I'll add * and / but I thought seeing that already now eval-cell and
eval-operand calls each other passing spreadsheet it's useful use it as
dynamic variables. Beasides in this way I can directly use eval-operand
as mapcar argument semplifing both its use and definition.
Another question: I've never used multi-threading in common-lisp but I
suppose that dynamic variables don't introduce any conflict among
threads beacause the variable are allocated on the thread frames. Is right?
Thanks.
Giannandrea
>
> kt
>
Giannandrea Castaldi wrote:
> The last question: in this spreadsheet I have to evaluate cell with in-fixed
> math epxressions (ex. =3*2+5*A1) so that I decided to use the regular expressions
> to split it in subexpression (currently only + and -) to evaluate single math
> operation. Is there a simpler solution?
Have a look at the infix library by Mark Kantrowitz. It translates infix
expressions to Lisp's S-expressions. You could probably just use his parser.
Arthur Lemmens
In article <················@news.xs4all.nl>, Arthur Lemmens
<········@xs4all.nl> wrote:
> Giannandrea Castaldi wrote:
>
> > The last question: in this spreadsheet I have to evaluate cell with in-fixed
> > math epxressions (ex. =3*2+5*A1) so that I decided to use the regular
expressions
> > to split it in subexpression (currently only + and -) to evaluate
single math
> > operation. Is there a simpler solution?
>
> Have a look at the infix library by Mark Kantrowitz. It translates infix
> expressions to Lisp's S-expressions. You could probably just use his parser.
>
> Arthur Lemmens
Or try my "parcil" parser.
E.
In article <···········@lacerta.tiscalinet.it>,
Giannandrea Castaldi <········@tiscali.it> wrote:
> Hi,
> While I'm writing a simple textual spreadsheet and I've noticed that
> several functions/methods have the same argument: the spreadsheet as 2
> dimensional array:
> ...
> (defun eval-spreadsheet (spreadsheet)
> (let ((output (make-array (array-dimensions spreadsheet))))
> (dotimes (row (array-dimension spreadsheet 0) output)
> (dotimes (column (array-dimension spreadsheet 1))
> (setf (aref output row column) (eval-cell (+ column 1) (+ row 1)
> spreadsheet))))))
>
>
> (defmethod eval-cell ((column string) row spreadsheet)
> (eval-cell (string-number column) row spreadsheet))
[...]
>
> Then I decided that perhaps it would be better to define a global
> variable (*current-spreadsheet*) and then the outer function
> (eval-spreadsheet) reallocate the same variable so that I can remove the
> spreadsheet argument from the inner methods (sorry, in the code there
> are also other small changes):
[...]
Given the structure of your code, I sometimes use global variables.
Specifically, in your original set of functions, the SPREADSHEET
parameter is always last. I'll change these parameters to optional ones
that default to the value of *SPREADSHEET*, e.g.:
(defmethod eval-cell ((column string) row spreadsheet)
(eval-cell (string-number column) row spreadsheet))
==>
(defmethod eval-cell ((column string) row &optional (spreadsheet
*spreadsheet*)
(eval-cell (string-number column) row spreadsheet))
Then you get the best of both worlds. If an explicit SPREADSHEET
argument is supplied, then it is used. Otherwise, it defaults to the
value of *SPREADSHEET*. And, as you do in your revised code:
> (defvar *spreadsheet*)
> (defun eval-spreadsheet (spreadsheet)
> (let ((*spreadsheet* spreadsheet)
> (output (make-array (array-dimensions spreadsheet))))
> (dotimes (row (array-dimension *spreadsheet* 0) output)
> (dotimes (column (array-dimension *spreadsheet* 1))
> (setf (aref output row column) (eval-cell (+ column 1) (+ row 1)))))))
[...]
it's easy to have a top-level function that binds *SPREADSHEET*
to the desired value and then calls the other functions, which
default as appropriate.
Giannandrea Castaldi wrote:
> Hi,
> While I'm writing a simple textual spreadsheet and I've noticed that
> several functions/methods have the same argument: the spreadsheet as 2
> dimensional array:
> ...
> Then I decided that perhaps it would be better to define a global
> variable (*current-spreadsheet*) and then the outer function
> (eval-spreadsheet) reallocate the same variable so that I can remove the
> spreadsheet argument from the inner methods.
In my (long) experience, this is almost always a bad idea. Never use a
global variable unless you have a good argument that you will _never_
need to have two such objects in existence at once. For spreadsheets,
it seems that you almost certainly _will_ require multiple spreadsheets.
Then you'll have to rewrite all of your code to evolve it back to
where it is now.
There are a couple of other ways to cut down on "globalish" variables
that get passed around a lot without being modified very often:
1 Use 'labels', or a variant with a more flexible syntax, to make many
of your functions local. If F has a variable 'spreadsheet,' all the
local functions can access it lexically.
2 If there are several 'globalish' variables, consider defining a data
structure that packages them all up. Then pass that data structure as
an argument with a name such as 'context'.
-- Drew McDermott
Drew McDermott <··················@at.yale.dot.edu> writes:
> Giannandrea Castaldi wrote:
>> Then I decided that perhaps it would be better to define a global
>> variable (*current-spreadsheet*) and then the outer function
>> (eval-spreadsheet) reallocate the same variable so that I can remove
>> the spreadsheet argument from the inner methods.
>
> In my (long) experience, this is almost always a bad idea. Never use a
> global variable unless you have a good argument that you will _never_
> need to have two such objects in existence at once. For spreadsheets,
> it seems that you almost certainly _will_ require multiple spreadsheets.
> Then you'll have to rewrite all of your code to evolve it back to where
> it is now.
Ah, but CL doesn't have global (variable) variables. :)
--
Rahul Jain
·····@nyct.net
Professional Software Developer, Amateur Quantum Mechanicist
* Drew McDermott wrote:
> In my (long) experience, this is almost always a bad idea. Never use
> a global variable unless you have a good argument that you will
> _never_ need to have two such objects in existence at once. For
> spreadsheets, it seems that you almost certainly _will_ require
> multiple spreadsheets. Then you'll have to rewrite all of your code
> to evolve it back to where it is now.
A special variable doesn't imply a global. Even if it's something
defined with DEFVAR that only implies a global *default*, which may be
null of course.
--tim
In article <············@news.wss.yale.edu>, Drew McDermott
<··················@at.yale.dot.edu> wrote:
> Giannandrea Castaldi wrote:
> > Hi,
> > While I'm writing a simple textual spreadsheet and I've noticed that
> > several functions/methods have the same argument: the spreadsheet as 2
> > dimensional array:
> > ...
> > Then I decided that perhaps it would be better to define a global
> > variable (*current-spreadsheet*) and then the outer function
> > (eval-spreadsheet) reallocate the same variable so that I can remove the
> > spreadsheet argument from the inner methods.
>
> In my (long) experience, this is almost always a bad idea. Never use a
> global variable unless you have a good argument that you will _never_
> need to have two such objects in existence at once.
One should qualify that with "... never need to have two such objects in
existence at once PER THREAD" because you can shadow a global binding with
dynamic bindings.
E.
·········@flownet.com (Erann Gat) writes:
> In article <············@news.wss.yale.edu>, Drew McDermott
> <··················@at.yale.dot.edu> wrote:
>
>> Giannandrea Castaldi wrote:
>> > Hi,
>> > While I'm writing a simple textual spreadsheet and I've noticed that
>> > several functions/methods have the same argument: the spreadsheet as 2
>> > dimensional array:
>> > ...
>> > Then I decided that perhaps it would be better to define a global
>> > variable (*current-spreadsheet*) and then the outer function
>> > (eval-spreadsheet) reallocate the same variable so that I can remove the
>> > spreadsheet argument from the inner methods.
>>
>> In my (long) experience, this is almost always a bad idea. Never use a
>> global variable unless you have a good argument that you will _never_
>> need to have two such objects in existence at once.
>
> One should qualify that with "... never need to have two such objects in
> existence at once PER THREAD" because you can shadow a global binding with
> dynamic bindings.
Having multiple threads gives you multiple instances, but since you
can only refer to the instance within your own thread, it isn't much
better than having only one global instance.
In article <············@ccs.neu.edu>, Joe Marshall <···@ccs.neu.edu> wrote:
> ·········@flownet.com (Erann Gat) writes:
>
> > In article <············@news.wss.yale.edu>, Drew McDermott
> > <··················@at.yale.dot.edu> wrote:
> >
> >> Giannandrea Castaldi wrote:
> >> > Hi,
> >> > While I'm writing a simple textual spreadsheet and I've noticed that
> >> > several functions/methods have the same argument: the spreadsheet as 2
> >> > dimensional array:
> >> > ...
> >> > Then I decided that perhaps it would be better to define a global
> >> > variable (*current-spreadsheet*) and then the outer function
> >> > (eval-spreadsheet) reallocate the same variable so that I can remove the
> >> > spreadsheet argument from the inner methods.
> >>
> >> In my (long) experience, this is almost always a bad idea. Never use a
> >> global variable unless you have a good argument that you will _never_
> >> need to have two such objects in existence at once.
> >
> > One should qualify that with "... never need to have two such objects in
> > existence at once PER THREAD" because you can shadow a global binding with
> > dynamic bindings.
>
> Having multiple threads gives you multiple instances, but since you
> can only refer to the instance within your own thread, it isn't much
> better than having only one global instance.
First, it is not necessarily true that you can only refer to the instance
in your own thread. MCL, for example, as SYMBOL-VALUE-IN-PROCESS.
Second, even if you can't refer to instances in other threads that is
vastly better than having only one instance per Lisp image for certain
applications. Multithreaded Web servers with a "connection" object per
thread come to mind.
E.
·········@flownet.com (Erann Gat) writes:
> In article <············@ccs.neu.edu>, Joe Marshall <···@ccs.neu.edu> wrote:
>
>> ·········@flownet.com (Erann Gat) writes:
>>
>> > In article <············@news.wss.yale.edu>, Drew McDermott
>> > <··················@at.yale.dot.edu> wrote:
>> >
>> >> Giannandrea Castaldi wrote:
>> >> > Hi,
>> >> > While I'm writing a simple textual spreadsheet and I've noticed that
>> >> > several functions/methods have the same argument: the spreadsheet as 2
>> >> > dimensional array:
>> >> > ...
>> >> > Then I decided that perhaps it would be better to define a global
>> >> > variable (*current-spreadsheet*) and then the outer function
>> >> > (eval-spreadsheet) reallocate the same variable so that I can remove the
>> >> > spreadsheet argument from the inner methods.
>> >>
>> >> In my (long) experience, this is almost always a bad idea. Never use a
>> >> global variable unless you have a good argument that you will _never_
>> >> need to have two such objects in existence at once.
>> >
>> > One should qualify that with "... never need to have two such objects in
>> > existence at once PER THREAD" because you can shadow a global binding with
>> > dynamic bindings.
>>
>> Having multiple threads gives you multiple instances, but since you
>> can only refer to the instance within your own thread, it isn't much
>> better than having only one global instance.
>
> First, it is not necessarily true that you can only refer to the instance
> in your own thread. MCL, for example, as SYMBOL-VALUE-IN-PROCESS.
Non-portable and clumsy.
> Second, even if you can't refer to instances in other threads that is
> vastly better than having only one instance per Lisp image for certain
> applications. Multithreaded Web servers with a "connection" object per
> thread come to mind.
Yes, but for a spreadsheet, it seems likely that one would want to use
two at once.
--
~jrm
In article <············@comcast.net>, Joe Marshall
<·············@comcast.net> wrote:
> > First, it is not necessarily true that you can only refer to the instance
> > in your own thread. MCL, for example, as SYMBOL-VALUE-IN-PROCESS.
>
> Non-portable
That's tautological. Threads are non-portable in Common Lisp.
> and clumsy.
No clumsier than SYMBOL-VALUE.
E.
Erann Gat wrote:
> That's tautological. Threads are non-portable in Common Lisp.
How much work would it be to make them portable?
What would that involve? How much cooperation from what sides?
Andr�
--
In article <············@ulric.tng.de>, =?ISO-8859-1?Q?Andr=E9_Thieme?=
<······································@justmail.de> wrote:
> Erann Gat wrote:
>
> > That's tautological. Threads are non-portable in Common Lisp.
>
> How much work would it be to make them portable?
> What would that involve? How much cooperation from what sides?
It depends on how portable you want them to be :-)
Among implementations that support threads it's not so hard beause all
their APIs are already more or less the same. On the Remote Agent project
we were able to move code that relied heavily on threads between Allegro,
Harlequin and MCL without too much trouble. (There are some superficial
differences, but these were easily addressed with macros.)
But there are popular Common Lisp implementations that don't support
threads at all, notably CLisp, and I believe CMUCL and its decendents.
And ECL claimed to have threads but they didn't work the last time I tried
them (but that was a very long time ago).
E.
·········@flownet.com (Erann Gat) wrote on Sat, 10 Apr 2004:
> But there are popular Common Lisp implementations that don't support
> threads at all, notably CLisp, and I believe CMUCL and its decendents.
CMUCL offers threads on x86 platforms, at least.
_______________________________________________________________________________
Don Geddis http://don.geddis.org/ ···@geddis.org
On Sat, 10 Apr 2004 10:19:48 -0700, ·········@flownet.com (Erann Gat) wrote:
> But there are popular Common Lisp implementations that don't support
> threads at all, notably CLisp, and I believe CMUCL and its
> decendents.
CMUCL has cooperative threads on Linux x86 and FreeBSD x86, SBCL (a
CMUCL descendant) has OS-level threads on Linux x86, Scieneer (another
CMUCL descendant) has OS-level threads on Solaris, HP-UX, and Linux -
32 bit and 64 bit.
Edi.
Erann Gat wrote:
>
> But there are popular Common Lisp implementations that don't support
> threads at all, notably CLisp, and I believe CMUCL and its decendents.
Huh?
Well, CMUCL itself only has "green" threading (in java terms), but it's
still pretty good. One of the main reasons for SBCL as far as I can see is
to experiment with implementing good OS threading in a CMUCL variant.
Though as far as I recall, you can't save core images if you use threads.
But I seldom do that - what am I, a smalltalker???
Erann Gat wrote:
> In article <············@ulric.tng.de>, =?ISO-8859-1?Q?Andr=E9_Thieme?=
> <······································@justmail.de> wrote:
>
>
>>Erann Gat wrote:
>>
>>
>>>That's tautological. Threads are non-portable in Common Lisp.
>>
>>How much work would it be to make them portable?
>>What would that involve? How much cooperation from what sides?
>
>
> It depends on how portable you want them to be :-)
>
> Among implementations that support threads it's not so hard beause all
> their APIs are already more or less the same. On the Remote Agent project
> we were able to move code that relied heavily on threads between Allegro,
> Harlequin and MCL without too much trouble. (There are some superficial
> differences, but these were easily addressed with macros.)
>
> But there are popular Common Lisp implementations that don't support
> threads at all, notably CLisp, and I believe CMUCL and its decendents.
> And ECL claimed to have threads but they didn't work the last time I tried
> them (but that was a very long time ago).
>
> E.
So at least it seems to me threads could be portable between LispWorks
and Allegro. It would also work to some degree for cmucl (and for sbcl?).
Such a threading package could later be extended to support any other
Lisp as soon it starts to support threads. How much work could such a
project possibly cost (in man hours)?
Andr�
--
Andr� Thieme wrote:
> Erann Gat wrote:
>
>> In article <············@ulric.tng.de>, =?ISO-8859-1?Q?Andr=E9_Thieme?=
>> <······································@justmail.de> wrote:
>>
>>
>>> Erann Gat wrote:
>>>
>>>
>>>> That's tautological. Threads are non-portable in Common Lisp.
>>>
>>>
>>> How much work would it be to make them portable?
>>> What would that involve? How much cooperation from what sides?
>>
>>
>>
>> It depends on how portable you want them to be :-)
>>
>> Among implementations that support threads it's not so hard beause all
>> their APIs are already more or less the same. On the Remote Agent
>> project
>> we were able to move code that relied heavily on threads between Allegro,
>> Harlequin and MCL without too much trouble. (There are some superficial
>> differences, but these were easily addressed with macros.)
>>
>> But there are popular Common Lisp implementations that don't support
>> threads at all, notably CLisp, and I believe CMUCL and its decendents.
>> And ECL claimed to have threads but they didn't work the last time I
>> tried
>> them (but that was a very long time ago).
>>
>> E.
>
>
> So at least it seems to me threads could be portable between LispWorks
> and Allegro. It would also work to some degree for cmucl (and for sbcl?).
> Such a threading package could later be extended to support any other
> Lisp as soon it starts to support threads. How much work could such a
> project possibly cost (in man hours)?
Very much. This is what you get when there are more than 1.8
implementations of your favourite language.
Cheers
marco
Andr� Thieme <······································@justmail.de> writes:
> Erann Gat wrote:
>
>> That's tautological. Threads are non-portable in Common Lisp.
>
> How much work would it be to make them portable?
Something similar has been done, see for example the file
src/port/proc.lisp in the CLOCC source tree.
Paolo
--
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
> Giannandrea Castaldi wrote:
> > Then I decided that perhaps it would be better to define a global variable
> > (*current-spreadsheet*) and then the outer function (eval-spreadsheet)
> > reallocate the same variable so that I can remove the spreadsheet argument
> > from the inner methods.
Drew McDermott <··················@at.yale.dot.edu> writes:
> In my (long) experience, this is almost always a bad idea. Never use a
> global variable unless you have a good argument that you will _never_ need to
> have two such objects in existence at once.
Dynamic variables let you have multiple instances, simply by binding the
variable to the appropriate instance before calling the relevant functions.
What's wrong with something roughly like this:
(defvar *current-spreadsheet*)
(defun eval-spreadsheet (spreadsheet)
(let ((*current-spreadsheet* spreadsheet))
... ))
I've used a structure much like that in a variety of applications, e.g. a
web server that had a client/thread context.
Dynamic variables don't seem incompatible with multiple instances to me.
And I sympathize with the OP's annoyance at passing the same silly "context"
argument into every single function.
-- Don
_______________________________________________________________________________
Don Geddis http://don.geddis.org/ ···@geddis.org
In a survey taken several years ago, all incoming freshman at MIT were asked if
they expected to graduate in the top half of their class. Ninety-seven percent
responded that they did.
Don Geddis <···@geddis.org> writes:
> What's wrong with something roughly like this:
> (defvar *current-spreadsheet*)
> (defun eval-spreadsheet (spreadsheet)
> (let ((*current-spreadsheet* spreadsheet))
> ... ))
>
It is difficult to write code that spans several spreadsheets this
way.
Don Geddis wrote:
> Dynamic variables let you have multiple instances, simply by binding the
> variable to the appropriate instance before calling the relevant functions.
>
> What's wrong with something roughly like this:
> (defvar *current-spreadsheet*)
> (defun eval-spreadsheet (spreadsheet)
> (let ((*current-spreadsheet* spreadsheet))
> ... ))
>
You're quite right. As are the others who pointed out much the same
thing. Even the OP
had a binding for *current-spreadsheet*. I jumped to a conclusion about
what he had
said, and responded to that instead of what he actually said. Sorry for
wasting everyone's brain bandwidth.
--
-- Drew McDermott
Yale Computer Science Department