From: Frank Goenninger DG1SBG
Subject: How to use constants in a case key form ?
Date: 
Message-ID: <lz63zsken3.fsf@de.goenninger.net>
I have something similar to

(defconstant +FIRST+  1)
(defconstant +SECOND+ 2)
(defconstant +THIRD+  3)

(defun test-case (value)
  (case value
    (( +FIRST+ +SECOND+ )
     "1 or 2")
    ((+THIRD+)
     "3")
    (otherwise "Something")))

The (case ....) macroexpands to

(LET ()
  (COND ((OR (EQL '+FIRST+ VALUE) (EQL '+SECOND+ VALUE)) "1 or 2")
        ((OR (EQL '+THIRD+ VALUE)) "3")
        (T "Something")))

and we have

CL-USER> (eql +FIRST+ 1)
T

CL-USER> (eql '+FIRST+ 1)
NIL

So far so bad. I have a real bunch of defconstants (about 200)
so... How do I get to use the constants in my case key forms?

Thx for any hints...

Frank 

-- 

  Frank Goenninger

  frgo(at)mac(dot)com

  "Don't ask me! I haven't been reading comp.lang.lisp long enough to 
  really know ..."

From: Geoff Wozniak
Subject: Re: How to use constants in a case key form ?
Date: 
Message-ID: <0e454050-e835-4022-8c07-e90f6c079db8@s36g2000prg.googlegroups.com>
On Nov 23, 1:20 pm, Frank Goenninger DG1SBG <·············@nomail.org>
wrote:
> So far so bad. I have a real bunch of defconstants (about 200)
> so... How do I get to use theconstantsin mycasekey forms?
>

One way is to write your own case macro.  Another is to evaluate the
constants at read time.

CL-USER> (defconstant +first+ 1)
+FIRST+
CL-USER> (let ((x 1))
        (case x
          (#.+first+ 'first!)
          (t 'something-else!)))
FIRST!

This assumes that the constants are available when the case form is
read.  It also assumes you recompile the forms that depend on the
constants if you change the constants.

The discussion "Putting evaluated constants in a CASE" from 2004 in
this newsgroup may be helpful.
From: Zach Beane
Subject: Re: How to use constants in a case key form ?
Date: 
Message-ID: <m3ejegitjk.fsf@unnamed.xach.com>
Geoff Wozniak <·············@gmail.com> writes:

> This assumes that the constants are available when the case form is
> read.  It also assumes you recompile the forms that depend on the
> constants if you change the constants.

This is important to remember. I use the #.+FOO+ form all the time,
but CLISP needs an EVAL-WHEN around the defconstant form to ensure the
value is visible at compile-time.

Zach
From: Willem Broekema
Subject: Re: How to use constants in a case key form ?
Date: 
Message-ID: <68112139-d466-4b73-92c3-177a709850ac@l1g2000hsa.googlegroups.com>
On Nov 23, 9:41 pm, Zach Beane <····@xach.com> wrote:
> Geoff Wozniak <·············@gmail.com> writes:
> > This assumes that the constants are available when the case form is
> > read.  It also assumes you recompile the forms that depend on the
> > constants if you change the constants.
>
> This is important to remember. I use the #.+FOO+ form all the time,
> but CLISP needs an EVAL-WHEN around the defconstant form to ensure the
> value is visible at compile-time.

This sounds wrong to me. The form prefixed by #. is evaluated at read
time, not compile time. If the constant's value should be known at
read time, the defconstant form must have been evaluated at read time
- and there is no eval-when clause for that.

I think the following is an appropriate way to define a constant and
use its value as case clause value, both in the same file:

(progn #.(progn #1=(defconstant +foo+ 1) nil) ;; declare constant at
read time; nil as harmless result
       #1#)  ;; also have the normal effect of defconstant

(defun f (x)
  (case x
    (#.+foo+ 42)))


- Willem
From: Zach Beane
Subject: Re: How to use constants in a case key form ?
Date: 
Message-ID: <m34pfcibtm.fsf@unnamed.xach.com>
Willem Broekema <········@gmail.com> writes:

>> This is important to remember. I use the #.+FOO+ form all the time,
>> but CLISP needs an EVAL-WHEN around the defconstant form to ensure the
>> value is visible at compile-time.
>
> This sounds wrong to me. The form prefixed by #. is evaluated at read
> time, not compile time. 

COMPILE-FILE reads successive forms in a file. #. happens at read
time, but that read time may also be compile time.

> If the constant's value should be known at read time, the
> defconstant form must have been evaluated at read time - and there
> is no eval-when clause for that.

:COMPILE-TOPLEVEL is sufficient.

In CLISP, the following errors during COMPILE-FILE:

  (defconstant +foo+ 5)

  (defun bar (arg)
    (case arg
      (#.+foo+ (print 'WHEE))
      (t (print 'BOO))))

The following works fine:

  (eval-when (:compile-toplevel)
    (defconstant +foo+ 5))

  (defun bar (arg)
    (case arg
      (#.+foo+ (print 'WHEE))
      (t (print 'BOO))))

I think this is consistent with the behavior described by 3.2.3 and
the documentation for DEFCONSTANT.

Zach
From: Willem Broekema
Subject: Re: How to use constants in a case key form ?
Date: 
Message-ID: <361b507b-4751-4f2e-b48a-1cb01b731898@y43g2000hsy.googlegroups.com>
On Nov 24, 4:04 am, Zach Beane <····@xach.com> wrote:
> > This sounds wrong to me. The form prefixed by #. is evaluated at read
> > time, not compile time.
>
> COMPILE-FILE reads successive forms in a file. #. happens at read
> time, but that read time may also be compile time.

Right, thanks for pointing that out.

> > If the constant's value should be known at read time, the
> > defconstant form must have been evaluated at read time - and there
> > is no eval-when clause for that.
>
> :COMPILE-TOPLEVEL is sufficient.

I think :execute is also necessary, to allow loading the file without
compiling.


- Willem
From: Ari Johnson
Subject: Re: How to use constants in a case key form ?
Date: 
Message-ID: <m2myt4yfnf.fsf@nibbler.theari.com>
Frank Goenninger DG1SBG <·············@nomail.org> writes:

> I have something similar to
>
> (defconstant +FIRST+  1)
> (defconstant +SECOND+ 2)
> (defconstant +THIRD+  3)
>
> (defun test-case (value)
>   (case value
>     (( +FIRST+ +SECOND+ )
>      "1 or 2")
>     ((+THIRD+)
>      "3")
>     (otherwise "Something")))
>
> The (case ....) macroexpands to
>
> (LET ()
>   (COND ((OR (EQL '+FIRST+ VALUE) (EQL '+SECOND+ VALUE)) "1 or 2")
>         ((OR (EQL '+THIRD+ VALUE)) "3")
>         (T "Something")))
>
> and we have
>
> CL-USER> (eql +FIRST+ 1)
> T
>
> CL-USER> (eql '+FIRST+ 1)
> NIL
>
> So far so bad. I have a real bunch of defconstants (about 200)
> so... How do I get to use the constants in my case key forms?
>
> Thx for any hints...

Take your choice:

(case ((#.+FIRST+ #.+SECOND+) ...)
      ...)

(defmacro eval-case (key &body forms)
  ...)
From: Frank Goenninger DG1SBG
Subject: Re: How to use constants in a case key form ?
Date: 
Message-ID: <lzwss8iz5m.fsf@de.goenninger.net>
Ari Johnson <·········@gmail.com> writes:

> Frank Goenninger DG1SBG <·············@nomail.org> writes:
>
>> I have something similar to
>>
>> (defconstant +FIRST+  1)
>> (defconstant +SECOND+ 2)
>> (defconstant +THIRD+  3)
>>
>> (defun test-case (value)
>>   (case value
>>     (( +FIRST+ +SECOND+ )
>>      "1 or 2")
>>     ((+THIRD+)
>>      "3")
>>     (otherwise "Something")))
>>
>> The (case ....) macroexpands to
>>
>> (LET ()
>>   (COND ((OR (EQL '+FIRST+ VALUE) (EQL '+SECOND+ VALUE)) "1 or 2")
>>         ((OR (EQL '+THIRD+ VALUE)) "3")
>>         (T "Something")))
>>
>> and we have
>>
>> CL-USER> (eql +FIRST+ 1)
>> T
>>
>> CL-USER> (eql '+FIRST+ 1)
>> NIL
>>
>> So far so bad. I have a real bunch of defconstants (about 200)
>> so... How do I get to use the constants in my case key forms?
>>
>> Thx for any hints...
>
> Take your choice:
>
> (case ((#.+FIRST+ #.+SECOND+) ...)
>       ...)
>
> (defmacro eval-case (key &body forms)
>   ...)

Ahhhh!!! The light just got switched on ... :-) Thanks!! (I will be
using the #. approach).

Thx!

Frank

-- 

  Frank Goenninger

  frgo(at)mac(dot)com

  "Don't ask me! I haven't been reading comp.lang.lisp long enough to 
  really know ..."
From: Carl Taylor
Subject: Re: How to use constants in a case key form ?
Date: 
Message-ID: <l8F1j.44172$if6.22264@bgtnsc05-news.ops.worldnet.att.net>
Frank Goenninger DG1SBG wrote:
> I have something similar to
> 
> (defconstant +FIRST+  1)
> (defconstant +SECOND+ 2)
> (defconstant +THIRD+  3)
> 
> (defun test-case (value)
>  (case value
>    (( +FIRST+ +SECOND+ )
>     "1 or 2")
>    ((+THIRD+)
>     "3")
>    (otherwise "Something")))
> 
> The (case ....) macroexpands to
> 
> (LET ()
>  (COND ((OR (EQL '+FIRST+ VALUE) (EQL '+SECOND+ VALUE)) "1 or 2")
>        ((OR (EQL '+THIRD+ VALUE)) "3")
>        (T "Something")))
> 
> and we have
> 
> CL-USER> (eql +FIRST+ 1)
> T
> 
> CL-USER> (eql '+FIRST+ 1)
> NIL
> 
> So far so bad. I have a real bunch of defconstants (about 200)
> so... How do I get to use the constants in my case key forms?


Sharp-dot them!

Carl Taylor


CL-USER 9 > 

(defun test-case (value)
  (case value
    (( #.+FIRST+ #.+SECOND+ )
     "1 or 2")
    ((#.+THIRD+)
     "3")
    (otherwise "Something")))
TEST-CASE

CL-USER 10 > (test-case 1)
"1 or 2"


CL-USER 11 > (test-case 3)
"3"

CL-USER 12 > (test-case 5)
"Something"
From: Kamen TOMOV
Subject: Re: How to use constants in a case key form ?
Date: 
Message-ID: <uhcjb4y3z.fsf@cybuild.com>
What if one wants:

(let ((a 1))
  (case 1
    (a t)))

Although, this is not a real problem as I don't see any practical
reasons to use "case" and not "cond" in this case, I'm still
interested in the approach of solving it.

-- 
Kamen
From: John Thingstad
Subject: Re: How to use constants in a case key form ?
Date: 
Message-ID: <op.t2aruh1fut4oq5@pandora.alfanett.no>
P� Sat, 24 Nov 2007 13:38:40 +0100, skrev Kamen TOMOV <·····@cybuild.com>:

> What if one wants:
>
> (let ((a 1))
>   (case 1
>     (a t)))
>
> Although, this is not a real problem as I don't see any practical
> reasons to use "case" and not "cond" in this case, I'm still
> interested in the approach of solving it.
>

The defmethod approach perhaps?

(defmethod ((a (eql #.+val+))
    ...)
...

--------------
John Thingstad
From: Kamen TOMOV
Subject: Re: How to use constants in a case key form ?
Date: 
Message-ID: <u63zr4ux7.fsf@cybuild.com>
On Sat, Nov 24 2007, John Thingstad wrote:

> P� Sat, 24 Nov 2007 13:38:40 +0100, skrev Kamen TOMOV <·····@cybuild.com>:
>
>> What if one wants:
>>
>> (let ((a 1))
>>   (case 1
>>     (a t)))
>>
>> Although, this is not a real problem as I don't see any practical
>> reasons to use "case" and not "cond" in this case, I'm still
>> interested in the approach of solving it.
>>
>
> The defmethod approach perhaps?
>
> (defmethod ((a (eql #.+val+))
>    ...)
> ...

Hmm, what do you mean? It doesn't make sense to me. How would you code
the following form using defmethod?

(let ((a 1)
      (b 2)
      (c 3))
  (case 1
    (a "bar")
    (b "baz")
    (c "bav")))



-- 
Kamen
From: Chris Russell
Subject: Re: How to use constants in a case key form ?
Date: 
Message-ID: <4ffe015a-501b-42c6-9940-3b8c3fe4bd49@s6g2000prc.googlegroups.com>
On 24 Nov, 13:47, Kamen TOMOV <·····@cybuild.com> wrote:
> On Sat, Nov 24 2007, John Thingstad wrote:
> > På Sat, 24 Nov 2007 13:38:40 +0100, skrev Kamen TOMOV <·····@cybuild.com>:
>
> >> What if one wants:
>
> >> (let ((a 1))
> >>   (case 1
> >>     (a t)))
>
> >> Although, this is not a real problem as I don't see any practical
> >> reasons to use "case" and not "cond" in this case, I'm still
> >> interested in the approach of solving it.
>
> > The defmethod approach perhaps?
>
> > (defmethod ((a (eql #.+val+))
> >    ...)
> > ...
>
> Hmm, what do you mean? It doesn't make sense to me. How would you code
> the following form using defmethod?
>
> (let ((a 1)
>       (b 2)
>       (c 3))
>   (case 1
>     (a "bar")
>     (b "baz")
>     (c "bav")))
>
> --
> Kamen

Use the other suggestion in the first two replies to this thread.
Write your own eval-case macro.
From: John Thingstad
Subject: Re: How to use constants in a case key form ?
Date: 
Message-ID: <op.t2avivo9ut4oq5@pandora.alfanett.no>
P� Sat, 24 Nov 2007 14:47:32 +0100, skrev Kamen TOMOV <·····@cybuild.com>:

>
> Hmm, what do you mean? It doesn't make sense to me. How would you code
> the following form using defmethod?
>
> (let ((a 1)
>       (b 2)
>       (c 3))
>   (case 1
>     (a "bar")
>     (b "baz")
>     (c "bav")))

I wouldn't. What have just written dosn't make any sense. I would just  
fall through the case and return NIL.
macroexpanding case shows you what is happening

(LET ((#:G2581 1))
   (COND ((EQL 'A #:G2581) "bar")
         ((EQL 'B #:G2581) "baz")
         ((EQL 'C #:G2581) "bav")))

Clearly a's value isn't known at compile time. the #. means readtime  
evaluation it is evaluated as the tokens are being read and thus can grab  
the expression before the compiler sees it.

hence

(eval-when (:compile-toplevel :load-toplevel :execute)
   (defconstant a 1)
   (defconstant b 2)
   (defconstant c 3))

(print (case 1
         (#.a "bar")
         (#.b "baz")
         (#.c "bav"))

whould print "bar"
I assume that is what you thought you wrote.

Now this I can translate to defmethod form.

(defmethod my-print ((val (eql #.a))) (print "bar"))
(defmethod my-print ((val (eql #.b))) (print "baz"))
(defmethod my-print ((val (eql #.c))) (print "bav"))

(my-print a) --> "bar"

Not very practical in this case, but if you imagine you have 100 such  
constants and a significant amout of work for each one it starts to make  
sense to decompose it like this.

--------------
John Thingstad
From: Alan Crowe
Subject: Re: How to use constants in a case key form ?
Date: 
Message-ID: <86zlx4r7mi.fsf@cawtech.freeserve.co.uk>
Frank Goenninger DG1SBG <·············@nomail.org> writes:

> I have something similar to
> 
> (defconstant +FIRST+  1)
> (defconstant +SECOND+ 2)
> (defconstant +THIRD+  3)
> 
> (defun test-case (value)
>   (case value
>     (( +FIRST+ +SECOND+ )
>      "1 or 2")
>     ((+THIRD+)
>      "3")
>     (otherwise "Something")))
> 
> The (case ....) macroexpands to
> 
> (LET ()
>   (COND ((OR (EQL '+FIRST+ VALUE) (EQL '+SECOND+ VALUE)) "1 or 2")
>         ((OR (EQL '+THIRD+ VALUE)) "3")
>         (T "Something")))
> 
> and we have
> 
> CL-USER> (eql +FIRST+ 1)
> T
> 
> CL-USER> (eql '+FIRST+ 1)
> NIL
> 
> So far so bad. I have a real bunch of defconstants (about 200)
> so... How do I get to use the constants in my case key forms?
> 
> Thx for any hints...

I'm reading your post, thinking the usual thoughts, either
get the reader to do it with #.+first+, or write an
eval-case macro that evaluates its keys, when my train of
thought gets derailed by "I have a real bunch of
defconstants (about 200)"

That sounds like a train wreck waiting to happen. What I'm
imagining is that number twenty three is (defconstant +foo+ 741) and
in a different file number one hundred and ninety three is
(defconstant +bar+ 741). You cheerfully write

(eval-case value
  (...)
  (+foo+ ...)
  (...)
  (+bar+ ...)
  (...))

and tear out a lot of hair before you find the bug due to
the shadowing of +bar+ by +foo+. (Perhaps the compiler saves
you by telling you it is deleting unreachable code?)

I'm also imagining debugging being painful because you look
in the variable code-number at see 42, an actual code
number, forcing you to go hunting for the line

  (defconstant +hymenoptera+ 42)

before you can understand the bug.

So, I think you want to code up some mechanical assistance,
both with checking for clashes and decoding the code
numbers.

    (defun make-decode-table (list-of-symbols)
      (let* ((unused 0)
             (largest-code (reduce #'max 
                                   list-of-symbols
                                   :key #'symbol-value))
             (table (make-array (+ largest-code 1) 
                                :initial-element unused)))
        (dolist (symbol list-of-symbols table)
          (let ((entry (aref table
                             (symbol-value symbol))))
            (unless (eql entry unused)
              (error "Code ~D for ~S already codes for ~S."
                     (symbol-value symbol)
                     symbol
                     entry))
            (setf (aref table (symbol-value symbol))
                  symbol)))))

    (let ((table (make-decode-table '(+first+ +second+ +third+))))
      (defun decode (code)
        (declare (integer code))
        (let ((symbol (aref table code)))
          (typecase symbol
            (symbol symbol)
            (t (error "Code number ~D is unassigned." code))))))
                

Now your problem evaporates

    (ecase (decode value)
      ((+first+ +second+)
       "1 or 2")
      (+third+
       "3"))

and you can usefully push the decode step closer to the
source of the numbers and operate on symbols.

Alan Crowe
Edinburgh
Scotland
From: Kaz Kylheku
Subject: Re: How to use constants in a case key form ?
Date: 
Message-ID: <623cd239-c9a2-414a-9313-8cc44174d218@b40g2000prf.googlegroups.com>
On Nov 23, 1:09 pm, Alan Crowe <····@cawtech.freeserve.co.uk> wrote:
> That sounds like a train wreck waiting to happen. What I'm
> imagining is that number twenty three is (defconstant +foo+ 741) and
> in a different file number one hundred and ninety three is
> (defconstant +bar+ 741). You cheerfully write
>
> (eval-case value
>   (...)
>   (+foo+ ...)
>   (...)
>   (+bar+ ...)
>   (...))
>
> and tear out a lot of hair before you find the bug due to
> the shadowing of +bar+ by +foo+.

This should be diagnosed by your EVAL-CASE, if not rejected outright.
All case labels should be unique, just like in the C switch statement
(GCC diagnoses this).

Also, maybe it would be helpful to have a macro which automatically
generates enumerated constants, starting from a given value.
From: Frank Goenninger DG1SBG
Subject: Re: How to use constants in a case key form ?
Date: 
Message-ID: <lzve7ssjj6.fsf@pcsde001.de.goenninger.net>
Alan Crowe <····@cawtech.freeserve.co.uk> writes:

> I'm reading your post, thinking the usual thoughts, either
> get the reader to do it with #.+first+, or write an
> eval-case macro that evaluates its keys, when my train of
> thought gets derailed by "I have a real bunch of
> defconstants (about 200)"
>
> That sounds like a train wreck waiting to happen. What I'm
> imagining is that number twenty three is (defconstant +foo+ 741) and
> in a different file number one hundred and ninety three is
> (defconstant +bar+ 741). You cheerfully write
>
> (eval-case value
>   (...)
>   (+foo+ ...)
>   (...)
>   (+bar+ ...)
>   (...))
>
> and tear out a lot of hair before you find the bug due to
> the shadowing of +bar+ by +foo+. (Perhaps the compiler saves
> you by telling you it is deleting unreachable code?)
>
> I'm also imagining debugging being painful because you look
> in the variable code-number at see 42, an actual code
> number, forcing you to go hunting for the line
>
>   (defconstant +hymenoptera+ 42)
>
> before you can understand the bug.
>
> So, I think you want to code up some mechanical assistance,
> both with checking for clashes and decoding the code
> numbers.
>
>     (defun make-decode-table (list-of-symbols)
>       (let* ((unused 0)
>              (largest-code (reduce #'max 
>                                    list-of-symbols
>                                    :key #'symbol-value))
>              (table (make-array (+ largest-code 1) 
>                                 :initial-element unused)))
>         (dolist (symbol list-of-symbols table)
>           (let ((entry (aref table
>                              (symbol-value symbol))))
>             (unless (eql entry unused)
>               (error "Code ~D for ~S already codes for ~S."
>                      (symbol-value symbol)
>                      symbol
>                      entry))
>             (setf (aref table (symbol-value symbol))
>                   symbol)))))
>
>     (let ((table (make-decode-table '(+first+ +second+ +third+))))
>       (defun decode (code)
>         (declare (integer code))
>         (let ((symbol (aref table code)))
>           (typecase symbol
>             (symbol symbol)
>             (t (error "Code number ~D is unassigned." code))))))
>                 
>
> Now your problem evaporates
>
>     (ecase (decode value)
>       ((+first+ +second+)
>        "1 or 2")
>       (+third+
>        "3"))
>
> and you can usefully push the decode step closer to the
> source of the numbers and operate on symbols.
>
> Alan Crowe
> Edinburgh
> Scotland

Alan - wow ! Thanks!!

Of course you're right - your code above saves me from some 
serious headaches, for sure! Now that PoC (piece of code) is saved in
my ever-growing code "knowledge base"...

c.l.l - you never know when you learn the next bit you've never
thought of - despite years of programming. 

Thx again!

Frank 

-- 

  Frank Goenninger

  frgo(at)mac(dot)com

  "Don't ask me! I haven't been reading comp.lang.lisp long enough to 
  really know ..."
From: John Thingstad
Subject: Re: How to use constants in a case key form ?
Date: 
Message-ID: <op.t19v7kwqut4oq5@pandora.alfanett.no>
P� Fri, 23 Nov 2007 22:09:57 +0100, skrev Alan Crowe  
<····@cawtech.freeserve.co.uk>:

>
> I'm reading your post, thinking the usual thoughts, either
> get the reader to do it with #.+first+, or write an
> eval-case macro that evaluates its keys, when my train of
> thought gets derailed by "I have a real bunch of
> defconstants (about 200)"
>
> That sounds like a train wreck waiting to happen. What I'm
> imagining is that number twenty three is (defconstant +foo+ 741) and
> in a different file number one hundred and ninety three is
> (defconstant +bar+ 741). You cheerfully write
>
> (eval-case value
>   (...)
>   (+foo+ ...)
>   (...)
>   (+bar+ ...)
>   (...))
>
> and tear out a lot of hair before you find the bug due to
> the shadowing of +bar+ by +foo+. (Perhaps the compiler saves
> you by telling you it is deleting unreachable code?)
>
> I'm also imagining debugging being painful because you look
> in the variable code-number at see 42, an actual code
> number, forcing you to go hunting for the line
>
>   (defconstant +hymenoptera+ 42)
>

I would prefer using a plist for this

(eval-when (:compile-toplevel :load-toplevel :execute)
   (defparameter *enum* (list :first 1 :second 2 :third 3))

   ;; could type #.(getf *enum* key) each time, but it's a bit tedious
   ;; lets write $symbol instead
   (let (*old-readtable*)

     (defun enable-shorthand ()
       (setf *old-readtable* (copy-readtable))
       (set-macro-character #\$ #'(lambda (stream char)
                                    (declare (ignore char))
                                    (getf *enum* (read stream)))))

     (defun disable-shorthand ()
       (setf *readtable* *old-readtable*))))

(enable-shorthand)

(defun test (value)
   (ecase value
     ($:first "first")
     ($:second "second")
     ($:third "third")))

(disable-shorthand)

It is the trivial to check for duplicates and lookup key

(defun duplicate-value-check ()
   (let* ((values (loop for v in (cdr *enum*) by #'cddr collect v))
          (sorted-values (sort values #'<))
          (position (mismatch sorted-values
                              (remove-duplicates sorted-values))))
     (when position
       (let ((index (nth position sorted-values)))
         (error "key ~A has a value ~D duplicated"
                (nth (* index 2) *enum*) (nth index sorted-values))))))

(duplicate-value-check)

(defun get-key (value)
   (nth (1- (position value *enum*)) *enum*))

--------------
John Thingstad
From: John Thingstad
Subject: Re: How to use constants in a case key form ?
Date: 
Message-ID: <op.t2aos9fmut4oq5@pandora.alfanett.no>
P� Sat, 24 Nov 2007 02:39:10 +0100, skrev John Thingstad  
<·······@online.no>:

Got a bit late last night. I made a mistake in duplicate-value-check
This should work.

(defun duplicate-value-check (enum)
   (let* ((values
            (iter
             (for element in enum)
             (when (numberp element)
               (collect element)))
          (sorted-values (sort values #'<))
          (position (mismatch sorted-values
                              (remove-duplicates sorted-values))))
     (when position
       (let ((value (nth position sorted-values)))
         (error "key ~A has a value ~D duplicated"
                (get-key enum value) value)))))

(duplicate-value-check *enum*)


If you think this is too slow

(defun get-key (plist value)
   (nth (1- (position value *enum*)) plist))

perhaps you prefer this (using iterate)

(defun get-key (plist value)
   (iter (generating element in plist)
     (let ((current-key (next element))
           (current-value (next element)))
       (when (= current-value value)
         (return current-key)))))

although in this case I don't think it matters.
(But getting key given the value from a plist is a generic function so..)


--------------
John Thingstad