[comp.lang.lisp] string to symbol conversion

moore%defmacro.utah.edu@cs.utah.edu (Tim Moore) (06/21/91)

In article <80761@eerie.acsu.Buffalo.EDU> lewocz@pegasus.cs.Buffalo.EDU (John S. Lewocz) writes:
>Is there any way to convert an arbitrary string like "bob" into the 
>symbol bob in Common Lisp?

(intern "bob") =>
|bob|
NIL

(intern (string-upcase "bob")) =>
BOB
NIL

-- 
Tim Moore                    moore@cs.utah.edu {bellcore,hplabs}!utah-cs!moore
"Ah, youth. Ah, statute of limitations."
		-John Waters

lewocz@pegasus.cs.Buffalo.EDU (John S. Lewocz) (06/21/91)

Is there any way to convert an arbitrary string like "bob" into the 
symbol bob in Common Lisp?

Thanks.

--John

dfs@doe.carleton.ca (David F. Skoll) (06/21/91)

In <80761@eerie.acsu.Buffalo.EDU> lewocz@pegasus.cs.Buffalo.EDU
(John S. Lewocz) writes:
>Is there any way to convert an arbitrary string like "bob" into the 
>symbol bob in Common Lisp?

If you don't have any weird characters in the string,

	(setq foo "bob")
	(read-from-string foo)  ;; ---> The symbol BOB

