From: Cecil Westerhof on
I can not find much about case, but I found the following:
case is a conditional that chooses one of its clauses to execute by
comparing a value to various constants, which are typically keyword
symbols, integers, or characters (but may be any objects).

So I would expect that I can use strings in a case statement.

I tried the following:
(defun do-command (this-command)
(case this-command
("d" (display-recipe-names))
(otherwise (format t "#~a# is an unknown command~%" this-command))))

But when feeding this "d" I get:
#d# is an unknown command

So properly I need to do something special to use strings. How would I
do this?

--
Cecil Westerhof
Senior Software Engineer
LinkedIn: http://www.linkedin.com/in/cecilwesterhof
From: Pascal J. Bourguignon on
Cecil Westerhof <Cecil(a)decebal.nl> writes:

> I can not find much about case, but I found the following:
> case is a conditional that chooses one of its clauses to execute by
> comparing a value to various constants, which are typically keyword
> symbols, integers, or characters (but may be any objects).
>
> So I would expect that I can use strings in a case statement.

Indeed, you can.


> I tried the following:
> (defun do-command (this-command)
> (case this-command
> ("d" (display-recipe-names))
> (otherwise (format t "#~a# is an unknown command~%" this-command))))
>
> But when feeding this "d" I get:
> #d# is an unknown command

No, that's the problem. You are not feeding it THIS "d". You are
feeding it another "d".

Read closely clhs case, and notice what equality operator is used to
match the cases.

> So properly I need to do something special to use strings. How would I
> do this?

Here is a way to do it as you want:


C/USER[30]> (let ((this-d #1="d"))
(defun get-this-d () #1#)
(defun do-command (this-command)
(case this-command
(#1# (print '(display-recipe-names)))
(otherwise (format t "~S is an unknown command~%" this-command)))))
DO-COMMAND
C/USER[31]> (do-command (get-this-d))

(DISPLAY-RECIPE-NAMES)
(DISPLAY-RECIPE-NAMES)
C/USER[32]> (do-command #|another|#"d")
"d" is an unknown command
NIL
C/USER[33]>



Another way would be to intern the string (you don't want to do that
on strings comming from the outside word because of the risks of DOS
attacks), or go thru a hash-table, to identify the CASE, or just do
not use CASE, but COND or a STRING-CASE macro of your own or from a
library.

--
__Pascal Bourguignon__ http://www.informatimago.com/
From: Thomas A. Russ on
pjb(a)informatimago.com (Pascal J. Bourguignon) writes:

> Another way would be to intern the string (you don't want to do that
> on strings comming from the outside word because of the risks of DOS
> attacks),

What is the DOS attack scenario?

Exhaustion of memory by interning too many different strings?
Presenting really long strings to intern?

I suppose one way around that would be to use symbols as your keys and
then use FIND-SYMBOL on the incoming strings instead of INTERN. Would
that be safe against the DOS attack?

--
Thomas A. Russ, USC/Information Sciences Institute
From: Thomas A. Russ on
Cecil Westerhof <Cecil(a)decebal.nl> writes:

> I can not find much about case, but I found the following:
> case is a conditional that chooses one of its clauses to execute by
> comparing a value to various constants, which are typically keyword
> symbols, integers, or characters (but may be any objects).

Well, the key item you need to consider for any of these comparison
constructs is to make sure you understand what predicate is used to do
the comparison for the selection of the items. From the HyperSpec:

"If the test-key is the SAME as any key for that clause, the forms in
that clause are evaluated as an implicit progn, and the values it
returns are returned as the value of the case, ccase, or ecase form."

So, now we need to know what "SAME" means in this context. The
HyperSpec definition that is relevant is the second one:

SAME: "... 2. (of objects if no predicate is implied by context)
indistinguishable by eql."

So that means that EQL is the test used for matching. And constant
strings with the same characters may or may not be EQL, and this can
differ depending on whether the function or file is compiled or not.

> So I would expect that I can use strings in a case statement.
>
> I tried the following:
> (defun do-command (this-command)
> (case this-command
> ("d" (display-recipe-names))
> (otherwise (format t "#~a# is an unknown command~%" this-command))))
>
> But when feeding this "d" I get:
> #d# is an unknown command
>
> So properly I need to do something special to use strings. How would I
> do this?

Yes. You either need to make sure that they are the same object (which
can be a bit tricky for strings but is doable), or you need to INTERN
the strings and use symbols (which is the easy way to make it work) or
you need to use one of the 3rd party utility libraries that implements a
different CASE-like statement that can handle strings.

Or, as an exercise in macro writing, you could write your own
STRING-CASE macro. That might be a nice learning exercise in its own
right.

One method of making it work would be:

(progn ;; Needed to have a single READER context
(defconstant D-COMMAND #1="d")
(defun do-command (this-command)
(case this-command
(#1# (format t "Found command ~A" this-command))
(otherwise (format t "#~a# is an unknown command~%" this-command)))))

But to use this, you need to specify the stored constant D-COMMAND:

(do-command d-command)
Found command d
NIL

(do-command "d")
#d# is an unknown command
NIL

So you can't use this to execute commands that you get, for example, by
reading in terminal input. To do that you would need to either intern
the string or use a different case structure.

There is actually a cleverer solution that uses a hash table and
registered functions that doesn't even use case at all. It is nicely
extensible and very general. And it is dead simple to implement in
Lisp.


--
Thomas A. Russ, USC/Information Sciences Institute
From: Pascal J. Bourguignon on
tar(a)sevak.isi.edu (Thomas A. Russ) writes:

> pjb(a)informatimago.com (Pascal J. Bourguignon) writes:
>
>> Another way would be to intern the string (you don't want to do that
>> on strings comming from the outside word because of the risks of DOS
>> attacks),
>
> What is the DOS attack scenario?
>
> Exhaustion of memory by interning too many different strings?
> Presenting really long strings to intern?

Yes.


> I suppose one way around that would be to use symbols as your keys and
> then use FIND-SYMBOL on the incoming strings instead of INTERN. Would
> that be safe against the DOS attack?

Yes, FIND-SYMBOL would do.


--
__Pascal Bourguignon__ http://www.informatimago.com/