jbaltz@cunixc.cc.columbia.edu (Jerry B. Altzman) (02/13/89)
Does anyone out there have an explode macro or function? For those that don't know, explode is a function that takes an atom and returns the letters that make up the atom itself, as a list. For example: (explode 'foobar) ==> (f o o b a r) As well, if someone has the opposite function, implode, which takes a list of single letters and implodes them into a list consisting of a single atom: for example (implode '(f o o b a r)) ==> (foobar) [For robustness, implode should only take the first character from each element of its argument: (implode '(foo b a r)) ==> (fbar) ] Thanks a bundle. /jerry -- jerry b. altzman consultant, instructional computing consulting group postmaster, columbia university 212 854 8555 (w) 212 853 6737 (h) jbaltz@cunixc.cc.columbia.edu postmast@cuvmb.bitnet nevis::x707jxa (hepnet)
jwz@spice.cs.cmu.edu (Jamie Zawinski) (02/13/89)
Jerry B. Altzman writes: > Does anyone out there have an explode macro or function? I would say that if you want to use EXPLODE/IMPLODE, then you're almost certainly not taking advantage of the language. This is what strings are for; EXPLODE/IMPLODE are leftovers from early Lisps which did not have strings. Say you want to get the first character of a symbol - you could do this with (char (symbol-name symbol) 0) or with (car (explode symbol)) The former will return a character object, which is likely stored as an immediate quantity. The latter will return a pointer to a symbol, which is a data structure of five slots, including a string of its name; also, symbols are entered in a package hash table, which adds some more overhead. EXPLODE could be implemented as (defun explode (sym) (let ((list '()) (string (symbol-name sym))) (dotimes (i (length string)) (push (intern (string (char string i))) list)) (nreverse list))) This means that INTERN will be called for every element of the namestring of the symbol, which will do a hash-table-store. Also, INTERN must be called with a string, so a number of one-character strings are created as well. Finally, a list is consed. So it's a real lose; anything else would be more efficient. Read the chapter of "Common Lisp: the Language" on sequence functions; there are a plethora of functions that operate on strings and arrays as well as lists. Jamie Zawinski jwz@spice.cs.cmu.edu sun!sunpitt!eti!jwz --
barmar@think.COM (Barry Margolin) (02/14/89)
In article <4257@pt.cs.cmu.edu> jwz@spice.cs.cmu.edu (Jamie Zawinski) writes: >EXPLODE could be implemented as [code deleted] >This means that INTERN will be called for every element of the >namestring of the symbol, which will do a hash-table-store. While I agree with everything else you said, I have one question: why are you INTERNing the symbols? For the purposes of EXPLODE and IMPLODE, uninterned symbols should probably work. Actually, it really depends on how the caller plans on using the list. I suspect that the requester knows that this is not the right way to do what he's doing. My guess is he's trying to port some old Franz Lisp code to Common Lisp, and it makes use of EXPLODE. By the way, the function requested (and which you implemented) is closer to MacLisp's EXPLODEC than EXPLODE. EXPLODE in MacLisp returns a list of the characters making up the object's printed representation. It works on any object, and it includes the slashes. It could be defined as: (defun explode (object &optional (intern-p t)) (let ((string (prin1-to-string object)) (result nil) (one-char-string (make-string 1))) (map 'list #'(lambda (char) (setf (char one-char-string 0) char) (if intern-p (intern one-char-string) (make-symbol one-char-string)))))) I added the INTERN-P optional argument to allow interning to be disabled. MacLisp EXPLODEC would be the same, except PRIN1-TO-STRING would be replaced with PRINC-TO-STRING. Note also the reuse of ONE-CHAR-STRING in my definition. I think this is about as efficient as one can get using only standard Common Lisp. Unfortunately there is still the string containing the printed representation; if Common Lisp had a way to define streams, you could implement a stream that took the characters and collected them into a immediately. Barry Margolin Thinking Machines Corp. barmar@think.com {uunet,harvard}!think!barmar
skef@SKEF.SLISP.CS.CMU.EDU (Skef Wholey) (02/14/89)
Both jwz@spice.cs.cmu.edu and barmar@think.com present versions of EXPLODE, with the well-meaning and correct advice that such awful functions be used only for compatibility with smelly old code you don't want to crawl around inside of. However, neither of them comes up with a very efficient implementation of EXPLODE. Come on, guys! I've seen you code, I know you can do better than this! Here's the trick: keep a table that maps character codes to symbols, and index into that. No need to call either INTERN or MAKE-SYMBOL for every character in the explodee. For efficiently exploding symbols, this code will do the trick: ;;; -*- Mode: Lisp; Package: EXPLODE -*- ;;; (in-package "EXPLODE") (defvar *explode-package* (find-package "EXPLODE")) (defvar *explode-initialized* nil) (defvar *char-code-to-symbols* (make-array char-code-limit)) (defun explode (symbol) ;; First time we're called, fill up the table: (unless *explode-initialized* (dotimes (i char-code-limit) (setf (aref *char-code-to-symbols* i) (intern (string (code-char i)) *explode-package*))) (setq *explode-initialized* t)) (do ((i 0 (1+ i)) (list nil) (name (symbol-name symbol))) ((= i (length name)) (nreverse list)) (push (aref *char-code-to-symbols* (char-code (char name i))) list))) Yeah, I know, consing up the list backwards and NREVERSE'ing it is not awesomely elegant, but, hey, it worked the first time: * (compile-file "explode.lisp" :load t) Error output from /usr1/skef/explode.lisp 13-Feb-89 22:11:25. Compiled on 13-Feb-89 22:11:53 by CLC version M1.7 (8-Feb-89). EXPLODE compiled. Finished compilation of file "/usr1/skef/explode.lisp". 0 Errors, 0 Warnings. Elapsed time 0:00:08, run time 0:00:01. T * (in-package "EXPLODE") #<The EXPLODE package, 253/390 internal, 0/9 external> * (explode 'foobar) (F O O B A R) * (explode 'foobar-bazola) (F O O B A R - B A Z O L A) * (explode 'yow!) (Y O W !) To work on numbers and other weird things, SYMBOL-NAME can be replaced with PRIN1-TO-STRING or PRINC-TO-STRING... --