Skip to content

Commit

Permalink
Concept Exercise: Lilly's Lasagna Leftovers
Browse files Browse the repository at this point in the history
Also includes a first draft of a new concept: lambda-list
  • Loading branch information
verdammelt authored and TheLostLambda committed Jan 29, 2021
1 parent f78821b commit b1c5a48
Show file tree
Hide file tree
Showing 17 changed files with 445 additions and 9 deletions.
22 changes: 21 additions & 1 deletion concepts/default-parameters/about.md
Original file line number Diff line number Diff line change
@@ -1 +1,21 @@
TODO: add information on default-parameters concept
An optional parameter is designated by the `&optional` lambda list keyword in a lambda list. Optional parameters are not required, can have a default and also can specify a "supplied-p parameter" which will be "true" or "false" depending on whether an argument was provided for the parameter.

```lisp
(defun optional-parameter (&optional (arg -1)) arg)
(optional-parameter) ;; => -1
(optional-parameter 13) ;; => 13
```

Optional parameters must be put after required parameters but before named or rest parameters. Arguments are first bound to required parameters, then optional parameters and finally to rest and named parameters.

```lisp
(defun req-and-opt (req &optional (opt -1)) (list req opt)
(req-and-opt 1) ;; => (1 -1)
(req-and-opt 1 13) ;; => (1 13)
```

While multiple types of parameters can be combined with other types of parameters (optional and keyword arguments) this can be be problematic and should be done carefully. See the section on ("Mixing Different Parameter Types")(pcl-function) in Practical Common Lisp.

--

[pcl-function]: http://www.gigamonkeys.com/book/functions.html
7 changes: 6 additions & 1 deletion concepts/default-parameters/links.json
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
[]
[
{
"url": "http://www.gigamonkeys.com/book/functions.html",
"description": "Practical Common Lisp chapter 5: Functions"
}
]
7 changes: 4 additions & 3 deletions concepts/functions/about.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
In Common Lisp, global, named functions are defined with `defun`.

This form takes as its first argument a list of parameters for the
function being defined. After that it can, optionally, take a string
for use as documentation to that function. Finally there are zero or
more expressions which make up the body of the function.
function being defined (the list of parameters is also called a
'lambda list'). After that it can, optionally, take a string for use
as documentation to that function. Finally there are zero or more
expressions which make up the body of the function.

Calling a function is done by using the name of the function as the
first element of an expression, followed by any parameters to that
Expand Down
11 changes: 11 additions & 0 deletions concepts/lambda-list/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
In Common Lisp a parameter list (such as for defining a function) is also known as a (lambda list)[lambda-list]. Lambda lists are also used for defining macros and destructuring.

Lambda lists can contain (lambda list keywords)[lambda-list-keyword] such as `&rest`, `&optional`, `&key` and others.

While multiple types of parameters can be combined with other types of parameters (optional and keyword arguments) this can be be problematic and should be done carefully. See the section on ("Mixing Different Parameter Types")(pcl-function) in Practical Common Lisp.

--

[lambda-list]: http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_l.htm#lambda_list
[lambda-list-keyword]: http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_l.htm#lambda_list_keyword
[pcl-function]: http://www.gigamonkeys.com/book/functions.html
10 changes: 10 additions & 0 deletions concepts/lambda-list/links.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[
{
"url": "http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_l.htm#lambda_list",
"description": "Definition of Lambda List"
},
{
"url": "http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_l.htm#lambda_list_keyword",
"description": "Definition of Lambda List Keyword"
}
]
23 changes: 22 additions & 1 deletion concepts/named-parameters/about.md
Original file line number Diff line number Diff line change
@@ -1 +1,22 @@
TODO: add information on named-parameters concept
In Common Lisp named parameters are called keyword parameters.

Keyword parameters are designated by the `&key` lambda list keyword in a lambda list. Keyword parameters are not required, can have a default and also can specify a "supplied-p parameter" which will be "true" or "false" depending on whether an argument was provided for the parameter.

```lisp
(defun keyword-parameter (&key (arg -1) arg)
(keyword-parameter) ;; => -1
(keyword-parameter :arg 13) ;; => 13
```

