Skip to content

Commit c07edad

Browse files
authored
Create format-basics concept & concept exercise. (#537)
* Add format-basic concept. * Add reporting-for-duty concept exercise. This will teach the format-basics concept. * Update some practice exercises to require and practice format-basics. * Add icon for exercise.
1 parent 81d1100 commit c07edad

File tree

13 files changed

+363
-7
lines changed

13 files changed

+363
-7
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"blurb": "Basic information about formatting output.",
3+
"authors": ["verdammelt"]
4+
}

concepts/format-basics/about.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# About
2+
3+
The Common Lisp Printer is the facility for outputting data to streams. One common function in this facility is `format`.
4+
5+
`format` takes two required parameters: a stream to write to and a control string. The contents of the control string determine how any further arguments are used.
6+
7+
The first parameter has two special values: `t` and `nil`. `t` indicates that the stream bound to `*standard-output*` should be used and `nil` indicates that a string should be created and returned. `format` evaluates to `nil` unless the stream parameter is `nil`.
8+
9+
The control string can contain format directives which are signified by a `~` followed by a character (the case of the character is not important). The format directive will define if any arguments are consumed and how they are formatted to the stream.
10+
11+
The important basic format directives are:
12+
13+
* `~A` - _aesthetically_ format in a "human readable" way.
14+
* `~S` - format in the _standard_ to be "machine readable".
15+
* `~%` - emit a newline
16+
* `~&` - emit a newline unless already at the beginning of a line.
17+
* `~~` - emit a tilde.
18+
19+
Examples:
20+
21+
```lisp
22+
(format nil "The list is: ~A~%" (list 1 2 3)) ; => "The list is: (1 2 3)
23+
"
24+
(format nil "(format t ~S)" "hello world") ; => "(format t \"hello world\""
25+
(format nil "~&fresh~&lines") ;=> "fresh
26+
lines"
27+
(format nil "directives start with a ~~") ; => "directives start with a ~"
28+
```
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Introduction
2+
3+
Common Lisp has a concept of The Printer which includes all functions for writing textural representations of Lisp data. One such function is `format` which allows the programmer to format the data as they want.
4+
5+
The abilities of `format` and the Lisp Printer in general is a large topic; here the basics of the use of `format` will be described.
6+
7+
## The FORMAT function
8+
9+
The function `format` takes at least two arguments: a stream to print to and control string defining what and how is to be printed.
10+
11+
Streams are a concept of their own and will be described elsewhere. But for now all one needs to know is that the printing is done to the stream and there are two special values: `t` and `nil` for this argument. `t` means to print to standard output (actually the stream to which the variable `*standard-output*` is bound). and `nil` means to create and string and return it.
12+
13+
Note that `format` will always evaluate to `nil` except in that latter case where `nil` is specified as the stream. This is a common way to do simply string interpolation or construction.
14+
15+
```lisp
16+
(format nil "hello world") ; => "hello world"
17+
(format t "hello world") ; => NIL (but "hello world" is printed to standard output)
18+
```
19+
20+
## Format Directives
21+
22+
The control string can contain "format directives" which give instructions to `format` about what to print and how to interpret any other parameters to `format`. In a sense the control string is a program which `format` will interpret and run. All directives start with `~` and are followed by a character (_e.g._ `~A`, `~D`, `~&`). The case of the character does not matter.
23+
24+
Most format directives simply interpolate an argument to `format` into the output. This is said to "consume" the argument. Some directives do not consume arguments, or consume more than one. One directive even allows you to jump around in the argument list or skip arguments.
25+
26+
## Basic formatting
27+
28+
A very general purpose format directive is `~A`. This will print a 'human readable' version of the data.
29+
30+
```lisp
31+
(format nil "Value = ~a" 10) ; => "10"
32+
(format nil "hello ~a" "world") ; => "hello world"
33+
(format nil "The list is: ~A" (list 1 2 3)) ; => "The list is: (1 2 3)"
34+
```
35+
36+
Another useful format directive is `~S`. This will print a 'machine readable' version of the data. This can be very useful if you want to be able to interpret the output created as Lisp data. For many things it will look the same as `~A` but note that for strings for example the output will include the quotation marks (escaped when included in another string) so that the value could be read back in as the string parameter.
37+
38+
```lisp
39+
(format nil "~s" 10) ; => "10"
40+
(format nil "~s" "hello world") ; => "\"hello world\""
41+
(format nil "~s" (list 1 2 3)) ; => "(1 2 3)"
42+
```
43+
44+
Two more very useful directives are `~%` and `~&`. The first outputs a newline character while the latter outputs a "fresh line". The difference is that `~%` always outputs a newline while `~&` will output one if not currently at the beginning of a line. Neither of these consume an argument.
45+
46+
```lisp
47+
(format nil "~%new~%lines") ; => "
48+
new
49+
lines"
50+
51+
(format nil "~&new~&lines" ; => "new
52+
lines"
53+
```
54+
55+
Finally `~~` is a simple directive that will output a literal `~`.
56+