should work.  If you have some weird characters, but no "bars" (|) in the
string, then

	(setq foo "A #Bunch^&OfWeird**Cha#r$")
	(read-from-string
	   (concatenate 'string "|" foo "|"))

should do it.  If your string contains "|", then it's tougher, and
I don't feel like figuring it out right now. :-)

(Of course, if you place bars around the string, you may not get the
symbol in upper-case or whatever your Lisp's default is.)

--
David F. Skoll

hall@aplcen.apl.jhu.edu (Marty Hall) (06/21/91)

In article <80761@eerie.acsu.Buffalo.EDU> lewocz@pegasus.cs.Buffalo.EDU 
(John S. Lewocz) writes:

>Is there any way to convert an arbitrary string like "bob" into the 
>symbol bob in Common Lisp?

(read-from-string "bob") --> BOB

					- Marty Hall
------------------------------------------------------
hall@aplcen.apl.jhu.edu, hall%aplcen@jhunix.bitnet, ..uunet!aplcen!hall
Artificial Intelligence Lab, AAI Corp, PO Box 126, Hunt Valley, MD 21030

(setf (need-p 'disclaimer) NIL)

kevin@dime.cs.umass.edu (kevin gallagher) (06/22/91)

An excellent programming principle is to use the least powerful
method for acheiving your objective.  This is especially important in
lisp because there are always several ways to do something, each with
widely different costs.  Using a big stick when a small one will do will
make your program run slower, probably lead to unexpected bugs, and
contribute to the misperception that `lisp is slow.'

In the case of converting a string to a symbol use INTERN.  INTERN looks
for an existing symbol with the specified name and creates one if no
such symbol exists.  It returns the symbol that was found or created.
(If you expect the symbol to already exist you should use FIND-SYMBOL
which returns nil no symbol with the specified name is found.)

As was pointed out in other messages, intern doesn't do case conversion
or anything else.

    (intern "Foo") => |Foo|
    (intern "FOO") => FOO

The function READ-FROM-STRING is too big a stick.  It invokes the reader
which is a large program by itself.  (On my Explorer (intern string) is
6-7 times faster than (read-from-string string) and it doesn't cons at
all.)  Perhaps even worse, INTERN will *always* return a string.
READ-FROM-STRING can return (and in fact *do*) anything.

    (intern "(cons 1 2)") => |(cons 1 2)|

    (read-from-string "(cons 1 2)" => (CONS 1 2)

    (catch 'exit (read-from-string "#.(throw 'exit 'dumb)")) => DUMB

EVAL, READ, and READ-xxx are the functions that are most overused when a
smaller stick will do.  Also, FORMAT and the other printing functions
can often be replaced with less powerful (and less costly) functions.

Kevin Gallagher

kevin@dime.cs.umass.edu (kevin gallagher) (06/22/91)

My previous note may have left an erroneous perception, namely, that
there is something wrong with READ-FROM-STRING.  The potential bug I
alluded to is due to the expectation (in the context of the original
question) that the particular use of READ-FROM-STRING will return a
symbol.  In fact, READ-FROM-STRING can return any datatype and your
program will break if you expect a symbol.  (Additionally, because of
read macros, READ-FROM-STRING can *do* anything.  It might not even
return at all.)

If you use INTERN you will be assured of getting back a symbol.

mitchell@tartarus.uchicago.edu (Mitchell Marks) (06/25/91)

Here is a set of general purpose functions and variations on this
theme.  The handling of packages involves some iffy choices, but I
think the rest is fairly solid.

---------------- clip here --------------------

;;; SYMCAT takes one or more constituents (each of which must be either a
;;; symbol, a string, or a nonnegative fixnum) and returns a symbol whose
;;; name is made by concatenating the printnames (for symbols) or contents
;;; (for strings) of the arguments, in the order given, with no separator.
;;; The new symbol is interned in the home package of the first
;;; constituent, if that constituent is a symbol, or else in the current
;;; package (value of *PACKAGE*) if the first constituent is a string.

(defun symcat (&rest syms)
  (insist symcat (not (null syms)))
  (symcat-symlist syms))

;;; SYMCAT-SEPARATE takes a separator string (or symbol) and one or more
;;; constituents (symbols, strings, or natural numbers).  It returns a
;;; symbol whose name consists of the printnames (of the symbols) or
;;; contents (of the strings) of the constituent arguments, with the
;;; separator inserted in between each succcessive symbol or string.  The
;;; separator is not added before the first nor after the last of the
;;; constituent arguments; and if there is only one constituent, the
;;; separator is not used at all.  The new symbol is interned in the home
;;; package of the first constituent, if that constituent is a symbol, or
;;; else in the current package (value of *PACKAGE*) if the first
;;; constituent is a string.

;;; SYMCAT-SEPARATE-AUX does the work of SYMCAT-SEPARATE, but takes the
;;; constituents in a list instead of an &rest argument; this makes it
;;; suitable for use by the predefined-separator functions.  The test for
;;; null constituent list cannot be left to the lower-level functions,
;;; since at least one implementation (ACL 3.2) tends to come up with the
;;; symbol NIL.

(defun symcat-separate (separator &rest constituents)
  (symcat-separate-aux separator constituents))

(defun symcat-separate-aux (separator constituents)
  (insist symcat-separate-aux 
          (not (null constituents))
          (or (symbolp separator) (stringp separator) (natnump separator)))
  (symcat-symlist 
   (cons (car constituents)
         (symcat-interleave separator (cdr constituents)))))

(defun symcat-interleave (separator constituents)
  (if (null constituents)
      nil
    (cons separator 
          (cons (car constituents)
                (symcat-interleave separator (cdr constituents))))))

;;; The functions SYMCAT-HYPHEN, SYMCAT-STAR, SYMCAT-EQUAL, SYMCAT-PLUS,
;;; SYMCAT-DOT, and SYMCAT-SPACE provide the functionality of
;;; SYMCAT-SEPARATE with predefined separators.  Each takes one or more
;;; arguments, which must each be a symbol or string.  

(defun symcat-hyphen (&rest constituents)
  (symcat-separate-aux "-" constituents))

(defun symcat-star (&rest constituents)
  (symcat-separate-aux "*" constituents))

(defun symcat-equal (&rest constituents)
  (symcat-separate-aux "=" constituents))

(defun symcat-plus (&rest constituents)
  (symcat-separate-aux "+" constituents))

(defun symcat-dot (&rest constituents)
  (symcat-separate-aux "." constituents))

(defun symcat-space (&rest constituents)
  (symcat-separate-aux " " constituents))



;;; SYMCAT-INTERN is the low-level function ultimately called by all the
;;; higher-level functions defined here; it's the place where INTERN gets
;;; used. The argument SYMLIST must be a non-null list of symbols or
;;; strings, and the argument PKG must be a package.

(defun symcat-intern (symlist pkg)
  (insist symcat-intern 
          (not (null symlist)) 
          (packagep pkg))
  (intern (apply #'concatenate 'string
                 (for (item :in symlist)
                      :save (stringify item)))
          pkg))

(defun stringify (item)
  (cond ((stringp item) item)
        ((symbolp item) (string item))
        ((natnump item) (format nil "~d" item))
        (t (error  "~s is not a string, symbol, or natural number" item))))

(defun natnump (item)
  (and (integerp item) (not (minusp item))))

;;; SYMCAT-SYMLIST feeds SYMCAT-INTERN.  The argument SYMLIST must be a
;;; nonnull list of symbols, strings, or naturalnumbers.  The function
;;; determines the package into which the new symbol should be interned,
;;; based on the first member of SYMLIST: if this member is a symbol, its
;;; home package is used, otherwise the current package (value of
;;; *PACKAGE*) is used.

(defun symcat-symlist (symlist)
  (insist symcat-symlist (not (null symlist)))
  (for (item :in symlist)
       :do (if (not (or (symbolp item) (stringp item) (natnump item)))
               (error "~s is not a string, symbol, or natural number" item)))
  (let ((pkg (if (symbolp (car symlist))
                 (symbol-package (car symlist))
               *package*)))
    (symcat-intern symlist pkg)))


;;; -----------------------------------------------
#|
  Oops, I see this uses INSIST; well, it follows:
|#
;;; -----------------------------------------------

#|
The INSIST macro provides a way of embedding assertions in code, and
raising an error when they are not met.  This can aid in debugging semantic
errors which otherwise do not immediately produce a LISP error.

The definition is taken from Inside Case-Based Reasoning.

The form is

  ( INSIST <name> <expression>* )

The <name> is a symbol which will not be evaluated, and should normally be
the name of the function (etc) within whose definition the INSIST form
appears. Each <expression> will be evaluated, and an error will be raised
if one evaluates to NIL.

Example:

(defun numeric-function (a b)
  (insist numeric-function (numberp a) (numberp b))
  (+ a (* 5 b)))

<cl 8> (numeric-function 4 5)

29 
<cl 9> (numeric-function 5 'z)
Error: (NUMBERP B) failed in NUMERIC-FUNCTION
[1] <cl 10> 

|#


(defmacro insist (fnname &rest exps)
 `(and ,@(make-insist-forms fnname exps)))

(defun make-insist-forms (fnname exps)
 (and (not (null exps))
      (cons `(or ,(car exps)
                 (error "~s failed in ~s"
                        ',(car exps) ',fnname))
            (make-insist-forms fnname (cdr exps)))))


--
Mitch Marks    mitchell@cs.UChicago.EDU
  --But...but...I can see with my own two eyes that it's...
  --Hey, who're you gonna believe: *me* or your own two eyes?

john@clouseau.mitre.org (John D. Burger) (06/26/91)

mitchell@tartarus.uchicago.edu writes:

  [Code to concatenate strings and symbols deleted]

As far as I can tell, the functionality of your INSIST macro is implemented by
a CommonLisp built-in, ASSERT.  The ASSERT macro also has some other nice
features, in that it allows you to specify some SETF places for its second
argument, e.g.

  (defun feed-horse (horse barn)
    (assert (horse-in-barn horse barn)
            ((barn-door-closed? barn)
             ;; More SETF places go here ...
             )
            "The horse has escaped!")
    ;; Code to actually feed horse goes here ...
    )

The debugger then presents these to the user so she can try to recover when
the assertion fails.  Thus:

  (feed-horse *trigger* *empty-barn*)

  ERROR: The horse has escaped!
  Options:
    A: Specify a new value for (BARN-DOOR-CLOSED? BARN)
    B: Exit to top level
--
John Burger                                               john@mitre.org

"You ever think about .signature files? I mean, do we really need them?"
  - alt.andy.rooney