Hello group,
I've the following question: I want to define macro case-string, which
behaves exactly like case, only using string-equal instead of eql.
I've looked up definition of case in SBCL[*], and realized that my
task is as simple as
(defmacro case-string (keyform &body cases)
(case-body 'case keyform cases t 'string-equal nil nil nil))
My problem is with case-body - it's not imported by default, and I
don't know how to import it (I guess that my problem is with
understanding how packages work, strictly speaking). I also don't know
how to make my definition of case-string portable - I guess not all CL
implementations may use case-body internally.
And I don't want simply to copy-paste case-body definition in my source
files - it's seems to be not elegant, I would need to copy definition
of list-of-length-at-least-p as well, etc.
Is there any elegant solution?
Thanks,
Victor.
[*]: http://www.google.com/codesearch?q=+%22defmacro-mundanely+case%22+show:ZDN4RI9bILk:3jwtAwW1w8E:WJNTlSHN6zc&sa=N&cd=1&ct=rc&cs_p=http://distfiles.master.finkmirrors.net/sbcl-0.9.3-source.tar.bz2&cs_f=sbcl-0.9.3/src/code/macros.lisp#a0
Victor Kryukov wrote:
> I've looked up definition of case in SBCL[*], and realized that my
> task is as simple as
>
> (defmacro case-string (keyform &body cases)
> (case-body 'case keyform cases t 'string-equal nil nil nil))
^^^^
Should read
(case-body 'case-string keyform cases t 'string-equal nil nil nil))
Victor Kryukov <···················@gmail.com> writes:
> Hello group,
>
> I've the following question: I want to define macro case-string, which
> behaves exactly like case, only using string-equal instead of eql.
>
> I've looked up definition of case in SBCL[*], and realized that my
> task is as simple as
>
> (defmacro case-string (keyform &body cases)
> (case-body 'case keyform cases t 'string-equal nil nil nil))
>
> My problem is with case-body - it's not imported by default, and I
> don't know how to import it (I guess that my problem is with
> understanding how packages work, strictly speaking). I also don't know
> how to make my definition of case-string portable - I guess not all CL
> implementations may use case-body internally.
>
> And I don't want simply to copy-paste case-body definition in my source
> files - it's seems to be not elegant, I would need to copy definition
> of list-of-length-at-least-p as well, etc.
>
> Is there any elegant solution?
Il faut ce qu'il faut.
If you want your program to be portable, it must be stand alone (it
shouldn't depend on implementation specific stuff). If you've found
in SBCL an implementation of case-string that works, then
copy-and-paste it in your program.
Or, make it a library asdf-installable on which you'll depend (and
other programs will too), and perhaps it'll become defacto standard
like slit-sequence.
--
__Pascal Bourguignon__ http://www.informatimago.com/
"Indentation! -- I will show you how to indent when I indent your skull!"
From: Ken Tilton
Subject: Re: Using "internal" macros of a CL implementation
Date:
Message-ID: <_k21h.4$Zq6.3@newsfe12.lga>
Victor Kryukov wrote:
> Hello group,
>
> I've the following question: I want to define macro case-string, which
> behaves exactly like case, only using string-equal instead of eql.
>
> I've looked up definition of case in SBCL[*], and realized that my
> task is as simple as
>
> (defmacro case-string (keyform &body cases)
> (case-body 'case keyform cases t 'string-equal nil nil nil))
>
> My problem is with case-body - it's not imported by default, and I
> don't know how to import it (I guess that my problem is with
> understanding how packages work, strictly speaking). I also don't know
> how to make my definition of case-string portable - I guess not all CL
> implementations may use case-body internally.
>
> And I don't want simply to copy-paste case-body definition in my source
> files - it's seems to be not elegant, I would need to copy definition
> of list-of-length-at-least-p as well, etc.
>
> Is there any elegant solution?
COND.
kt
--
Cells: http://common-lisp.net/project/cells/
"I'll say I'm losing my grip, and it feels terrific."
-- Smiling husband to scowling wife, New Yorker cartoon
Ken Tilton <·········@gmail.com> writes:
> Victor Kryukov wrote:
>> Hello group,
>>
>> I've the following question: I want to define macro case-string, which
>> behaves exactly like case, only using string-equal instead of eql.
>>
>> I've looked up definition of case in SBCL[*], and realized that my
>> task is as simple as
>>
>> (defmacro case-string (keyform &body cases)
>> (case-body 'case keyform cases t 'string-equal nil nil nil))
>>
> COND.
Let me be more precise here. I do understand how to use COND to
achieve what I need. I just want more useful macro, so that
(let ((keyform "test"))
(case-string keyform
("test1" 'test1)
(("test2" "test3") 'test2-or-3)
(("test" "test4") 'test-or-4)))
would evaluate to TEST-OR-4. Of course, you can do that simply by
using COND, as Ken suggested:
(cond
((string= keyform "test1") 'test1)
((member keyform '("test2" "test3") :test #'string=) 'test2-or-3)
((member keyform '("test" "test4") :test #'string=) 'test-or-4))
and is seems not that hard to write case-string macro which translates
first snippet to the second. I've just noticed that similar
functionality already existed in SBCL core - e.g. case is really
similar, it's only uses 'eql instead of string=, and is defined with
help of internal macro case-body; my question was how one can possibly
access such macros (even in non-portable way) to avoid duplicating
existing functionality.
Victor.
From: Pascal Costanza
Subject: Re: Using "internal" macros of a CL implementation
Date:
Message-ID: <4qku5vFnkj97U1@individual.net>
Victor Kryukov wrote:
> Ken Tilton <·········@gmail.com> writes:
>
>> Victor Kryukov wrote:
>>> Hello group,
>>>
>>> I've the following question: I want to define macro case-string, which
>>> behaves exactly like case, only using string-equal instead of eql.
>>>
>>> I've looked up definition of case in SBCL[*], and realized that my
>>> task is as simple as
>>>
>>> (defmacro case-string (keyform &body cases)
>>> (case-body 'case keyform cases t 'string-equal nil nil nil))
>>>
>> COND.
>
> Let me be more precise here. I do understand how to use COND to
> achieve what I need. I just want more useful macro, so that
>
> (let ((keyform "test"))
> (case-string keyform
> ("test1" 'test1)
> (("test2" "test3") 'test2-or-3)
> (("test" "test4") 'test-or-4)))
>
> would evaluate to TEST-OR-4. Of course, you can do that simply by
> using COND, as Ken suggested:
>
> (cond
> ((string= keyform "test1") 'test1)
> ((member keyform '("test2" "test3") :test #'string=) 'test2-or-3)
> ((member keyform '("test" "test4") :test #'string=) 'test-or-4))
>
> and is seems not that hard to write case-string macro which translates
> first snippet to the second. I've just noticed that similar
> functionality already existed in SBCL core - e.g. case is really
> similar, it's only uses 'eql instead of string=, and is defined with
> help of internal macro case-body; my question was how one can possibly
> access such macros (even in non-portable way) to avoid duplicating
> existing functionality.
In a non-portable way: Check in what packages they are defined, and
access the right symbols in those packages. 'apropos helps to determine
the package. Access to a symbol in a package is made by package:symbol,
or package::symbol if it is not exported (but then it's also really not
advisable to use it). There are more ways to access symbols in other
packages via defpackage.
In a portable way: Create your own package with a low-level API. Make
sure that the API is close to at least one or ore existing low-level
APIs. Then you can provide different implementations depending on each
implementation, and in some implementations simply make the existing one
available through your own package. There exist numerous compatibility
layers that do it like this, like ACL-Compat, LW-Compat,
trivial-garbage, Closer to MOP, and various others. (For example, CLiki
has a page for such compatibility layers.)
A necessary prerequisite is to learn and understand packages beforehand,
either by studying a good tutorial or the HyperSpec/CLtL2 directly. It's
also a very good idea to understand something about system construction,
especially system definition utilities like asdf, mk-defsystem, and the
likes.
I hope this helps.
Pascal
--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
> In a non-portable way: Check in what packages they are defined, and
> access the right symbols in those packages. 'apropos helps to
> determine the package. Access to a symbol in a package is made by
> package:symbol, or package::symbol if it is not exported (but then
> it's also really not advisable to use it). There are more ways to
> access symbols in other packages via defpackage.
>
> In a portable way: Create your own package with a low-level API. Make
> sure that the API is close to at least one or ore existing low-level
> APIs. Then you can provide different implementations depending on each
> implementation, and in some implementations simply make the existing
> one available through your own package. There exist numerous
> compatibility layers that do it like this, like ACL-Compat, LW-Compat,
> trivial-garbage, Closer to MOP, and various others. (For example,
> CLiki has a page for such compatibility layers.)
Thanks, Pascal; that's exactly what I was asking for, though I
probably wasn't very clear.
I am impressed to see a large number of very skilled persons (Bill,
Ken, Pascal & Pascal - to name just a few) in this group who spend
*significant* amount of their times explaining in great detail
different aspect of the language, often answering similar questions again
and again. I still fail to see why this group is sometimes considered to be
"hostile" to the newcomers.
Best regards,
Victor.
From: Ken Tilton
Subject: Re: Using "internal" macros of a CL implementation
Date:
Message-ID: <v3a2h.101$Rj2.98@newsfe10.lga>
Victor Kryukov wrote:
> I still fail to see why this group is sometimes considered to be
> "hostile" to the newcomers.
Nonsense. We just ate. Had a couple of nice juicy noobs in here just
before you arrived, the big ones can take a couple of weeks to digest.
kt
--
Cells: http://common-lisp.net/project/cells/
"I'll say I'm losing my grip, and it feels terrific."
-- Smiling husband to scowling wife, New Yorker cartoon
Ken Tilton <·········@gmail.com> writes:
> Victor Kryukov wrote:
>> I still fail to see why this group is sometimes considered to be
>> "hostile" to the newcomers.
>
> Nonsense. We just ate. Had a couple of nice juicy noobs in here just
> before you arrived, the big ones can take a couple of weeks to digest.
And of course, they never return... Those rare who survive become
noob-eaters - right after they learn how to write backquote
implementation themselves. Now I see why they say that "CL is a hard
language to learn" :)
Pascal Costanza <··@p-cos.net> wrote:
+---------------
| Victor Kryukov wrote:
| > my question was how one can possibly access such macros
| > (even in non-portable way) to avoid duplicating existing functionality.
...
| In a portable way: Create your own package with a low-level API. Make
| sure that the API is close to at least one or ore existing low-level
| APIs. Then you can provide different implementations depending on each
| implementation, and in some implementations simply make the existing one
| available through your own package. There exist numerous compatibility
| layers that do it like this, like ACL-Compat, LW-Compat,
| trivial-garbage, Closer to MOP, and various others. (For example, CLiki
| has a page for such compatibility layers.)
+---------------
Victor, here's a classic example of what Pascal's talking about:
;;; GETENV -- Mostly-portable code for accessing Unix
;;; (or Windows?) environment variables. Morphed very slightly
;;; from <URL:http://cl-cookbook.sourceforge.net/os.html>
;;; Copyright (c) 2002 The Common Lisp Cookbook Project
;;; See: <URL:http://cl-cookbook.sourceforge.net/license.html>
(defun getenv (name &optional default)
(or
#+CMU (cdr (assoc name ext:*environment-list* :test #'string=))
#+Allegro (sys:getenv name)
#+CLISP (ext:getenv name)
#+ECL (si:getenv name)
#+SBCL (sb-unix::posix-getenv name)
#+LISPWORKS (lispworks:environment-variable name)
default))
-Rob
-----
Rob Warnock <····@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607
From: ···············@space.at
Subject: Re: Using "internal" macros of a CL implementation
Date:
Message-ID: <u1woch20q.fsf@space.at>
>>>>> "Rob" == Rob Warnock <····@rpw3.org> writes:
Slightly off-topic,
Rob> [...] here's a classic example of [a portable
Rob> implementation using implementation-specific internal
Rob> functionality]
Rob> ;;; GETENV -- Mostly-portable code for accessing Unix
Rob> ;;; (or Windows?) environment variables. Morphed very slightly
^^^^^^^^
Rob> ;;; from <URL:http://cl-cookbook.sourceforge.net/os.html>
Rob> ;;; Copyright (c) 2002 The Common Lisp Cookbook Project
Rob> ;;; See: <URL:http://cl-cookbook.sourceforge.net/license.html>
Rob> (defun getenv (name &optional default)
Rob> (or
Rob> #+CMU (cdr (assoc name ext:*environment-list* :test #'string=))
Rob> #+Allegro (sys:getenv name)
Rob> #+CLISP (ext:getenv name)
Rob> #+ECL (si:getenv name)
Rob> #+SBCL (sb-unix::posix-getenv name)
Rob> #+LISPWORKS (lispworks:environment-variable name)
Rob> default))
This is SBCL 0.9.17, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.
[..removed some chatter]
* (sb-unix::posix-getenv "HOME")
"h:/"
So it does work for SBCL on Windows XP. The clisp I have here is
ancient (1999) and getenv was not exported then, but it does work
there too:
[1]> (lisp-implementation-version)
"1999-07-22 (July 1999)"
[2]> (system::getenv "HOME")
"h:/"
Hope this helps.
regards
Roland
Victor Kryukov wrote:
> Hello group,
>
> I've the following question: I want to define macro case-string, which
> behaves exactly like case, only using string-equal instead of eql.
>
> I've looked up definition of case in SBCL[*], and realized that my
> task is as simple as
>
> (defmacro case-string (keyform &body cases)
> (case-body 'case keyform cases t 'string-equal nil nil nil))
>
> My problem is with case-body - it's not imported by default, and I
> don't know how to import it (I guess that my problem is with
> understanding how packages work, strictly speaking).
So specify (whatever-cl-package::case-body....)
> I also don't know
> how to make my definition of case-string portable - I guess not all CL
> implementations may use case-body internally.
If you want portable, expand each case into a cond clause.
kt
--
Cells: http://common-lisp.net/project/cells/
"I'll say I'm losing my grip, and it feels terrific."
-- Smiling husband to scowling wife, New Yorker cartoon
Victor Kryukov <···················@gmail.com> writes:
> Hello group,
>
> I've the following question: I want to define macro case-string, which
> behaves exactly like case, only using string-equal instead of eql.
Here is what I use for this sort of thing. It could certainly be
cleaned up, but it's a start:
(defun build-case (test-fn var clauses &key errorp)
(let ((x (gensym)))
`(let ((,x ,var))
(cond ,@(loop for (vals . code) in clauses
if (consp vals)
collect (append `((or
,@(loop for val in vals
collect `(,test-fn ,x ,val))))
code)
else
collect (append (when (and (eq vals t)
(not errorp))
`((,test-fn ,x ,vals)))
code))
,@(if errorp
`((t (error "~S fell through CASE; expected one of ~S"
,var
',(loop for (clause . rest) in clauses
if (consp clause)
append clause
else collect clause)))))))))
(defmacro case-string-equal (var &body clauses)
(build-case 'string-equal var clauses))
(defmacro ecase-string-equal (var &body clauses)
(build-case 'string-equal var clauses :errorp t))
(defmacro case-string (var &body clauses)
(build-case 'string= var clauses))
(defmacro ecase-string (var &body clauses)
(build-case 'string= var clauses :errorp t))
CL-USER 5 > (macroexpand '(ecase-string-equal "foob"
("foo" 3)
(t 4)))
(LET ((#:X16625 "foob")) (COND ((STRING-EQUAL #:X16625 "foo") 3) ((STRING-EQUAL #:X16625 T) 4) (T (ERROR "~S fell through CASE; expected one of ~S" "foob" (QUOTE ("foo" T))))))
T
Cleaned up a little:
(defun mklist (o)
(if (consp o) o (list o)))
#-lispworks
(defmacro when-let ((var clause) &body body)
`(let ((,var ,clause))
(when ,var
,@body)))
#+lispworks
(import 'lispworks:when-let)
(defmacro generic-ecase (keyform test-fn &body clauses)
(when (assoc t clauses)
(error "GENERIC-ECASE already has a T clause"))
`(generic-case ,keyform ,test-fn
,@clauses
(t (error "~S fell through ECASE; expected one of ~S"
,keyform ',(loop for clause in clauses
if (consp (car clause))
appending (car clause)
else
appending (list (car clause)))))))
(defmacro generic-case (keyform test-fn &body clauses)
(let ((x (gensym)))
`(let ((,x ,keyform))
(cond ,@(loop for (vals . code) in clauses
unless (eq vals t)
collect `((or ,@(loop for val in (mklist vals)
collect `(,test-fn ,x ',val)))
,@code))
,@(when-let (t-clause (assoc t clauses))
(list t-clause))))))
(defmacro case-string-equal (keyform &body clauses)
`(generic-case ,keyform string-equal ,@clauses))
(defmacro ecase-string-equal (keyform &body clauses)
`(generic-ecase ,keyform string-equal ,@clauses))
(defmacro case-string (keyform &body clauses)
`(generic-case ,keyform string= ,@clauses))
(defmacro ecase-string (keyform &body clauses)
`(generic-ecase ,keyform string= ,@clauses))
---
Now you can use GENERIC-CASE as a macro on its own (the old version
used a function BUILD-CASE, so that a new macro had to be defined every
time you wanted a new kind of case). So you can do:
(generic-case #\a char-equal
(#\A 'foo))
(generic-ecase #\a char-equal
(#\A 'foo))
or something similar.