Skip to content

Commit 1333d31

Browse files
lasagna concept exercise (#173)
1 parent 27cad07 commit 1333d31

11 files changed

Lines changed: 343 additions & 19 deletions

File tree

config.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,26 @@
3131
},
3232
"exercises": {
3333
"concept": [
34+
{
35+
"slug": "lasagna",
36+
"name": "Leah's Luscious Lasagna",
37+
"uuid": "a35d8564-a135-4930-9497-c47ae93857a5",
38+
"concepts": [
39+
"stack-effect"
40+
],
41+
"prerequisites": [],
42+
"status": "wip"
43+
},
3444
{
3545
"slug": "annalyns-infiltration",
3646
"name": "Annalyns Infiltration",
3747
"uuid": "57a3408b-1c45-4860-9605-47eb6651fb81",
3848
"concepts": [
49+
"booleans"
50+
],
51+
"prerequisites": [
3952
"stack-effect"
4053
],
41-
"prerequisites": [],
4254
"status": "wip"
4355
}
4456
],
Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,44 @@
11
# Introduction
22

3-
## Booleans in Factor
3+
This exercise builds on the data stack and stack-effect notation you
4+
met in [Leah's Luscious Lasagna][lasagna]. Here you will write words
5+
that combine boolean values.
46

5-
True and false are written as the words `t` and `f`. `f` is Factor's only
6-
falsy value — everything else, including `0` and empty collections, is
7+
## Booleans
8+
9+
The two boolean literals are `t` and `f`. `f` is Factor's only falsy
10+
value — everything else, including `0` and empty collections, is
711
truthy.
812

913
```factor
10-
t .
11-
! => t
12-
13-
f .
14-
! => f
14+
t . ! => t
15+
f . ! => f
1516
```
1617

17-
## Boolean operators
18+
## Boolean words
1819

19-
Three words in the `kernel` vocabulary handle the common cases:
20+
Three boolean words live in the `kernel` vocabulary and are available
21+
without a `USING:` line:
2022

21-
- `and` — returns truthy when both inputs are truthy.
22-
- `or` — returns truthy when either input is truthy.
23-
- `not` — inverts its argument.
23+
```
24+
and ( x y -- ? ) ! true when both inputs are truthy
25+
or ( x y -- ? ) ! true when either input is truthy
26+
not ( x -- ? ) ! inverts its argument
27+
```
2428

2529
```factor
2630
t t and . ! => t
2731
t f and . ! => f
28-
2932
f t or . ! => t
30-
3133
t not . ! => f
3234
```
3335

34-
Because Factor is concatenative, these words consume their arguments from
35-
the data stack. If your arguments are on the stack in the "wrong" order for
36-
a particular operator, use `swap` to flip them.
36+
The `?` in each output is the stack-effect convention for "a boolean".
37+
38+
## Swapping the top two values
39+
40+
If two values are on the stack in the wrong order for the next word,
41+
`swap ( x y -- y x )` flips the top two. You will need it for one of
42+
the tasks below.
43+
44+
[lasagna]: https://exercism.org/tracks/factor/exercises/lasagna
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Hints
2+
3+
## General
4+
5+
- You will need to define a constant and three words that operate on
6+
the stack.
7+
- The arithmetic words `+`, `-`, and `*` all live in the `math`
8+
vocabulary. Add `math` to your `USING:` line.
9+
10+
## 1. Store the expected bake time in a constant
11+
12+
- Use `CONSTANT:` to define `expected-bake-time` near the top of the
13+
file, above the word definitions.
14+
15+
## 2. Calculate the preparation time in minutes
16+
17+
- Use `*` with stack effect `( x y -- product )` to multiply the number
18+
of layers by `2`.
19+
20+
## 3. Calculate the remaining oven time in minutes
21+
22+
- Push `expected-bake-time` and then subtract the current time.
23+
- Remember that `-` computes `deeper - top`. If you push values in the
24+
order `current-time`, `expected-bake-time`, you will need `swap` to
25+
get the subtraction the right way around.
26+
27+
## 4. Calculate the total working time in minutes
28+
29+
- Reuse the `preparation-time` word you defined in task 2.
30+
- Only one of the two inputs should go to `preparation-time`; the other
31+
should be added to the result.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Instructions
2+
3+
In this exercise you're going to write some code to help you cook a
4+
brilliant lasagna from your favorite cooking book.
5+
6+
You have four tasks, all related to the time spent cooking the lasagna.
7+
8+
## 1. Store the expected bake time in a constant
9+
10+
Define the `expected-bake-time` constant, which should return how many
11+
minutes the lasagna needs to bake in the oven.
12+
13+
According to the cooking book, lasagna needs to be in the oven for a
14+
total of 40 minutes.
15+
16+
```factor
17+
expected-bake-time .
18+
! => 40
19+
```
20+
21+
## 2. Calculate the preparation time in minutes
22+
23+
Define the `preparation-time` word. It takes the number of layers you
24+
added to the lasagna off the stack and leaves behind how many minutes
25+
you spent preparing it, assuming each layer takes 2 minutes to prepare.
26+
27+
```factor
28+
4 preparation-time .
29+
! => 8
30+
```
31+
32+
## 3. Calculate the remaining oven time in minutes
33+
34+
Define the `remaining-time` word. It takes the number of minutes the
35+
lasagna has already spent in the oven and leaves behind how many
36+
minutes it still has to remain in there.
37+
38+
```factor
39+
25 remaining-time .
40+
! => 15
41+
```
42+
43+
## 4. Calculate the total working time in minutes
44+
45+
Define the `total-working-time` word. It takes two arguments off the
46+
stack — first the number of layers in the lasagna, then the number of
47+
minutes the lasagna has already been in the oven — and leaves behind
48+
the total time spent cooking so far: the preparation time plus the
49+
time already in the oven.
50+
51+
```factor
52+
3 20 total-working-time .
53+
! => 26
54+
```
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# Introduction
2+
3+
Welcome to Factor! Factor is a *concatenative* language: instead of
4+
calling functions with parenthesised arguments, you write a sequence of
5+
words that pass values to each other through a shared **data stack**.
6+
7+
## Comments
8+
9+
A `!` starts a line comment — Factor ignores everything from `!` to the
10+
end of the line. The examples below use `!` both to annotate what code
11+
does and to show what it would print.
12+
13+
## The data stack
14+
15+
Writing a literal pushes it onto the top of the stack. Code is read
16+
left to right.
17+
18+
```factor
19+
2 3 ! stack (bottom → top): 2 3
20+
```
21+
22+
There is no other way to pass data around — every word reads its
23+
inputs from the top of the stack and writes its outputs back there.
24+
25+
## Words
26+
27+
A **word** is Factor's name for a function. Calling a word pops some
28+
values from the top of the stack and pushes some values back.
29+
`.` pops the top value and prints it; the integer arithmetic words
30+
`+`, `-`, `*` pop two numbers and push the result:
31+
32+
```factor
33+
2 3 + . ! prints 5
34+
8 3 - . ! prints 5 (8 - 3, not 3 - 8)
35+
2 3 * . ! prints 6
36+
```
37+
38+
The arithmetic words live in the `math` vocabulary, so a file that uses
39+
them needs `math` in its `USING:` line.
40+
41+
## Stack effects
42+
43+
Every word is documented with a **stack effect** of the form
44+
`( inputs -- outputs )`. It is the word's contract: this word pops the
45+
inputs off the top of the stack and leaves the outputs in their place.
46+
The names inside are documentation for humans — the stack itself is
47+
positional, not named.
48+
49+
```factor
50+
! + is specified as ( x y -- sum )
51+
! . is specified as ( x -- )
52+
```
53+
54+
The top of the stack is the *right-hand* input. So `8 3 -` has `3` on
55+
top, the stack effect is `( x y -- difference )`, and the result is
56+
`8 - 3`.
57+
58+
A trailing `?` in the outputs is the convention for "a boolean", but
59+
the lasagna exercise uses only numbers.
60+
61+
## Defining a word
62+
63+
`:` starts a word definition, the stack effect comes next, then the
64+
body, then `;` ends it.
65+
66+
```factor
67+
: square ( x -- x^2 ) dup * ;
68+
69+
4 square . ! => 16
70+
```
71+
72+
Factor's compiler checks that the body actually matches the declared
73+
stack effect: a word that claims `( x -- y )` but leaves zero or two
74+
values on the stack will not compile.
75+
76+
## Constants
77+
78+
`CONSTANT:` defines a name for a fixed value. A constant is itself a
79+
word — calling it pushes the value onto the stack:
80+
81+
```factor
82+
CONSTANT: pi 3
83+
84+
pi pi * . ! => 9
85+
```
86+
87+
`CONSTANT:` is core syntax and does not need a `USING:` line. Place
88+
constants at the top of the file, before any word that uses them.
89+
90+
## Calling one word from another
91+
92+
A word's body can call any word already in scope, including ones you
93+
defined earlier in the same file:
94+
95+
```factor
96+
: double ( x -- 2x ) 2 * ;
97+
: quadruple ( x -- 4x ) double double ;
98+
99+
5 quadruple . ! => 20
100+
```
101+
102+
This is how the last task in the exercise reuses an earlier one.
103+
104+
## Swapping the top two values
105+
106+
If two values are on the stack in the wrong order for the next word,
107+
`swap` flips the top two:
108+
109+
```
110+
swap ( x y -- y x )
111+
```
112+
113+
`swap` lives in the `kernel` vocabulary.
114+
115+
## Naming conventions
116+
117+
Words and constants both use `lowercase-kebab-case`: lowercase letters
118+
joined by hyphens (for example, `expected-bake-time`,
119+
`preparation-time`).
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Source
2+
3+
Forked from the Julia track's ["Lasagna" exercise][julia-source], which is in turn based on the F# track's ["Lucian's Luscious Lasagna" exercise][fsharp-source], designed by [Erik Schierboom][erik].
4+
5+
[julia-source]: https://github.com/exercism/julia/tree/main/exercises/concept/lasagna
6+
[fsharp-source]: https://github.com/exercism/fsharp/tree/main/exercises/concept/lucians-luscious-lasagna
7+
[erik]: https://github.com/ErikSchierboom
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"authors": [
3+
"keiravillekode"
4+
],
5+
"files": {
6+
"solution": [
7+
"lasagna/lasagna.factor"
8+
],
9+
"test": [
10+
"lasagna/lasagna-tests.factor"
11+
],
12+
"exemplar": [
13+
".meta/exemplar.factor"
14+
]
15+
},
16+
"forked_from": [
17+
"julia/lasagna"
18+
],
19+
"blurb": "Learn about stack effects in Factor by cooking delicious lasagna."
20+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Design
2+
3+
## Goal
4+
5+
Introduce a complete novice to Factor by way of a tiny cooking-themed exercise. This is the first exercise on the track, so it has to teach the fundamentals from scratch: the data stack, words operating on it, and the stack-effect notation that documents every word.
6+
7+
## Learning objectives
8+
9+
- Understand that Factor passes values via a last-in-first-out data stack.
10+
- Read and write a stack effect of the form `( inputs -- outputs )`.
11+
- Define a word with `:` ... `;` and have its body match the declared stack effect.
12+
- Define a constant with `CONSTANT:` and use it as a word.
13+
- Use the integer arithmetic words `+`, `-`, `*`.
14+
- Define a word whose body calls another word already in scope.
15+
- Use `swap` to reorder the top two values when the operator wants them flipped.
16+
17+
## Out of scope
18+
19+
- Locals (`::`, named parameters).
20+
- Combinators beyond `swap` (`dup`, `dip`, `bi`, `tri`, …).
21+
- Floating-point arithmetic and the difference between `/` and `/i`.
22+
- Vocabulary visibility, private words.
23+
- Tests-framework internals beyond `unit-test`.
24+
25+
## Concepts
26+
27+
- `stack-effect`: every Factor word is documented as `( inputs -- outputs )` and its body must leave exactly that on the stack. This exercise is a student's first encounter with the notation and the calling convention behind it.
28+
29+
## Prerequisites
30+
31+
There are no prerequisites — this is the first concept exercise on the Factor track.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
USING: kernel math ;
2+
IN: lasagna
3+
4+
CONSTANT: expected-bake-time 40
5+
6+
: preparation-time ( layers -- minutes )
7+
2 * ;
8+
9+
: remaining-time ( current-time -- remaining )
10+
expected-bake-time swap - ;
11+
12+
: total-working-time ( layers current-time -- minutes )
13+
swap preparation-time + ;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
USING: lasagna tools.test ;
2+
IN: lasagna.tests
3+
4+
! TASK: 1 expected bake time
5+
{ 40 } [ expected-bake-time ] unit-test
6+
7+
! TASK: 2 preparation time
8+
{ 2 } [ 1 preparation-time ] unit-test
9+
{ 8 } [ 4 preparation-time ] unit-test
10+
11+
! TASK: 3 remaining time
12+
{ 15 } [ 25 remaining-time ] unit-test
13+
14+
! TASK: 4 total working time
15+
{ 32 } [ 1 30 total-working-time ] unit-test
16+
{ 16 } [ 4 8 total-working-time ] unit-test

0 commit comments

Comments
 (0)