Scheme - Optional arguments and default values

2019-07-09 23:40发布

问题:

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!

回答1:

In Racket, they 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!


回答2:

@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.

  1. The arguments can be called in either order
  2. The last name can be specified without the first name