CLisp: set encoding to UTF-8 for strings - unicode

I have some piece of code:
; Palatal Pulmonic Consonants
(loop for e in (list
'(:nasal "ɲ")
'(:plosive "c") '(:plosive "ɟ")
'(:fricative "ç") '(:fricative "ʝ")
'(:approximant "j")
'(:lateral-fricative "ʎ̥˔")
'(:lateral-approximant "ʎ")
'(:lateral-flap "ʎ̯") ) do
(add-sound :palatal (car e) (cadr e)))
I have a lot of these bits for all IPA symbols, this isn't specifically the problem one.
However, attempting to run my code gives this error:
SYSTEM::STRING-READER: Invalid byte #x90 in CHARSET:CP1252 conversion
Which is fine, except I cannot find a way to, WITHIN the script file, tell CLisp that I am typing strings in UTF-8, and I want it to read them and print them in UTF-8.
How do I globally, permanently set UTF-8. I am thinking similarly to Ruby's # encoding: utf-8
Specifically I am using CLisp 2.48.
EDIT:
Here is the full source listing for the file that causes the issue:
(defstruct sound place means sym)
(defparameter $sounds nil)
(defun add-sound (place means sym)
(setf $sounds (append $sounds (list (make-sound :place place :means means :sym sym)))))
; There are alot of IPA symbols, so we'll add them column by column.
; The first column is the Bilabial Pulmonic Consonants
(loop for e in (list
'(:nasal "m") '(:plosive "p")
'(:plosive "b") '(:fricative "ɸ")
'(:fricative "β") '(:trill "ʙ")
'(:flap "ⱱ̟") ) do
(add-sound :bilabial (car e) (cadr e)))
; Labiodental Pulmonic Consonants
(loop for e in (list
'(:nasal "ɱ") '(:plosive "p̪")
'(:plosive "b̪") '(:fricative "f")
'(:fricative "v") '(:approximant "ʋ")
'(:flap "ⱱ") ) do
(add-sound :labiodental (car e) (cadr e)))
; Dental Pulmonic Consonants
(loop for e in (list
'(:nasal "n̪")
'(:plosive "t̪") '(:plosive "d̪")
'(:fricative "θ") '(:fricative "ð") ) do
(add-sound :dental (car e) (cadr e)))
; Alveolar Pulmonic Consonants
(loop for e in (list
'(:nasal "n")
'(:plosive "t") '(:plosive "d")
'(:fricative "s") '(:fricative "z")
'(:trill "r")
'(:flap "ɾ")
'(:lateral-fricative "ɬ") '(:lateral-fricative "ɮ")
'(:lateral-approximant "l")
'(:lateral-flap "ɺ") ) do
(add-sound :alveolar (car e) (cadr e)))
; Palato-Alveolar Pulmonic Consonants
(loop for e in (list
'(:fricative "ʃ") '(:fricative "ʒ")
'(:approximant "ɹ") ) do
(add-sound :palato-alveolar (car e) (cadr e)))
; Retroflex Pulmonic Consonants
(loop for e in (list
'(:nasal "ɳ")
'(:plosive "ʈ") '(:plosive "ɖ")
'(:fricative "ʂ") '(:fricative "ʐ")
'(:approximant "ɻ")
'(:trill "ɽ͡r")
'(:flap "ɽ")
'(:lateral-fricative "ɭ˔̊")
'(:lateral-approximant "ɭ")
'(:lateral-flap "ɺ̢") ) do
(add-sound :retroflex (car e) (cadr e)))
; Palatal Pulmonic Consonants
(loop for e in (list
'(:nasal "ɲ")
'(:plosive "c") '(:plosive "ɟ")
'(:fricative "ç") '(:fricative "ʝ")
'(:approximant "j")
'(:lateral-fricative "ʎ̥˔")
'(:lateral-approximant "ʎ")
'(:lateral-flap "ʎ̯") ) do
(add-sound :palatal (car e) (cadr e)))
; Velar Pulmonic Consonants
(loop for e in (list
'(:nasal "ŋ")
'(:plosive "k") '(:plosive "ɡ")
'(:fricative "x") '(:fricative "ɣ")
'(:approximant "ɰ")
'(:lateral-fricative "ʟ̝̊")
'(:lateral-approximant "ʟ") ) do
(add-sound :velar (car e) (cadr e)))
; Uvular Pulmonic Consonants
(loop for e in (list
'(:nasal "ɴ")
'(:plosive "q") '(:plosive "ɢ")
'(:fricative "χ") '(:fricative "ʁ")
'(:trill "ʀ")
'(:flap "ɢ̆") ) do
(add-sound :uvular (car e) (cadr e)))
; Pharyngeal Pulmonic Consonants
(loop for e in (list
'(:plosive "ʡ")
'(:fricative "ħ") '(:fricative "ʕ")
'(:trill "ʜ") '(:trill "ʢ")
'(:flap "ʡ̯") ) do
(add-sound :pharyngeal (car e) (cadr e)))
; Glottal Pulmonic Consonants
(loop for e in (list
'(:plosive "ʔ")
'(:fricative "h") '(:fricative "ɦ") ) do
(add-sound :glottal (car e) (cadr e)))

Summary
Either
use the OS:
Windows: How do I view and change the system locale settings to use my language of choice?
Unix: How to change locale environment variable?
OR
run CLISP with -E UTF-8 command line argument (clisp.exe -E UTF-8 /path/....), OR
set the default encodings in the init file.
Note that setting these variables inside your lisp file which errors out will NOT help because by the time CLISP reads the variables, the file has already been opened in the wrong external format.
CLISP FAQ: What do charset errors mean?
This means that you are trying to read (“invalid byte”) or write (“character cannot be represented”) a non-ASCII character from (or to) a character stream which has ASCII :EXTERNAL-FORMAT. The default is described in -Edomain encoding.
This may also be caused by filesystem access. If you have files with names incompatible with your CUSTOM:*PATHNAME-ENCODING*, filesystem access (e.g., DIRECTORY) will SIGNAL this ERROR. You will need to set CUSTOM:*PATHNAME-ENCODING* or pass -Edomain encoding to CLISP. Using a “1:1” encoding, such as CHARSET:ISO-8859-1, should help you avoid this error.
Please see the official site for full documentation.
PS. You now owe me 10 zorkmids
PPS. Your code (list '(...) '(...) ...) looks weird, you might want to replace it with '((...) (...) ...). I mean, your works too, it's just bad style.

Related

AutoLisp - Two basic functions: Determining the number and value ​of block attributes

I have to write the function that determines the number and values ​​of block attributes in AutoLisp.
I have function which count the atributes:
(defun c:Test (/ s ss)
(if (and (princ "\n Select FIRST Attributed Block :")
(setq s (ssget "_+.:S:E" '((0 . "INSERT") (66 . 1))))
(princ "\n Select the SECOND Attributed Block :")
(setq ss (ssget "_+.:S:E" '((0 . "INSERT") (66 . 1))))
)
(mapcar
'length
(mapcar
'(lambda (a)
(mapcar
'(lambda (x) (vla-get-textstring x))
(vlax-invoke (vlax-ename->vla-object a) 'getattributes)
)
)
(list (ssname s 0) (ssname ss 0))
)
)
)
)**
A function that returns the values ​​of attributes:
(defun c:Test (/ ss n e x)
(while (progn (princ "\n Select single attributed block :")
(setq ss (ssget "_+.:S" '((0 . "INSERT") (66 . 1))))
)
(setq n (entnext (ssname ss 0)))
(while (not (eq (cdr (assoc 0 (setq e (entget n)))) "SEQEND" ))
(if (eq (cdr (assoc 0 e)) "ATTRIB")
(print (cdr (assoc 1 e)))
)
(setq n (entnext n))
)
)
(princ)
)
Could you help me to combine this to functions into one?
Here is a lisp program that will loop over all blocks from a user selection set and:
1.) print block name
2.) print the association list of AttributeTag.AttributeValue
3.) print the list of AttributeTags
4.) print the list of AttributeValues
5.) print the number of AttributeValues
I also attached what the command line output should look like.
Lisp command line output
;;www.cadwiki.net
(defun c:test (/ SSINPUT)
(setq ssInput (ssget (list '(0 . "insert"))))
(PRINT-BLOCK-ATTRIBUTE-INFO ssInput)
(princ)
)
(defun PRINT-BLOCK-ATTRIBUTE-INFO (ssInput / ATTRIBUTETAGS ATTRIBUTETAGSTOVALUES ATTRIBUTEVALUES BLOCKENTITY BLOCKVLAOBJECT I NUMBEROFBLOCKATTRIBUTES
)
(setq i 0)
(if (= ssInput nil)
(progn
(princ "ssInput was nothing, exiting.")
(exit)
)
)
(princ (strcat "\nItems in selection set: " (itoa (sslength ssInput))))
(while (< i (sslength ssInput))
(setq blockEntity (ssname ssInput i))
(setq blockVlaObject (vlax-ename->vla-object blockEntity))
(setq attributeTagsToValues (GET-BLOCK-ATTRIBUTE-NAME-TO-VALUE-ASSOC blockEntity))
(princ (strcat "\nBlock name: " (vla-get-name blockVlaObject)))
(princ "\nBlock attributes tag to values association list: ")
(princ attributeTagsToValues)
(setq attributeTags (GET-NTHS-FROM-LISTS 0 attributeTagsToValues nil))
(princ "\nBlock attribute tags list: ")
(princ attributeTags)
(setq attributeValues (GET-LAST-ITEM-FROM-EACH-LIST attributeTagsToValues))
(princ "\nBlock attributes values list: ")
(princ attributeValues)
(princ "\nNumber of block attributes: ")
(setq numberOfBlockAttributes (itoa (length attributeValues)))
(princ numberOfBlockAttributes)
(setq i (+ i 1))
)
)
(defun GET-NTHS-FROM-LISTS (N LSTs removeDuplicates / CT LST2 LST IT)
(setq LST2 nil)
(foreach LST LSTs
(setq IT (nth N LST))
(if removeDuplicates
(if (not (member IT LST2))
(setq LST2 (append LST2 (list IT)))
)
(setq LST2 (append LST2 (list IT)))
)
)
LST2
)
(defun GET-LAST-ITEM-FROM-EACH-LIST (LSTs / CDRs FAIL LST)
(setq CDRs nil
FAIL nil
)
(if (not (= (type LSTs) 'LIST))
(setq FAIL "not a list")
)
(if (not FAIL)
(foreach LST LSTs
(setq FAIL (cond
((not (= (type LST) 'LIST)) "non-list member")
((not (cdr LST)) "no CDR")
(T nil)
)
)
(if (not FAIL)
(setq CDRs (append CDRs (list (cdr LST))))
)
)
)
CDRs
)
(defun GET-BLOCK-ATTRIBUTE-NAME-TO-VALUE-ASSOC (entity / COUNTER COUNTER2 COUNTERMAX COUNTERMAX2 DXFCODE0 DXFCODE2 DXFCODE66 DXFCODE8 DXFCODECODE-1 ENTITIESTORETURN ENTITYDXFCODES *ERROR* RETURNLIST
SUPPLIEDTRUENAME TRUENAME ATTRIBUTETAG ATTRIBUTEVALUE DXFCODE-1 ENTITYNAMEFORDRILLING SUBLIST TAGSANDVALUES THECALLINGFUNCTIONSNAME
)
(setq counter 0) ;initialize counter to 0 for while loop
(if ;if
(/= entity nil) ;entity is not nil
(progn ;progn wrap
(setq entityDxfCodes (entget entity)) ;set the varaible entityDxfCodes to the list of entities from the en varaible
;; you can use the method here to find any value from a dxfCodecode
(setq dxfCode-1 (cdr (assoc -1 entityDxfCodes))) ;set dxfCode-1 to the second element of the item that has -1 as it's first element, this is the entity name
(setq dxfCode0 (cdr (assoc 0 entityDxfCodes))) ;set dxfCode0 to the element of the item that has 0 as it's first element, this is the entity type
(setq dxfCode2 (cdr (assoc 2 entityDxfCodes))) ;set dxfCode8 to the second element of the item that has 8 as it's first element, this is the name, or block name
(setq dxfCode8 (cdr (assoc 8 entityDxfCodes))) ;set dxfCode8 to the second element of the item that has 8 as it's first element, this is the layer
(setq dxfCode66 (cdr (assoc 66 entityDxfCodes))) ;set dxfCode66 to the second element of the item that has 66 as it's first element, this is the attribute flag
(setq entityNameForDrilling entity)
(if ;if start
(= dxfCode66 1) ;entity attribute flag is 1
(progn ;progn wrap
(while (/= dxfCode0 "SEQEND") ;while loop to drill to each sub entity in a block
(setq attributeTag (cdr (assoc 2 entityDxfCodes))) ;set attributeTag to the second element of the second Dxf code (assoc 2) of the entityDxfCodes variable
(setq attributeValue (cdr (assoc 1 entityDxfCodes))) ;set attributeValue to the second element of the first Dxf code (assoc 1) of the entityDxfCodes variable
(if
(/= attributeValue nil)
(progn
(setq sublist (cons attributeTag attributeValue))
(setq tagsAndValues (cons sublist TagsAndValues))
)
)
(setq entityNameForDrilling (entnext entityNameForDrilling))
(setq entityDxfCodes (entget entityNameForDrilling))
(setq dxfCode0 (cdr (assoc 0 entityDxfCodes)))
)
) ;progn wrap end
) ;if end
) ;progn wrap end
) ;if end
(setq returnList tagsAndValues)
)

AutoLISP, How to export my selected polylines to a CSV with a name

I have this code below. It exports the selected polylines lenght to a CSV but it does not give it a name so i cant make a difference between two(or more) types of polyline.
My question is how to modify this code in order to be able to export the lenghts with the name of the linetype.
For example: I loaded ZIGZAG and TRACKS linetype, next I run my function and select all of the drawn polylines and I want to see in my CSV that which linetype is how long by name.
(defun c:Polyline_számoló (/ s i e l fn)
(if (and(setq s (ssget '((0 . "LWPOLYLINE"))))
(setq fn (getfiled "Create Output File" "" "csv" 1)))
(progn
(setq s (_SortSSByXValue s))
(setq i (sslength s))
(while (setq e(ssname s (setq i (1- i))))
(setq l (cons (vla-get-length (vlax-ename->vla-object e)) l))
(ssdel e s)
)
)
)
(setq l (list (cd:CON_All2Str l nil)))
(if (LM:WriteCSV l fn)
(startapp "explorer" fn)
)
(princ)
)
(defun cd:CON_All2Str (Lst Mode)
(mapcar
(function
(lambda (%)
(if Mode
(vl-prin1-to-string %)
(vl-princ-to-string %)
)
)
)
Lst
)
)
(defun _SortSSByXValue (ss / lst i e add)
(if (eq (type ss) 'PICKSET)
(progn
(repeat (setq i (sslength ss))
(setq lst (cons (cons (setq e (ssname ss (setq i (1- i))))
(cadr (assoc 10 (entget e)))
)
lst
)
)
)
(setq add (ssadd))
(foreach e (vl-sort lst (function (lambda (a b) (< (cdr a) (cdr b))))) (ssadd (car e) add))
(if (> (sslength add) 0)
add
)
)
)
)
(defun LM:writecsv ( lst csv / des sep )
(if (setq des (open csv "w"))
(progn
(setq sep (cond ((vl-registry-read "HKEY_CURRENT_USER\\Control Panel\\International" "sList")) (",")))
(foreach row lst (write-line (LM:lst->csv row sep) des))
(close des)
t
)
)
)
(defun LM:lst->csv ( lst sep )
(if (cdr lst)
(strcat (LM:csv-addquotes (car lst) sep) sep (LM:lst->csv (cdr lst) sep))
(LM:csv-addquotes (car lst) sep)
)
)
(defun LM:csv-addquotes ( str sep / pos )
(cond
( (wcmatch str (strcat "*[`" sep "\"]*"))
(setq pos 0)
(while (setq pos (vl-string-position 34 str pos))
(setq str (vl-string-subst "\"\"" "\"" str pos)
pos (+ pos 2)
)
)
(strcat "\"" str "\"")
)
( str )
)
)
Here's a lisp function that will export a csv file.
The csv file contains two sections:
1.) a length summary by linetype name
2.) an individual line summary with length and linetype
csv example:
--Length Summary By LineType--
LineType,Length
CENTER,739.97
HIDDEN,1858.61
--Length Breakdown By Individual Line--
LineType,Length
CENTER,246.656
HIDDEN,309.768
HIDDEN,309.768
CENTER,246.656
HIDDEN,309.768
HIDDEN,309.768
CENTER,246.656
HIDDEN,309.768
HIDDEN,309.768
Lisp code
;;www.cadwiki.net
(defun c:test (/ s i e l fn CSVSTRING CSVSTRINGLIST DATAITEM individualLineDataList LINELENGTH LINETYPE VLAOBJECT NEWASSOC NEWLENGTH PREVIOUSLENGTH lineTypeToLengthAssoc SUMMARYENTRY
)
(if (and (setq s (ssget '((0 . "LWPOLYLINE"))))
(setq fn (getfiled "Create Output File" "" "csv" 1))
)
(progn
(setq s (_SortSSByXValue s))
(setq i (sslength s))
(setq individualLineDataList (list))
(while (setq e (ssname s (setq i (1- i))))
(setq vlaObject (vlax-ename->vla-object e))
(setq lineType (vla-get-linetype vlaObject))
(setq lineLength (vla-get-length vlaObject))
(setq dataItem (list lineType lineLength))
(setq individualLineDataList (cons dataItem individualLineDataList))
(setq summaryEntry (assoc lineType lineTypeToLengthAssoc))
(if (/= summaryEntry nil)
(progn
(setq previousLength (cdr summaryEntry))
(setq newLength (+ previousLength lineLength))
(setq newAssoc (cons lineType newLength))
(setq lineTypeToLengthAssoc (REMOVE-ASSOC-BY-KEY lineType lineTypeToLengthAssoc))
(setq lineTypeToLengthAssoc (cons newAssoc lineTypeToLengthAssoc))
)
(progn
(setq newAssoc (cons lineType lineLength))
(setq lineTypeToLengthAssoc (cons newAssoc lineTypeToLengthAssoc))
)
)
(ssdel e s)
)
)
)
(setq csvStringList (list (list "--Length Summary By LineType--")))
(setq csvStringList (cons (list "LineType" "Length") csvStringList))
(foreach assocItem lineTypeToLengthAssoc
(setq csvString (summaryAssocToStringList assocItem))
(setq csvStringList (cons csvString csvStringList))
)
(setq csvStringList (cons (list "--Length Breakdown By Individual Line--") csvStringList))
(setq csvStringList (cons (list "LineType" "Length") csvStringList))
(foreach item individualLineDataList
(setq csvString (cd:CON_All2Str item nil))
(setq csvStringList (cons csvString csvStringList))
)
(setq csvStringList (reverse csvStringList))
(if (LM:WriteCSV csvStringList fn)
(startapp "explorer" fn)
)
(princ)
)
(defun REMOVE-ASSOC-BY-KEY (assocKey assocList / newAssocList item)
(setq newAssocList nil)
(foreach item assocList
(if (not (= (car item) assocKey))
(setq newAssocList (append newAssocList (list item)))
)
)
newAssocList
)
(defun summaryAssocToStringList (assocItem / LINELENGTH LINETYPE STRINGLIST)
(setq lineType (car assocItem))
(setq lineLength (cdr assocItem))
(setq stringList (list lineType (rtos lineLength 2 2)))
)
(defun cd:CON_All2Str (Lst Mode)
(mapcar
(function
(lambda (%)
(if Mode
(vl-prin1-to-string %)
(vl-princ-to-string %)
)
)
)
Lst
)
)
(defun _SortSSByXValue (ss / lst i e add)
(if (eq (type ss) 'PICKSET)
(progn
(repeat (setq i (sslength ss))
(setq lst (cons (cons (setq e (ssname ss (setq i (1- i))))
(cadr (assoc 10 (entget e)))
)
lst
)
)
)
(setq add (ssadd))
(foreach e (vl-sort lst (function (lambda (a b) (< (cdr a) (cdr b))))) (ssadd (car e) add))
(if (> (sslength add) 0)
add
)
)
)
)
(defun LM:writecsv (lst csv / des sep)
(if (setq des (open csv "w"))
(progn
(setq sep (cond ((vl-registry-read "HKEY_CURRENT_USER\\Control Panel\\International" "sList"))
(",")
)
)
(foreach row lst (write-line (LM:lst->csv row sep) des))
(close des)
t
)
)
)
(defun LM:lst->csv (lst sep)
(if (cdr lst)
(strcat (LM:csv-addquotes (car lst) sep) sep (LM:lst->csv (cdr lst) sep))
(LM:csv-addquotes (car lst) sep)
)
)
(defun LM:csv-addquotes (str sep / pos)
(cond
((wcmatch str (strcat "*[`" sep "\"]*"))
(setq pos 0)
(while (setq pos (vl-string-position 34 str pos))
(setq str (vl-string-subst "\"\"" "\"" str pos)
pos (+ pos 2)
)
)
(strcat "\"" str "\"")
)
(str)
)
)

read words from a file into nested list in common lisp programming language

I have a file named test.txt, it contains
"hello this is a test file"
I want to read it from the file so that every word represents lists of character and every paragraph represents lists of words means that I want to store them into a nested list like:
(list(list (h e l l o)) (list(t h i s))(list(i s)) (list(a)) (list(t e s t)) (list(f i l e))))
I am totally new in lisp and have a lot of confusion about this problem.
Solution without any dependencies
(defun split (l &key (separators '(#\Space #\Tab #\Newline)) (acc '()) (tmp '()))
(cond ((null l) (nreverse (if tmp (cons (nreverse tmp) acc) acc)))
((member (car l) separators)
(split (cdr l) :separators separators
:acc (if tmp (cons (nreverse tmp) acc) acc)
:tmp '()))
(t
(split (cdr l) :separators separators
:acc acc
:tmp (cons (car l) tmp)))))
(defun read-file-lines (file-path)
(with-open-file (f file-path :direction :input)
(loop for line = (read-line f nil)
while line
collect line)))
(defun read-file-to-word-characters (file-path)
(mapcan (lambda (s) (split (coerce s 'list)))
(read-file-lines file-path)))
(read-file-to-word-characters "~/test.lisp.txt")
;; ((#\h #\e #\l #\l #\o) (#\t #\h #\i #\s) (#\i #\s) (#\a) (#\t #\e #\s #\t)
;; (#\f #\i #\l #\e))
Convert the characters to one-letter strings:
;; apply to elements of nested list (= a tree) the conversion function `string`
(defun map-tree (fn tree)
(cond ((null tree) '())
((atom tree) (funcall fn tree))
(t (mapcar (lambda (branch) (map-tree fn branch)) tree))))
(map-tree #'string (read-file-to-word-characters "~/test.lisp.txt"))
;; (("h" "e" "l" "l" "o") ("t" "h" "i" "s") ("i" "s") ("a") ("t" "e" "s" "t")
;; ("f" "i" "l" "e"))
Content of "~/test.lisp.txt":
hello this
is a test file
Solution using cl-ppcre (Edi Weitz's congenial regex package)
;; look here in an answer how to use cl-ppcre:split
;; https://stackoverflow.com/questions/15393797/lisp-splitting-input-into-separate-strings
(ql:quickload :cl-ppcre)
(defun read-file-lines (file-path)
(with-open-file (f file-path :direction :input)
(loop for line = (read-line f nil)
while line
collect line)))
(defun string-to-words (s) (cl-ppcre:split "\\s+" s))
(defun to-single-characters (s) (coerce s 'list))
(defun read-file-to-character-lists (file-path)
(mapcan (lambda (s)
(mapcar #'to-single-characters
(string-to-words s)))
(read-file-lines file-path)))
(read-file-to-character-lists "~/test.lisp.txt")
;; ((#\h #\e #\l #\l #\o) (#\t #\h #\i #\s) (#\i #\s) (#\a) (#\t #\e #\s #\t)
;; (#\f #\i #\l #\e))
;; or use above's function:
(map-tree #'string (read-file-to-character-lists "~/test.lisp.txt"))
;; (("h" "e" "l" "l" "o") ("t" "h" "i" "s") ("i" "s") ("a") ("t" "e" "s" "t")
;; ("f" "i" "l" "e"))
;; or:
(defun to-single-letter-strings (s) (cl-ppcre:split "\\s*" s))
(defun read-file-to-letter-lists (file-path)
(mapcan (lambda (s)
(mapcar #'to-single-letter-strings
(string-to-words s)))
(read-file-lines file-path)))
(read-file-to-letter-lists "~/test.lisp.txt")
;; (("h" "e" "l" "l" "o") ("t" "h" "i" "s") ("i" "s") ("a") ("t" "e" "s" "t")
;; ("f" "i" "l" "e"))

How to do auto input in multiple read-lines?

How to do auto input in multiple read-line?
(let ((out (with-output-to-string (*standard-output*)
(let ((*standard-input* (make-string-input-stream "y y")))
(when (find (read-line) '("yes" "y" "t") :test #'string-equal)
(print "aaaaa"))
(when (find (read-line) '("yes" "y" "t") :test #'string-equal)
(print "bbbbbb"))
))))
out)
I try like this, and I get:
; Evaluation aborted on #<END-OF-FILE {10048FD503}>.
This code work with read, but I need with read-line.
Another possibility is to use the parameter of read-line that requires to return nil on end of file:
(let ((out (with-output-to-string (*standard-output*)
(let ((*standard-input* (make-string-input-stream "y y")))
(when (find (read-line *standard-input* nil) '("yes" "y" "t") :test #'string-equal)
(print "aaaaa"))
(when (find (read-line *standard-input* nil) '("yes" "y" "t") :test #'string-equal)
(print "bbbbbb"))))))
out)
I made it work like this:
(with-output-to-string (*standard-output*)
(with-input-from-string (*standard-input* (format nil "y~%y"))
(when (find (read-line) '("yes" "y" "t") :test #'string-equal)
(print "aaaaa"))
(when (find (read-line) '("yes" "y" "t") :test #'string-equal)
(print "bbbbbb"))))
The without-to-string is unnecessary for an example...
CL-USER 177 > (flet ((yes-p (input-string &aux (yes-words '("yes" "y" "t")))
"returns T when the input-string is one of yes, y or t."
(find input-string yes-words :test #'string-equal)))
(with-input-from-string (*standard-input* (format nil "y~%y"))
(when (yes-p (read-line))
(print "aaaaa"))
(when (yes-p (read-line))
(print "bbbbbb"))
(values)))
"aaaaa"
"bbbbbb"

Functions inside a loop behaves differently

So I have a loop to just repeat the little text game I have made about dota, but when the function 'play' is called within a loop it doesn't return the result of my cond function, it just takes an input and then moves on to the next loop.
;;;;learn the invoker combo's
(defparameter *invoker-combo* '((cold-snap (3 0 0) 'QQQ);all of the possible invoker combo's
(ghost-walk (2 1 0) 'QQW)
(Ice-Wall (2 0 1) 'QQE)
(EMP (0 3 0) 'WWW)
(Tornado (1 2 0) 'QWW)
(Alacrity (0 2 1) 'WWE)
(Sun-Strike (0 0 3) 'EEE)
(Forge-Spirit (1 0 2) 'QEE)
(Chaos-Meteor (0 1 2) 'WEE)
(Deafening-Blast (1 1 1) 'QWE)))
(defun rand-combo (invoker-combo);returns a random combo
(nth (random (length invoker-combo))invoker-combo))
(defun count-letters (input);converts the keyboard strokes into numbers to be compared as it doesn't matter what order they are in, just that there is the correct quantity of them e.g QQE could also be written QEQ.
(append
(list (count #\Q input)
(count #\W input)
(count #\E input))))
(defun try-for-combo (rand-combo);takes i-p and compares it with the value for the random combo
(print(car rand-combo))
(let* ((i-p (string-upcase(read-line)))
(try (count-letters i-p)))
(cond ((equal try (cadr rand-combo))'Good-job)
((equal i-p "END")(list 'Thanks 'for 'playing))
(t (list i-p 'was 'wrong 'correct 'is (caddr(assoc (car rand-combo)*invoker-combo*)))))))
(defun play ()
(try-for-combo (rand-combo *invoker-combo*)))
(defun loop-play (x)
(loop for i from 0 to x
:do (play)))
If I call the function 'play' I get the following o/p:
FORGE-SPIRIT asdf
("ASDF" WAS WRONG CORRECT IS 'QEE)
or
ALACRITY wwe
GOOD-JOB
But if I call the function 'loop-play' I get the following o/p:
Break 3 [7]> (loop-play 2)
SUN-STRIKE eee
ALACRITY wwe
TORNADO qww
NIL
Can someone explain to me why this is happening?
EDIT: feel free to change the title, I didn't really know what to put.
The indentation and formatting of the code is poor. Please make it easier for you and for us to read the code.
(defun try-for-combo (rand-combo);takes i-p and compares it with the value for the random combo
(print(car rand-combo))
(let* ((i-p (string-upcase(read-line)))
(try (count-letters i-p)))
(cond ((equal try (cadr rand-combo))'Good-job) ; wrong indent level
((equal i-p "END")(list 'Thanks 'for 'playing))
(t (list i-p 'was 'wrong 'correct 'is (caddr(assoc (car rand-combo)*invoker-combo*)))))))
lacks spaces between s-expressions
wrong indentation levels
structure of the code unclear
does not use built-in documentation features
some lines are too long
Better:
(defun try-for-combo (rand-combo)
"takes i-p and compares it with the value for the random combo" ; built in doc
(print (car rand-combo))
(let* ((i-p (string-upcase (read-line)))
(try (count-letters i-p)))
(cond ((equal try (cadr rand-combo)) ; indentation
'Good-job)
((equal i-p "END")
(list 'Thanks 'for 'playing))
(t
(list i-p 'was 'wrong 'correct 'is ; several lines
(caddr (assoc (car rand-combo)
*invoker-combo*)))))))
I would propose to use an editor which actually understands some Lisp formatting. like GNU Emacs / SLIME, Clozure CL's Hemlock, LispWorks' editor...
If you are unsure about formatting, you can also ask Lisp to do it. Clisp is not that good at formatting, but something like SBCL or CCL would do:
* (let ((*print-case* :downcase))
(pprint '(defun try-for-combo (rand-combo)
(print (car rand-combo))
(let* ((i-p (string-upcase (read-line)))
(try (count-letters i-p)))
(cond ((equal try (cadr rand-combo))
'Good-job) ((equal i-p "END")
(list 'Thanks 'for 'playing))
(t (list i-p 'was 'wrong 'correct 'is
(caddr (assoc (car rand-combo)
*invoker-combo*)))))))))
And you get nicely formatted code:
(defun try-for-combo (rand-combo)
(print (car rand-combo))
(let* ((i-p (string-upcase (read-line))) (try (count-letters i-p)))
(cond ((equal try (cadr rand-combo)) 'good-job)
((equal i-p "END") (list 'thanks 'for 'playing))
(t
(list i-p 'was 'wrong 'correct 'is
(caddr (assoc (car rand-combo) *invoker-combo*)))))))
Automatic indenting of Lisp code by the editor saves you a lot of work.
There are hints for manual indentation.
Your try-for-combo function doesn't actually output anything. Rather, it returns values.
In the REPL, if you evaluate a form, like (+ 1 2), it will always print the evaluation of that form at the end (in this case, 3). However, consider instead (+ 1 (print 2)). The print function actually outputs the argument to standard output, then returns the value itself. So this will show (on the repl)
2
3
The 2 is outputted first, because (print 2) itself prints 2. Then, the form (+ 1 (print 2)) is evaluates to the same things as (+ 1 2), or 3.
In your case, your try-for-combo function should look like:
(defun try-for-combo (rand-combo)
(print (car rand-combo))
(let* ((i-p (string-upcase(read-line)))
(try (count-letters i-p)))
(print
(cond
((equal try (cadr rand-combo)) 'Good-job)
((equal i-p "END") (list 'Thanks 'for 'playing))
(t (list i-p 'was 'wrong 'correct 'is (caddr(assoc (car rand-combo) *invoker-combo*))))))
nil))
This will print the result of that cond form, and return 'nil'.
That's just the difference between the output your program does and the output the Lisp system does for each evaluation:
print prints something (a newline and then its argument) and returns a value. The value is printed by the REPL. Thus we see output twice:
[3]> (print "3")
"3"
"3"
Next we do several call to print in a progn. The value of the progn form is printed by the REPL. The first three strings are printed by the code and the last string is printed because of the Lisp REPL printing the value:
[4]> (progn (print "1") (print "2") (print "3"))
"1"
"2"
"3"
"3"