In the arguments to a function the keyword parameters are specified by their "keyword name" which is, by default, a keyword symbol version of the parameter name (_i.e._ keyword parameter `name` has a keyword name of `:name`). It is possible to specify another name for the keyword parameter by using a list of keyword name and parameter name instead of just the parameter name:

```lisp
(defun other-keyword-name (&key ((other-name arg))) (list arg))
(other-keyword-name 'other-name 5) ;; => (5)
```

While multiple types of parameters can be combined with other types of parameters (optional and keyword arguments) this can be be problematic and should be done carefully. See the section on ("Mixing Different Parameter Types")(pcl-function) in Practical Common Lisp.

--

[pcl-function]: http://www.gigamonkeys.com/book/functions.html
7 changes: 6 additions & 1 deletion concepts/named-parameters/links.json
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
[]
[
{
"url": "http://www.gigamonkeys.com/book/functions.html",
"description": "Practical Common Lisp chapter 5: Functions"
}
]
8 changes: 7 additions & 1 deletion concepts/rest-parameters/about.md
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
TODO: add information on rest-parameters concept
A rest parameter is designated by the `&rest` lambda list keyword in a lambda list. This parameter will be bound to a value which is a list of all the arguments provided by the caller which is not consumed by other parameters.

While multiple types of parameters can be combined with other types of parameters (optional and keyword arguments) this can be be problematic and should be done carefully. See the section on ("Mixing Different Parameter Types")(pcl-function) in Practical Common Lisp.

--

[pcl-function]: http://www.gigamonkeys.com/book/functions.html
7 changes: 6 additions & 1 deletion concepts/rest-parameters/links.json
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
[]
[
{
"url": "http://www.gigamonkeys.com/book/functions.html",
"description": "Practical Common Lisp chapter 5: Functions"
}
]
28 changes: 28 additions & 0 deletions exercises/concept/lillys-lasagna-leftovers/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
## 1. Make `preparation-time-in-minutes` easier to use

- A "rest parameter" will collect all arguments not consumed by other parameters into a list.
- A "rest parameter" is designated in the lambda list with the `&rest` lambda list keyword.
- The function `length` can be used to get the number of items in a list.

## 2. Allow changing the expected oven time

- An optional parameter can be provided or not.
- An optional parameter can have a default value.
- Optional parameters are designated by the `&optional` lambda list keyword.

## 3. Lilly remembers another preferred cooking style

- A "supplied-p parameter" can be specified with the optional parameter to determine if the caller has provided the parameter.

## 4. Splitting the leftovers

- Keyword parameters are named and are optional.
- Keyword parameters are designated with the `&key` lambda list keyword.

## 5. Making assumptions

- Keyword parameters can have default values.

## 6. Standard amount of leftovers

- Keyword parameters can have a "supplied-p parameter" like optional parameters.
110 changes: 110 additions & 0 deletions exercises/concept/lillys-lasagna-leftovers/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
Lilly the Lisp Alien has definitely made a lot of Lasagna with your
help. They want to thank you by giving you some leftovers. But first
they have some feedback about one of the functions you helped them
with.

In this exercise you are going to write some more functions to help
Lilly with their Lasagna cooking as well as helping them split the
leftovers with you.

You have six tasks, three about improving the existing Lasagna cooking
functions and three about splitting the leftovers.

## 1. Make `preparation-time-in-minutes` easier to use

Lilly says they found using your `preparation-time-in-minutes` a
little awkward to use since they had to count all the layers before
using the functions. Lilly wonders if you could make a version of the
function that takes the layers (any number of them) as arguments and
then calculate the preparation time based upon how many layers there
were? As a reminder, it takes 19 minutes to prepare a single layer.

```lisp
(preparation-time 'sauce 'cheese 'right-handed-macaroni 'cheese 'sauce
'left-handed-macaroni 'sauce 'sauce 'cheese 'cheese)
;; => 190 (because there are 10 layers.)
```

## 2. Allow changing the expected oven time

While Lilly's parental-units always cooked their lasagna for 337
minutes, Lilly does admit that other Lisp Aliens like to cook their
lasagna for longer or shorter amounts of time.

