Scheme - Optional arguments and default values - arguments

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

Related

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

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

let: bad syntax (not an identifier and expression for a binding) in: wordslist ###scheme

(define (most-common-word str)
(let (wordslist str-split str " ")))
I am trying to do a inner variable of lists of strings.
but I get the error "bad syntax".
I looked for answers here but the things I changed, didn't help.
str-split returns a list of strings with " " the separator.
thanks.
It should look like:
(let ([word-list <VALUE>]) <BODY>)
... which establishes a local binding from word-list to the value <VALUE>. This binding is effective only inside the <BODY> form enclosed by the let.
Now, in order to compute <VALUE>, you have to call str-split with the arguments you want (i.e. str and " "). The way you perform a function call is to wrap it in parenthesis (this is valid only in a context where the form is evaluated as an expression, not where parenthesis mean binding, for example). So <VALUE> should really be:
(str-split str " ")

string-set! not working in Racket

Following code is giving error:
(define s "test")
(string-set! s 0 #\T)
The error is:
string-set!: contract violation
expected: (and/c string? (not/c immutable?))
given: "test"
argument position: 1st
other arguments...:
How can I change a character of 's' using string-set! function? I think it has to be made "mutable". How can that be done?
That's because a string defined using the default reader is immutable; it's literally the first thing stated in the documentation. For example, to turn it into a mutable string:
(define s (string-append "test"))
(string-set! s 0 #\T)
It works as expected:
s
=> "Test"

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