How to convert a string to a list of symbols? - string

I am not sure if I am missing something very basic here, but I want to read in a string from a file and use it in my later program as a list of symbols, i.e.
"8C TS" should become ((8 C) (T S))
I know that I can split the initial string using the split-sequence library without problems, but as a string is a sequence of characters I end up with
> (loop :for c :across "8C" :collect c)
(#\8 #\C)
Is it possible to convert the initial string as specified above or is there some reason why this should not/could not be done?

If you want to represent cards as a generic datastructure, you might as well use a vector instead of a list. A vector of characters is just a string, so (split-sequence #\space hand), which gives ("8C" "TS"), should be enough. You'd define a hand to be a list of cards, a card a string of length 2 containing value and suit, and value and suit as characters.
You then use simple readers to access the attributes:
(defun card-value (card)
(aref card 0))
(defun card-suit (card)
(aref card 1))
If you want a more explicit approach, you might prefer defining classes or structs for each:
(defclass hand ()
((cards :initarg :cards
:reader hand-cards)))
(defclass card ()
((value :initarg :value
:reader card-value)
(suit :initarg :suit
:reader card-suit)))
Parsing creates such objects:
(defun read-hand (string &aux (upcased (string-upcase string)))
(make-instance 'hand
:cards (mapcar #'read-card
(split-sequence #\space upcased))))
(defun read-card (string)
(make-instance 'card
:value (case (aref string 0)
(#\T 10)
(#\J 11)
(#\Q 12)
(#\K 13)
(#\A 14)
(t (parse-integer (string (aref string 0)))))
:suit (intern (aref string 1) '#:keyword))
This would represent the value as an integer and the suit as a keyword. You might then want to define predicates like card=, card-suit-=, card-value-< etc.

Related

Accessing members of a struct in array Clisp

Imagine I have a function that receive a array of structs like this:
(defun name-of-func (array)
(dotimes (i (array-total-size array))
(print (aref array i))))
and the stuct is something like this
(defstruct sTSP
cidade
x
y)
How can I access the field x on i position of the array?
Please take a look at the definition of defstruct.
It is long, but well worth the read.
If you are lazy, like we all are, search for reader:
(defstruct foo x y z)
(defparameter foo (make-foo :x 2 :y 4))
(foo-x foo)
==> 2
(foo-y foo)
==> 4
(foo-z foo)
==> NIL
PS1. Please note that array-total-size should
not be used with aref but
rather with row-major-aref.
The difference is with multi-dimensional arrays which are implemented
as vectors under the hood.
E.g., your function will fail on (make-array '(2 2) :initial-element (make-sTSP)).
PS2. I re-use foo for both type name and variable name to illustrate that they reside is different name spaces.

How to map reduce referential data in functional programming

I want to make a tile based game using functional programming.
Game has 6 tiles and each tile can occupy a piece. Here's my data structure:
{
:pieces {
1 { :type 'p' }
2 { :type 'r' }
}
:tiles [{}
{}
{:current 1}
{}
{:current 2}
{}]
}
This is a serial representation of the game tiles:
'00p0r0'
I need a function that transforms this serial data into my data structure.
There are ways to optimize this but it should give you the right idea. See below code for alternate suggestion.
The interesting thing here is you are mapping a fixed tile model over a variable piece type set. There would be efficiency in making the piece model a fixed array as well.
Note: the input needs to be a string and not using single quotes as in your example: (e.g. "00p0r0" vs. '00p0r0').
(def empty-tile-map
{:piece-count 0
:pieces {}
:tiles (into [] (repeat 6 {}))})
(defn set-tile
"Sets the tile to the index reference to piece"
[arref indx value]
(assoc arref indx {:current value}))
(defn string-to-board-reducer
"Reduce function to create pieces and reference in tile"
[{:keys [piece-count tcount tiles] :as acc} x]
(let [ccnt (inc piece-count)
nmap (assoc acc :tcount (inc tcount))]
(if (> (int x) 48)
(assoc
(assoc
(update-in nmap [:pieces] assoc ccnt {:type x})
:tiles (set-tile tiles tcount ccnt))
:piece-count ccnt)
nmap)
))
(defn string-to-board
[s]
"Take serializated string and generate board"
(dissoc (reduce string-to-board-reducer
(assoc empty-tile-map :tcount 0) (seq s))
:tcount))
Alternate
By definition, the input string implicitly contains all the information you want. A series of functions could be defined that work with the serialize string as the board state, replacing it with a new string as you go. Just a suggestion.

String Switch in Common Lisp

How do I do a conditional dispatch on a string in Common Lisp? I'm looking for something like:
(case str
("a" "found an a")
("abc" "found an abc")
(otherwise "other"))
so (case "a") returns "found an a"
Alexandria has the macros switch, cswitch, eswitch:
(switch (str :test #'equal)
("a" "found an a")
("abc" "found an abc")
(t "other"))
The string-case library implements this functionality. It is available from quicklisp.
A trivial (and potentially slow) variant would be something like:
(defmacro string-case (str &rest forms)
(let* ((strval (gensym "STRVAL"))
(cond-body (loop for (s . f) in forms
collect `((string= ,strval ,s) ,#f))))
`(let ((,strval ,str)) (cond ,#cond-body))))
This (unfortunately) does not allow for an else, otherwise or grouping of strings, but making that extension should be pretty straight-forward. Using the existing string-case from quicklisp is probably the better choice.
This is from the Practical Common LISP by Seibel only changes made are using the read-char instead of read-line. This worked for me using simple characters "A" "B" etc. To see what the characters and strings actually are try using (format nil "~:C" c) c being character and (format nil "~:S" s) s being string.
(defun prompt-read(prompt)
(format *query-io* "~a: " prompt)
(force-output *query-io*)
(read-char *query-io*))
(defun move()
(case(prompt-read "ROW[A-C]")
(#\A (print "A"))
(#\B (print "B"))
(#\C (print "C"))))
I was able to get it working by converting the string to a symbol:
(case (intern (string-upcase str))
(a "found an a")
(abc "found an abc")
(otherwise "other"))

Split a string even if the last character is a delimiter

I want to delete some characters at the end of a string.
I made this function :
(defun del-delimiter-at-end (string)
(cond
((eq (delimiterp (char string (- (length string) 1))) nil)
string )
(t
(del-delimiterp-at-end (subseq string 0 (- (length string) 1))) ) ) )
with this :
(defun delimiterp (c) (position c " ,.;!?/"))
But I don't understand why it doesn't work. I have the following error :
Index must be positive and not -1
Note that I want to split a string in list of strings, I already looked here :
Lisp - Splitting Input into Separate Strings
but it doesn't work if the end of the string is a delimiter, that's why I'm trying to do that.
What am I doing wrong?
Thanks in advance.
The Easy Way
Just use string-right-trim:
(string-right-trim " ,.;!?/" s)
Your Error
If you pass an empty string to you del-delimiter-at-end, you will be passing -1 as the second argument to char.
Your Code
There is no reason to do (eq (delimiterp ...) nil); just use (delimiterp ...) instead (and switch the clauses!)
It is mode idiomatic to use if and not cond when you have just two clauses and each has just one form.
You call subseq recursively, which means that you not only allocate memory for no reason, your algorithm is also quadratic in string length.
There are really two questions here. One is more specific, and is described in the body of the question. The other is more general, and is what the title asks about (how to split a sequence). I'll handle the immediate question that's in the body, of how to trim some elements from the end of a sequence. Then I'll handle the more general question of how to split a sequence in general, and how to split a list in the special case, since people who find this question based on its title may be interested in that.
Right-trimming a sequence
sds answered this perfectly if you're only concerned with strings. The language already includes string-right-trim, so that's probably the best way to solve this problem, if you're only concerned with strings.
A solution for sequences
That said, if you want a subseq based approach that works with arbitrary sequences, it makes sense to use the other sequence manipulation functions that the language provides. Many functions take a :from-end argument and have -if-not variants that can help. In this case, you can use position-if-not to find the rightmost non-delimiter in your sequence, and then use subseq:
(defun delimiterp (c)
(position c " ,.;!?/"))
(defun right-trim-if (sequence test)
(let ((pos (position-if-not test sequence :from-end t)))
(subseq sequence 0 (if (null pos) 0 (1+ pos)))))
(right-trim-if "hello!" 'delimiterp) ; some delimiters to trim
;=> "hello"
(right-trim-if "hi_there" 'delimiterp) ; nothing to trim, with other stuff
;=> "hi_there"
(right-trim-if "?" 'delimiterp) ; only delimiters
;=> ""
(right-trim-if "" 'delimiterp) ; nothing at all
;=> ""
Using complement and position
Some people may point out that position-if-not is deprecated. If you don't want to use it, you can use complement and position-if to achieve the same effect. (I haven't noticed an actual aversion to the -if-not functions though.) The HyperSpec entry on complement says:
In Common Lisp, functions with names like xxx-if-not are related
to functions with names like xxx-if in that
(xxx-if-not f . arguments) == (xxx-if (complement f) . arguments)
For example,
(find-if-not #'zerop '(0 0 3)) ==
(find-if (complement #'zerop) '(0 0 3)) => 3
Note that since the xxx-if-not functions and the :test-not
arguments have been deprecated, uses of xxx-if functions or :test
arguments with complement are preferred.
That said, position and position-if-not take function designators, which means that you can pass the symbol delimiterp to them, as we did in
(right-trim-if "hello!" 'delimiterp) ; some delimiters to trim
;=> "hello"
complement, though, doesn't want a function designator (i.e., a symbol or function), it actually wants a function object. So you can define right-trim-if as
(defun right-trim-if (sequence test)
(let ((pos (position-if (complement test) sequence :from-end t)))
(subseq sequence 0 (if (null pos) 0 (1+ pos)))))
but you'll have to call it with the function object, not the symbol:
(right-trim-if "hello!" #'delimiterp)
;=> "hello"
(right-trim-if "hello!" 'delimiterp)
; Error
Splitting a sequence
If you're not just trying to right-trim the sequence, then you can implement a split function without too much trouble. The idea is to increment a "start" pointer into the sequence. It first points to the beginning of the sequence. Then you find the first delimiter and grab the subsequence between them. Then find the the next non-delimiter after that, and treat that as the new start point.
(defun split (sequence test)
(do ((start 0)
(results '()))
((null start) (nreverse results))
(let ((p (position-if test sequence :start start)))
(push (subseq sequence start p) results)
(setf start (if (null p)
nil
(position-if-not test sequence :start p))))))
This works on multiple kinds of sequences, and you don't end up with non delimiters in your subsequences:
CL-USER> (split '(1 2 4 5 7) 'evenp)
((1) (5 7))
CL-USER> (split '(1 2 4 5 7) 'oddp)
(NIL (2 4))
CL-USER> (split "abc123def456" 'alpha-char-p)
("" "123" "456")
CL-USER> (split #(1 2 3 foo 4 5 6 let 7 8 list) 'symbolp)
(#(1 2 3) #(4 5 6) #(7 8))
Although this works for sequences of all types, it's not very efficient for lists, since subseq, position, etc., all have to traverse the list up to the start position. For lists, it's better to use a list specific implementation:
(defun split-list (list test)
(do ((results '()))
((endp list)
(nreverse results))
(let* ((tail (member-if test list))
(head (ldiff list tail)))
(push head results)
(setf list (member-if-not test tail)))))
CL-USER> (split-list '(1 2 4 5 7) 'oddp)
(NIL (2 4))
CL-USER> (split-list '(1 2 4 5 7) 'evenp)
((1) (5 7))
Instead of member-if and ldiff, you could also us cut from this answer to Idiomatic way to group a sorted list of integers?.

Define global from within a function

I need some function which among other stuff would define a new global symbol. So that I could use it like this:
(define (func-prototype symbol value comment)
(define symbol value) ; this should somehow be reformulated
(format "~a=~a !~a\n" symbol value comment))
(define string-list (map func-prototype
'((s1 1 "first value")
(s2 20 "second value")
(s3 300 "third value))))
and be able to get the following results:
> string-list
'("s1=1 !first value\n"
"s2=20 !second value\n"
"s3=300 !third value\n")
> s1
1
> s2
20
> s3
300
Can this be implemented as a function or it is possible to do that only with the help of macros? Could you please suggest any possible implementations or at least give some hints/references that might be helpful?
I'd rethink the general approach, making it simpler. My suggestion: define a global hash table and inside the function add bindings to it, for example:
(define value-map (make-hash))
(define (func-prototype symbol value comment)
(hash-set! value-map symbol value)
(format "~a=~a !~a\n" symbol value comment))
Use it like this:
(define string-list
(map (lambda (lst)
(apply func-prototype lst))
'((s1 1 "first value")
(s2 20 "second value")
(s3 300 "third value"))))
string-list
=> '("s1=1 !first value\n"
"s2=20 !second value\n"
"s3=300 !third value\n")
And wherever you need to refer to one of the symbols in the hash table, do this:
(define (get key)
(hash-ref value-map key))
(get 's1)
=> 1
(get 's2)
=> 20
(get 's3)
=> 300
In general it is not possible to accomplish what you are trying to accomplish in the way you described. Your only hope would be to write stuff out to a file and then load that file into an interactive session. But even then.
In scheme you can't introduce top-level names, such as your desired s1, s2, and s3, except at the top-level. To do so, you could define a macro as:
>(define-syntax define-foo
(syntax-rules ()
((_ name value)
(define name value))))
>(define-foo s1 1)
<undefined>
> s1
1
If you try to use that macro in a function, it is no dice because the body of a function must end with an expression and any definition forms, like what the above macro would expand into, become local variables. That is:
(define (func-prototype name value comment)
(define-foo name value)
name)
>(func-prototype 's1 1 "com")
1
> s1
<error>
One approach that you could take that would work if your string-list is a constant would be as such:
> (define-syntax declare-variables
(syntax-rules ()
((_ (name value comment) ...)
(begin
(define name value)
...))))
> (declare-variables (s1 1 "com") (s2 20 "com") (s3 300 "com"))
> s1
1
This gets it done (I've ignored using 'comment') but, as I said, requires a compile time string-list.
One possibility you might think would work, but wouldn't, would be to use eval as:
(eval '(define s1 1) (environment ...))
but 'eval' only works for expressions, not declarations. Which gets me back to 'load' as a possibility.
First, consider whether you really want to do this, or whether a different solution (like a hash table) would work as well.
You can do this with reflection and dynamic evaluation using the eval procedure.
;; define-variable-with-value! : symbol any -> void
(define (define-variable-with-value! name value)
(eval `(define ,name (quote ,value))))
The quote is important; otherwise you risk reinterpreting a value as an expression to evaluate. To see the difference, consider the example
(define-variable-with-value! 'x (list 'error "kaboom"))

Resources