So it would be good if `remaining-minutes-in-oven` would not use
`expected-minutes-in-oven` but instead could take an _optional_
parameter to define if the time should be longer, very long, shorter
or very short. Longer or shorter means add or subtract 100 from the
default time of 337. Very long or very short means add or
subtract 200. If no parameter is provided, or the argument is 'normal'
then the default value of 337 should be returned.

```lisp
(remaining-minutes-in-oven) ;; => 337
(remaining-minutes-in-oven :normal) ;; => 337
(remaining-minutes-in-oven :shorter) ;; => 237
(remaining-minutes-in-oven :longer) ;; => 437
(remaining-minutes-in-oven :very-long) ;; => 537
```

## 3. Lilly remembers another preferred cooking style

Just after you finished that change Lilly remembers that there are
some Lisp Aliens that like _really short_ cooking times. Effectively
they want it raw. So now the function should return 0 if `NIL` was the
argument as well as the same values as in the previous task.

```lisp
(remaining-minutes-in-oven) ;; => 337
(remaining-minutes-in-oven nil) ;; => 0
```

## 4. Splitting the leftovers

Now that you've helped improve the lasagna cooking functions there is
_a lot of lasanga_! Lilly wants to repay you for all your help by
splitting the leftovers with you. You will need to write a function
that takes three parameters: the total amount of leftovers, the number
of containers for leftovers Lilly found in their cupboards and the
same for you. Because searching for leftover containers may take
different amounts of time, and the exact amount of leftovers may be
determined while yourself and Lilly are out searching your cupboards
you both agree that the function should take the arguments in any
order, so as each number is determined you can write it down. When you
have all three numbers you can just close the parenthesis on the
function call and evaluate it.

The function should return the amount of leftovers which are leftover,
that is, the difference between the weight of left-overs and the total
number of containers that you and Lilly found.

```lisp
(split-leftovers :weight 20 :human 10 :alien 5) ;; => 5
(split-leftovers :weight 20 :alien 10 :human 2) ;; => 8
(split-leftovers :alien 12 :weight 20 :human 4) ;; => 4
```

## 5. Making assumptions

After a few trips back and forth to your respective cupboards, you
both agree that it would be great if the function could just _assume_
that you or Lilly had... say 10 containers if it were not specified.

```lisp
(split-leftovers :weight 20 :human 5) ;; => 5
(split-leftovers :weight 20 :alien 5) ;; => 5
(split-leftovers :weight 20) ;; => 0
```

## 6. Standard amount of leftovers

Even better the function could _assume_ that there is enough leftovers
to fit into the containers. So if the weight is not provided the
function should return `'just-split-it`. _However_ if the weight is
provided and it is `nil` or `0` then it should return
`'looks-like-someone-was-hungry`.

```lisp
(split-left-overs :human 5 :alien 5) ;; => :JUST-SPLIT-IT
(split-left-overs :weight NIL :human 5 :alien 5) ;; => :LOOKS-LIKE-SOMEONE-WAS-HUNGRY
```
53 changes: 53 additions & 0 deletions exercises/concept/lillys-lasagna-leftovers/.docs/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
In Common Lisp a functions argument list (also known as a ('lambda list')[lambda-list]) can have arguments of different types. These different types are designated with the use of ("lambda list keywords")[lambda-list-keyword] which all begin with `&`. The most commonly used types are optional, keyword and rest arguments types. Every parameter in the lambda list after a particular lambda list keyword is will be of that type. A lambda list keyword can only be used once in a lambda list.

## default-parameters

In Common Lisp a function can have some arguments are are optional. These are designated in the lambda list by `&optional` lambda list keyword. A parameter will be bound to the value `nil` if it is not specified. If there are several optional parameters they are bound in order. Default values can be specified for optional parameters. Finally a symbol an be specified for each optional parameter which will be bound to true or false depending on whether that parameter was supplied by the caller of the function (this is referred to as the "supplied-p parameter").

```lisp
(defun default-parameters (&optional x (y 'default) (z nil z-supplied-p))
(list x y (if z-supplied-p (list :z-was-supplied z)
(list :z-was-not-supplied z))))
(default-parameters) ;; => (NIL DEFAULT (:Z-WAS-NOT-SUPPLIED NIL))
(default-parameters 5 nil 10) ;; => (5 NIL (:Z-WAS-SUPPLIED 10))
```

