common lisp: looking for a library which works like (str:string-case BUT with regular expessions - switch-statement

I am trying to rewrite an AWK script in Common Lisp (as a kind of learning exercise)
I need something like:
(str:string-case field
("FODL.*" (do something)))
(try to match the regex "FODL.*" for a field)

depending on the translated script complexity, you can use something as simple as cond + ppcre, or something more sophisticated, like trivia pattern matching, which also has ppcre based patterns contrib:
(ql:quickload :cl-ppcre)
(ql:quickload :trivia)
(ql:quickload :trivia.ppcre)
(use-package '(:trivia :trivia.ppcre))
(match "other"
((ppcre "som[e|a].") :some)
((ppcre "^oth") :other))
;;=> :OTHER
(match "some"
((ppcre "som[e|a].") :some)
((ppcre "^oth") :other))
;;=> NIL
(match "something"
((ppcre "som[e|a].") :some)
((ppcre "^oth") :other))
;;=> :SOME

Related

Switch statement in Lisp

Switch statement with Strings in Lisp.
(defun switch(value)
(case value
(("XY") (print "XY"))
(("AB") (print "AB"))
)
)
I want to compare if value is "XY" then print "XY" or same for "AB".
I have tried this code but it gives me nil. Can some please tell me what i am doing wrong?
You can use the library alexandria, which has a configurable switch macro:
(switch ("XY" :test 'equal)
("XY" "an X and a Y")
("AB" "an A and a B"))
print("XY") looks more like Algol (and all of its descendants) rather than LISP. To apply print one would surround the operator and arguments in parentheses like (print "XY")
case happens to be a macro and you can test the result yourself with passing the quoted code to macroexpand and in my implementation I get:
(let ((value value))
(cond ((eql value '"XY") (print "XY"))
((eql value '"AB") (print "AB"))))
You should know that eql is only good for primiitive data types and numbers. Strings are sequences and thus (eql "XY" "XY") ;==> nil
Perhaps you should use something else than case. eg. use cond or if with equal.
The Hyperspec on CASE says:
These macros allow the conditional execution of a body of forms in a clause that is selected by matching the test-key on the basis of its identity.
And strings are not identical in CL, i.e. (EQ "AB" "AB") => NIL.
That is why CASE wouldn't work for strings. You either need to use symbols (they are interned once only, thus guaranteeing identity) or use COND with EQUAL or even EQUALP if the letters case to be ignored.

Is there a single function that concat a string to every entry in a list?

(setq a '("bar" "baz" "barz"))
(setq prefix "foo")
;; Expected result
==> ("foobar" "foobaz" "foobarz")
I have been doing this by dolist or iterating through car. Is there a single function doing the same job?
Not a single function, but I would use:
(mapcar (apply-partially #'concat prefix) a)
There are lots of ways you could do this, though, and there's probably nothing particularly wrong with what you were doing.

Strange comparison of `'quote` and `'lambda` in Scheme (guile)

Let see my code
Code1:
(eqv? 'lambda 'quote)
return #f
Code2:
(case 'lambda ('quote "equal") (else "not equal"))
return "not equal" but generate a warning ;;; <stdin>:17:0: warning: duplicate datum quote in clause ((quote quote) "equal") of case expression (case (quote lambda) ((quote quote) "equal") (else "not equal"))
Code3: strange result
(case 'quote ('lambda "equal"))
return "equal" and without warning
I interpreted code using guile (GNU Guile) 2.0.11. And here is the description of case syntax from gnu
The the result of this evaluation is compared against all datums using eqv?
'<something> is an abbreviation for the list (quote <something>). In Scheme, the case form should be a list (in some Lisps, e.g., Common Lisp, it can also be a single non-list element, representing the list of just that element), so that you can do, e.g.:
(case n
((0) 'zero) ; in Common Lisp, could also be (0 'zero)
((1 2 3) 'one-two-or-three)
((4 5) 'four-or-five))
When you do:
(case some-symbol
('quote 'it-was-quote))
You're doing
(case some-symbol
((quote quote) 'it-was-quote))
So you're providing a list, and it's got a duplicate element. There's nothing wrong with that, but it's usually unexpected, and so you get a warning.
By that reasoning
(case 'quote ('lambda "equal"))
is the same as
(case 'quote ((quote lambda) "equal"))
and, since the symbol quote is in the list (quote lambda), the case matches, and you get "equal". The moral of the story is: "don't quote the cases in case." That is, you should be doing:
(case 'quote
((lambda) "equal"))

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"))

Importing strings to Scheme without using open-input-string

I am trying to have my Scheme program import strings without needing to use open-input-string before the string. So for example, right now I can do the following:
> (scheme_lexer (open-input-string "3+4*2"))
However, is there a way for my program to work if I input the string this way?:
> (scheme_lexer ("3+4*2"))
Thank you!
Is there any particular reason you can't just make a scheme_lexer_string function that does this for you when dealing with strings? The extra parentheses just seem like clutter, and they make a macro the only real solution. If you dropped that requirement and made something like (scheme_lexer "3+4*2") acceptable, you can make an ordinary function for handling strings:
(define (scheme_lexer_string s)
(scheme_lexer (open-input-string s)))
If what you want is a function that handles both input ports and strings, you can make a general function that dispatches based on the type of the argument to the specific functions. In this case, your original scheme_lexer would be renamed to scheme_lexer_input_port and you would have these functions:
(define (scheme_lexer_string s)
(scheme_lexer_input_port (open-input-string s)))
(define (scheme_lexer in)
(if (string? in)
(scheme_lexer_string in)
(scheme_lexer_input_port in)))
Now scheme_lexer works for both strings and ports and dispatches to the correct function as desired.
> (scheme_lexer some-input-port)
... evaluates the content in the port
> (scheme_lexer "abcd")
... evaluates the string "abcd"
Here is one option. I have used a testing function lexer just to show the macro. You can adjust it to your needs.
(define (lexer sp) (read sp))
(define-syntax scheme_lexer
(syntax-rules ()
((_ (input))
(lexer (open-input-string input)))))
And to test:
> (scheme_lexer ("3+4*2"))
'3+4*2

Resources