car/cdr arguments are lists... right? [duplicate] - scheme

This question already has answers here:
What is the difference between quote and list?
(2 answers)
Closed 4 years ago.
I made a list of lists of strings called chatbot in Scheme.
Every list element in chatbot have strings. I'm trying to classify these strings using different lists, and these lists are all stored in a big list called chatbot. (sorry for redundance)
To make it clear, here is the code doing this:
(define greetings '("string 1"
"string 2"
"string 3"
"string 4"))
(define cheerUpPhrases '("string 5"
"string 6"))
(define congratsPhrases '("string 7"
"string 8))
(define didNotUnderstand '("string 8"
"string 9"
"string 10"))
(define chatbot '(greetings cheerUpPhrases congratsPhrases didNotUnderstand))
I really think this is okay. But later, in a function, I wanted to get "string 3" so I tried to do this:
(caddar chatbot)
and then got this error:
caddar: contract violation
expected: (cons/c (cons/c any/c (cons/c any/c pair?)) any/c)
given: '(greetings cheerUpPhrases congratsPhrases didNotUnderstand)
Not very sure of what that meant, I changed (caddar chatbot) into:
(third (car chatbot))
Finally, I got this error:
third: contract violation
expected: list?
given: 'greetings
Now, I understand (third) needs a list (actually pair) to work; and so are car/cdr and similar functions. Am I not giving it a list after all? I'm really confused now.
I'm just starting with Scheme and the functional paradigm, so I may be missing a basic thing. It would really help me if you could explain me what's going on.
Thanks in advance.

Close. the problem is your misuse of '.
You see, '(a b c) doesn't expand to something like (list a b c), but rather, it expands to something like (list 'a 'b 'c).
So, instead of inserting the lists greetings, cheerUpPhrases, congratsPhrases, and didNotUnderstand, you actually inserted them literally as symbols.
There are two easy ways to get around this. Either you can use quasiquote ` and unquote ,, or you can just use list directly. Rewriting your code as using list (and fixing a your broken "string 8" gives:
(define greetings '("string 1"
"string 2"
"string 3"
"string 4"))
(define cheerUpPhrases '("string 5"
"string 6"))
(define congratsPhrases '("string 7"
"string 8"))
(define didNotUnderstand '("string 8"
"string 9"
"string 10"))
(define chatbot (list greetings cheerUpPhrases congratsPhrases didNotUnderstand))
You can now see that the chatbot variable contains the actual list you were expecting:
> chatbot
'(("string 1" "string 2" "string 3" "string 4") ("string 5" "string 6") ("string 7" "string 8") ("string 8" "string 9" "string 10"))

Related

Declare a string in elisp and then inserting it in emacs lisp

I want to create i string and then insert it, but i get the error wrong argument char-or-stringp. What does it mean?
(defun test ()
(string "1 2 3")
)
(insert (test))
Your string call is incorrect. "1 2 3" is already a string. You seem to be looking for simply
(insert "1 2 3")
or if you want to demonstrate a function call,
(defun test ()
"1 2 3")
(insert (test))
The string form expects a sequence of characters, like this:
(string ?1 ? ?2 ? ?3)
=> "1 2 3"

Use variable padding in iteration directive in FORMAT

Is there a way to do something like the following?
(format t "~{~va~}" '("aa" "bb" "cc") 4)
I need to iterate through a list. Each element of that list should be padded with a variable number of spaces (specified at runtime, so I cannot use "~4a").
Or more generally, is there a way to refer to a specific argument in the argument list of FORMAT?
By nesting format function, you can do what you want.
(format t (format nil "~~{~~~Aa~~}" 4) '("aa" "bb" "cc"))
;; returns: aa bb cc
Here the inner format directive:
The nil as first argument, format returns a string.
(format nil "~~{~~~Aa~~}" 4)
;; returns: "~{~4a~}" - and this is exactly what you want to give
;; to the outer `format` as second argument!
You can of course write a function for this:
(defun format-by-padding-over (lst padding)
(format t (format nil "~~{~~~Aa~~}" padding) lst))
And then:
(format-by-padding-over '("aa" "bb" "cc") 4)
;; aa bb cc
;; NIL
I learned this trick here from #Sylwester (many thanks!).
You could also interleave the list with repetitions of the padding:
(format t "~{~va~}"
(mapcan (lambda (element)
(list 4 element))
list))
You can build the format control string using nested format functions, but then you have to take care about escaping tildes. When working with regular expressions (using CL-PPCRE), one can define regular expressions using trees, like (:alternation #\\ #\*), which helps preventing bugs and headaches related to escaping special characters. The same can be done with format strings, using format-string-builder, available in Quicklisp:
(lambda (v)
(make-format-string `((:map () (:str ,v)))))
Returns a closure, which can be used to build format strings:
(funcall * 10)
=> "~{~10a~}"