## named-parameters

In Common Lisp a function can have named parameters (referred to as "keyword parameters" or "keyword arguments"). These are designated in the lambda list by the `&key` lambda list keyword. Keyword parameters are not required parameters. Like optional parameters they can be given default values and symbols to bind to their 'supplied-or-not' state.

When calling a function with keyword parameters the name of the parameter as a keyword is used in front of the parameter value. Keyword parameters can be specified by the caller of the function in any order.

```lisp
(defun keyword-parameters (&key x (y 'default) (z nil z-supplied-p))
(list x y (if z-supplied-p (list :z-was-supplied z)
(list :z-was-not-supplied z))))
(keyword-parameters) ;; => (NIL DEFAULT (:Z-WAS-NOT-SUPPLIED NIL))
(keyword-parameters :y 5) ;; => (NIL 5 (:Z-WAS-NOT-SUPPLIED NIL))
(keyword-parameters :z 10 :x 5) ;; => (5 NIL (:Z-WAS-SUPPLIED 10))
```

Care should be taken when combining optional and keyword arguments as the keyword name and argument could be consumed by optional parameters:

```lisp
(defun could-be-confusing (&optional x y &key z) (list x y z))
(could-be-confusing :z 'huh?) ;; => (:Z HUH? NIL)
```

## rest-parameters

In Common Lisp a function can have a parameter that will contain the "rest" of the arguments after any required or optional parameters are processed. This parameter is designated by the `&rest` lambda list keyword. If all arguments to a function are used by by other types of parameters then the rest parameter will be bound to an empty list. If there are unused arguments then the rest parameter will be bound to a list of those arguments.

```lisp
(defun rest-of-it (req &optional opt &rest rest) (list req opt rest))
(rest-of-it 1) ;; => (1 NIL NIL)
(rest-of-it 1 2) ;; => (1 2 NIL)
(rest-of-it 1 2 3) ;; => (1 2 (3))
(rest-of-it 1 2 3 4 5) ;; => (1 2 (3 4 5))
```

--
[lambda-list]: http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_l.htm#lambda_list
[lambda-list-keyword]: http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_l.htm#lambda_list_keyword
14 changes: 14 additions & 0 deletions exercises/concept/lillys-lasagna-leftovers/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"authors": [
{
"github_username": "verdammelt",
"exercism_username": "verdammelt"
}
],
"contributors": [],
"forked_from": ["swift/lasagna-master"],
"editor": {
"solution_files": ["lillys-lasagna-leftovers.lisp"],
"test_files": ["lillys-lasagna-leftovers-test.lisp"]
}
}
52 changes: 52 additions & 0 deletions exercises/concept/lillys-lasagna-leftovers/.meta/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
## Goal

This exercise should focus on teaching about the special types of arguments to `defun`: `&key`, `&rest`, `&optional`.

## Learning objectives

- Know how to take optional arguments using `&optional`
- Know how to use `&rest` to take an arbitrary number of arguments
- Know how to take keyword arguments using `&key`
- Know how to set default values for optional / keyword arguments
- Be able to use `supplied-p` parameters with optional / keyword
arguments

## Out of scope

- Anonymous functions using `lambda`
- Local functions using `flet`, `labels`, etc.
- Recursion / self-referential functions
- The somewhat arcane `&aux` keyword
- The `&allow-other-keys` keyword
- Multiple value returns with `values`
- Early returns with `return-from`
- Methods / generic functions
- Higher-order functions
- Macros

The concept file for `named-parameters` can (and perhaps should) mention `&allow-other-keys`.

## Concepts

- `named-parameters`
- `rest-parameters`
- `default-parameters`

## Prerequisites

- `functions`
- `expressions`
- `integers`
- `arithmetic`
- `conditionals`

## Resources to refer to

- [Practical Common
Lisp](http://www.gigamonkeys.com/book/functions.html)
- [Common Lisp Hyperspec](http://clhs.lisp.se/Body/m_defun.htm)

### Hints

- [Common Lisp
Cookbook](https://lispcookbook.github.io/cl-cookbook/functions.html)
Loading

0 comments on commit b1c5a48

Please sign in to comment.