concepts/format-basics/links.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[
2+
{
3+
"url": "https://gigamonkeys.com/book/a-few-format-recipes.html#basic-formatting",
4+
"description": "Information about basic format directives."
5+
},
6+
{
7+
"url": "https://gigamonkeys.com/book/a-few-format-recipes.html#the-format-function",
8+
"description": "Description of the format function."
9+
},
10+
{
11+
"url": "https://gigamonkeys.com/book/a-few-format-recipes.html#format-directives",
12+
"description": "Overview of format directives."
13+
},
14+
{
15+
"url": "http://www.lispworks.com/documentation/HyperSpec/Body/22_c.htm",
16+
"description": "Hyperspec chapter on formatting output."
17+
}
18+
]

config.json

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,14 @@
154154
"keyword-parameters"
155155
],
156156
"status": "beta"
157+
},
158+
{
159+
"slug": "reporting-for-duty",
160+
"name": "Reporting for Duty",
161+
"uuid": "c72ffe7d-eda5-41b0-b1ae-10c9db1550fa",
162+
"concepts": ["format-basics"],
163+
"prerequisites": ["strings", "functions"],
164+
"status": "beta"
157165
}
158166
],
159167
"practice": [
@@ -170,12 +178,17 @@
170178
"slug": "two-fer",
171179
"name": "Two Fer",
172180
"uuid": "b80ab7d9-6152-4351-b405-07c2fb071962",
173-
"practices": ["optional-parameters", "conditionals", "functions"],
181+
"practices": [
182+
"optional-parameters",
183+
"conditionals",
184+
"functions",
185+
"format-basics"
186+
],
174187
"prerequisites": [
175-
"expressions",
176188
"functions",
177189
"optional-parameters",
178-
"conditionals"
190+
"conditionals",
191+
"format-basics"
179192
],
180193
"difficulty": 1,
181194
"status": "beta"
@@ -248,17 +261,17 @@
248261
"name": "Bob",
249262
"uuid": "1e6c8365-775d-4a4f-8fc7-e376912d7912",
250263
"difficulty": 1,
251-
"practices": ["conditionals", "strings"],
252-
"prerequisites": ["conditionals", "strings"],
264+
"practices": ["conditionals", "strings", "format-basics"],
265+
"prerequisites": ["conditionals", "strings", "format-basics"],
253266
"status": "beta"
254267
},
255268
{
256269
"slug": "twelve-days",
257270
"name": "Twelve Days",
258271
"uuid": "40ce6656-b8f5-4156-94b6-d634876215b2",
259272
"difficulty": 4,
260-
"practices": ["strings"],
261-
"prerequisites": ["strings"],
273+
"practices": ["format-basics"],
274+
"prerequisites": ["format-basics"],
262275
"status": "beta"
263276
},
264277
{
@@ -608,6 +621,11 @@
608621
"slug": "floating-point-numbers",
609622
"name": "Floating Point Numbers"
610623
},
624+
{
625+
"uuid": "b4d57dc7-761d-4a53-818f-21aaa19e8db8",
626+
"slug": "format-basics",
627+
"name": "Format - Basics"
628+
},
611629
{
612630
"uuid": "a7b463d3-4d7f-4b4a-8a47-28307219541d",
613631
"slug": "functions",
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Hints
2+
3+
- The `format` function will be needed for this exercise.
4+
5+
## 1. Formatting quarterly values
6+
7+
- `format` always returns `nil` unless the stream parameter is `nl`.
8+
- `~A` can be used to format a value.
9+
10+
## 2. The Two-Quarter report
11+
12+
- Your previous function is perfect to create the two lines needed.
13+
- `~%` and `~&` can be used to create newlines.
14+
15+
## 3. Human vs. Lisp Alien readable
16+
17+
- Your previous quarter formatting function is perfect to create the needed strings.
18+
- `~S` can be used to format values in a readable form.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Instructions
2+
3+
Like just about everyone with a job Layla the Lisp Alien sometimes needs to make reports to let others know information. They ask if you could help them format data she has for these reports.
4+
5+
## 1. Formatting quarterly values
6+
7+
The first thing Layla needs is a function: `format-quarter-value` that takes two parameters: the calendar quarter and the value and formats it for a report. It should evaluate to a string.
8+
9+
```lisp
10+
(format-quarter-value "last" 3.14) ; => "The value last quarter: 3.14"
11+
(format-quarter-value "this" 0) ; => "The value this quarter: 0"
12+
```
13+
14+
## 2. The Two-Quarter report
15+
16+
Layla thanks you for the `format-quarter-value` function and says it will be very useful in the *next* function that is needed.
17+
18+
The next report needs to show the quarter and value from 2 quarters printed on two lines.
19+
20+
Also the output needs to go to the stream provided as the first argument.
21+
22+
For example: `(format-two-quarters t "last" 3.14 "this" 0)` will produce the following on standard output:
23+
24+
```
25+
26+
The value last quarter: 3.14
27+
The value this quarter: 0
28+
29+
```
30+
31+
Note the blank lines around it.
32+
33+
## 3. Human vs. Lisp Alien readable
34+
35+
Layla forgot to tell you that Lisp Aliens like to see values in a way that is readable as Lisp data. So they need a new function that could be read as lisp data (again to the specified stream):
36+
37+
```lisp
38+
(format-two-quarters-for-reading nil "last" 3.14 "this" 0) ; =>
39+
40+
"(\"The value last quarter: 3.14\" \"The value this quarter: 0\")"
41+
```
42+
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Introduction
2+
3+
Common Lisp has a concept of The Printer which includes all functions for writing textural representations of Lisp data. One such function is `format` which allows the programmer to format the data as they want.
4+
5+
The abilities of `format` and the Lisp Printer in general is a large topic; here the basics of the use of `format` will be described.
6+
7+
## The FORMAT function
8+
9+
The function `format` takes at least two arguments: a stream to print to and control string defining what and how is to be printed.
10+
11+
Streams are a concept of their own and will be described elsewhere. But for now all one needs to know is that the printing is done to the stream and there are two special values: `t` and `nil` for this argument. `t` means to print to standard output (actually the stream to which the variable `*standard-output*` is bound). and `nil` means to create and string and return it.
12+
13+
Note that `format` will always evaluate to `nil` except in that latter case where `nil` is specified as the stream. This is a common way to do simply string interpolation or construction.
14+
15+
```lisp
16+
(format nil "hello world") ; => "hello world"
17+
(format t "hello world") ; => NIL (but "hello world" is printed to standard output)
18+
```
19+
20+
## Format Directives
21+
22+
The control string can contain "format directives" which give instructions to `format` about what to print and how to interpret any other parameters to `format`. In a sense the control string is a program which `format` will interpret and run. All directives start with `~` and are followed by a character (_e.g._ `~A`, `~D`, `~&`). The case of the character does not matter.
23+
24+
Most format directives simply interpolate an argument to `format` into the output. This is said to "consume" the argument. Some directives do not consume arguments, or consume more than one. One directive even allows you to jump around in the argument list or skip arguments.
25+
26+
## Basic formatting
27+
28+
A very general purpose format directive is `~A`. This will print a 'human readable' version of the data.
29+
30+
```lisp
31+
(format nil "Value = ~a" 10) ; => "10"
32+
(format nil "hello ~a" "world") ; => "hello world"
33+
(format nil "The list is: ~A" (list 1 2 3)) ; => "The list is: (1 2 3)"
34+
```
35+
36+
Another useful format directive is `~S`. This will print a 'machine readable' version of the data. This can be very useful if you want to be able to interpret the output created as Lisp data. For many things it will look the same as `~A` but note that for strings for example the output will include the quotation marks (escaped when included in another string) so that the value could be read back in as the string parameter.
37+
38+
```lisp
39+
(format nil "~s" 10) ; => "10"
40+
(format nil "~s" "hello world") ; => "\"hello world\""
41+
(format nil "~s" (list 1 2 3)) ; => "(1 2 3)"
42+
```
43+
44+
Two more very useful directives are `~%` and `~&`. The first outputs a newline character while the latter outputs a "fresh line". The difference is that `~%` always outputs a newline while `~&` will output one if not currently at the beginning of a line. Neither of these consume an argument.
45+
46+
```lisp
47+
(format nil "~%new~%lines") ; => "
48+
new
49+
lines"
50+
51+
(format nil "~&new~&lines" ; => "new
52+
lines"
53+
```
54+
55+
Finally `~~` is a simple directive that will output a literal `~`.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Introduction
2+
3+
%{concept:format-basics}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"blurb": "Help Layla produce short reports",
3+
"authors": ["verdammelt"],
4+
"files": {
5+
"solution": ["reporting-for-duty.lisp"],
6+
"test": ["reporting-for-duty-test.lisp"],
7+
"exemplar": [".meta/exemplar.lisp"]
8+
},
9+
"icon": "high-scores"
10+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
(defpackage :reporting-for-duty
2+
(:use :cl)
3+
(:export :format-quarter-value :format-two-quarters
4+
:format-two-quarters-for-reading))
5+
6+
(in-package :reporting-for-duty)
7+
8+
(defun format-quarter-value (quarter value)
9+
(format nil "The value ~A quarter: ~A" quarter value))
10+
11+
(defun format-two-quarters (stream quarter1 value1 quarter2 value2)
12+
(format stream "~%~A~&~A~&"
13+
(format-quarter-value quarter1 value1)
14+
(format-quarter-value quarter2 value2)))
15+
16+
(defun format-two-quarters-for-reading (stream quarter1 value1 quarter2 value2)
17+
(format stream "(~S ~S)"
18+
(format-quarter-value quarter1 value1)
19+
(format-quarter-value quarter2 value2)))

0 commit comments

Comments
 (0)