Scheme - Optional arguments and default values

I'm currently looking into Scheme, and the way I have understood it, procedures can take an arbitrary number of arguments.
I have been trying to play around with this, but I'm struggling to grasp the concept.
For instance, say I want to write a welcome message, based on information provided by the user.
If user provides a first and last name, the program shout write:
Welcome, <FIRST> <LAST>!
;; <FIRST> = "Julius", <LAST>= "Caesar"
Welcome, Julius Caesar!
Otherwise, the program should refer to a default value, specified as:
Welcome, Anonymous Person!
I have the following outline for my code, but struggling with how to finalise this.
(define (welcome . args)
(let (('first <user_first>/"Anonymous")
('last <user_last>/"Person"))
(display (string-append "Welcome, " first " " last "!"))))
Example usage:
(welcome) ;;no arguments
--> Welcome, Anonymous Person!
(welcome 'first "John") ;;one argument
--> Welcome, John Person!
(welcome 'first "John" 'last "Doe") ;;two arguments
--> Welcome, John Doe!
Any help is highly appreciated!
In Racket, the way to do this would be using keyword arguments. You can define a function with keyword arguments my writing #:keyword argument-id when declaring the arguments:
(define (welcome #:first first-name #:last last-name)
(display (string-append "Welcome, " first-name " " last-name "!")))
Which you can call like this:
> (welcome #:first "John" #:last "Doe")
Welcome, John Doe!
However, what you want is to make them optional. To do that, you can write #:keyword [argument-id default-value] in the argument declaration.
(define (welcome #:first [first-name "Anonymous"] #:last [last-name "Person"])
(display (string-append "Welcome, " first-name " " last-name "!")))
So that if you don't use that keyword in a certain function call, it is filled with the default value.
> (welcome)
Welcome, Anonymous Person!
> (welcome #:first "John")
Welcome, John Person!
> (welcome #:first "John" #:last "Doe")
Welcome, John Doe!
> (welcome #:last "Doe" #:first "John")
Welcome, John Doe!
#Alex Knauth's answer is great. That's something I didn't know about.
Here's an alternative, though it's not quite as flexible
(define (welcome (first "Anonymous") (last "Person"))
(displayln (string-append "Welcome, " first " " last "!")))
This works pretty nicely with your basic requirements
> (welcome)
Welcome, Anonymous Person!
> (welcome "John")
Welcome, John Person!
> (welcome "John" "Doe")
Welcome, John Doe!
However, Alex's solution has two distinct advantages.
The arguments can be called in either order
The last name can be specified without the first name

how do i write a string to a file without quote marks in scheme

I'm trying to write a string to a file, but every time i do it has quotes around it.
I've tried
(call-with-output-file file-path
(lambda(output-port)(write "some text" output-port)))
and
(let ((p (open-output-file file-path)))
(write "some text" p)
(close-output-port p))
but in both cases i expected "some text" but got "\"some text\""
I'm currently working in chicken-scheme but I don't think that matters.
write is for serializing S-expressions to a file. It is the opposite of read, which will read a serialized S-expression back into lists, symbols, strings and so on. That means write will output everything like it would occur in source code.
If you just want to output a string to a port, use display:
(call-with-output-file file-path
(lambda(output-port)
(display "some text" output-port)))
Or in CHICKEN, you can use printf or fprintf:
(call-with-output-file file-path
(lambda(output-port)
(fprintf output-port
"Printing as s-expression: ~S, as plain string: ~A"
"some text"
"some other test")))
This will print the following to the file:
Printing as s-expression: "some text", as plain string: some other text

How to compare partial string matches in racket?

I was wondering how one would compare strings for a partial match. For example,
to see if the phrase "example" is found in a sentence, say "this is an example"?
Use string-contains from SRFI 13:
> (require srfi/13)
> (string-contains "this is an example" "example")
11
> (string-contains "this is an example" "hexample")
#f
In Racket the general mechanism for this are regular expressions:
(regexp-match "example" "this is an example")
=> '("example")
(regexp-match-positions "example" "this is an example")
=> '((11 . 18))
Regular expressions are a slightly complex but very powerful way of processing strings. You can specify if the search string needs to be a separate word, search for repetitive patterns or character classes. See the excellent Racket doc for this.

Resources