Skip to content

Commit

Permalink
Raindrops approaches (#946)
Browse files Browse the repository at this point in the history
* Raindrops approach: introduction

* Raindrops approaches: if statements approach

* Raindrops approaches: sprintf function approach

* Raindrops approaches: data-driven programming approach

* Apply suggestions from code review

---------

Co-authored-by: Ryan Hartlage <ryanplusplus@gmail.com>
  • Loading branch information
danilopiazza and ryanplusplus committed Jan 29, 2024
1 parent 4e49d3b commit 39c890b
Show file tree
Hide file tree
Showing 8 changed files with 375 additions and 0 deletions.
36 changes: 36 additions & 0 deletions exercises/practice/raindrops/.approaches/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"introduction": {
"authors": [
"danilopiazza"
]
},
"approaches": [
{
"uuid": "830f6fb9-cd91-4c53-8b73-cbcb4585ad51",
"slug": "if-statements",
"title": "if Statements",
"blurb": "Use multiple if statements.",
"authors": [
"danilopiazza"
]
},
{
"uuid": "cac9cf42-cd7b-4070-8f86-5a9753d17d8e",
"slug": "sprintf",
"title": "sprintf Function",
"blurb": "Use the sprintf function.",
"authors": [
"danilopiazza"
]
},
{
"uuid": "ec393164-0222-4bed-9234-fc25755d8746",
"slug": "data-driven",
"title": "Data-Driven Programming",
"blurb": "Use data-driven programming.",
"authors": [
"danilopiazza"
]
}
]
}
68 changes: 68 additions & 0 deletions exercises/practice/raindrops/.approaches/data-driven/content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Data-Driven Programming

**raindrops.h**

```c
#ifndef RAINDROPS_H
#define RAINDROPS_H

char *convert(char result[], int drops);

#endif
```

**raindrops.c**

```c
#include "raindrops.h"

#include <stdio.h>
#include <string.h>

typedef struct {
int factor;
const char *sound;
} sound_t;

static const sound_t SOUNDS[] = {
{ 3, "Pling" },
{ 5, "Plang" },
{ 7, "Plong" },
};

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))

char *convert(char result[], int drops)
{
for (size_t i = 0; i < ARRAY_SIZE(SOUNDS); i++) {
if (drops % SOUNDS[i].factor == 0) {
strcat(result, SOUNDS[i].sound);
}
}

if (strlen(result) == 0) {
sprintf(result, "%d", drops);
}

return result;
}
```
First, the program defines a [structure][struct] [data type][typedef] to hold together two pieces of information: a factor and its corresponding sound.
Then, an array is created to hold all the necessary data, which is used to drive the logc of the program.
The body of the function does not have any knowledge of the actual data, becoming simple and flexible:
- For each element of the `SOUNDS` array
-- If the given number is a multiple of the current `factor`
--- Then concatenate the current `sound` to the result string using the [`strcat` function][strcat].
This approach allows for extensible code: for example, new sounds could be added (or removed) without modifying the `convert` function.
Finally, the [`strlen` function] checks if `result` is empty: if so, the [`sprintf` function] formats `drops` as string into `result`.
[struct]: https://www.geeksforgeeks.org/structures-c/
[typedef]: https://www.geeksforgeeks.org/typedef-in-c/
[strcat]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/strcat.html
[strlen]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/strlen.html
[sprintf]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/sprintf.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
for (size_t i = 0; i < ARRAY_SIZE(SOUNDS); i++) {
if (drops % SOUNDS[i].factor == 0) {
strcat(result, SOUNDS[i].sound);
}
}
49 changes: 49 additions & 0 deletions exercises/practice/raindrops/.approaches/if-statements/content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# `if` Statements

**raindrops.h**

```c
#ifndef RAINDROPS_H
#define RAINDROPS_H

char *convert(char result[], int drops);

#endif
```

**raindrops.c**

```c
#include "raindrops.h"

#include <stdio.h>
#include <string.h>

char *convert(char result[], int drops)
{
if (drops % 3 == 0)
strcat(result, "Pling");
if (drops % 5 == 0)
strcat(result, "Plang");
if (drops % 7 == 0)
strcat(result, "Plong");

if (strlen(result) == 0)
sprintf(result, "%d", drops);

return result;
}
```
- The first `if` statement checks if `drops` is a multiple of `3`.
If so, `"Pling"` is concatenated to `result` using the [`strcat` function][strcat].
- The second `if` statement checks if `drops` is a multiple of `5`.
If so, `"Plang"` is concatenated to `result` using the [`strcat` function][strcat].
- The thrd `if` statement checks if `drops` is a multiple of `7`.
If so, `"Plong"` is concatenated to `result` using the [`strcat` function][strcat].
Finally, the [`strlen` function] checks if `result` is empty: if so, the [`sprintf` function] formats `drops` as string into `result`.
[strcat]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/strcat.html
[strlen]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/strlen.html
[sprintf]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/sprintf.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
if (drops % 3 == 0)
strcat(result, "Pling");
if (drops % 5 == 0)
strcat(result, "Plang");
if (drops % 7 == 0)
strcat(result, "Plong");
159 changes: 159 additions & 0 deletions exercises/practice/raindrops/.approaches/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# Introduction

There are various idioomatic ways to solve Raindrops.
A straightforward and approach is to use a series of `if` statements.
Another approach could look up both factors and raindrop sounds from an array, using data-driven programming to make the code as simple as possible.

## General Guidance

The key to solving Raindrops is to know if the input is evenly divisible by `3`, `5` and/or `7` using the [modulo operator][modulo-operator].

## Assumptions

- `drops` is a non-negative, 32-bit integer (from `0` to `2,147,483,647`).
- `result` has enough space to hold the largest output string, meaning 16 bytes:
- 15 bytes for `"PlingPlangPlong"`, plus one for the null terminating character;
- 10 bytes for the largest possible value of `drops`, plus one for `'\0'`.
- `result` has been initialized as an empty string (that is, `result[0]` is `'\0'`).

## Approach: `if` Statements

**raindrops.h**

```c
#ifndef RAINDROPS_H
#define RAINDROPS_H

char *convert(char result[], int drops);

#endif
```

**raindrops.c**

```c
#include "raindrops.h"

#include <stdio.h>
#include <string.h>

char *convert(char result[], int drops)
{
if (drops % 3 == 0)
strcat(result, "Pling");
if (drops % 5 == 0)
strcat(result, "Plang");
if (drops % 7 == 0)
strcat(result, "Plong");

if (strlen(result) == 0)
sprintf(result, "%d", drops);

return result;
}
```
This approach uses a series of `if`-statements and string concatentation to build up the result string.
For more information, check the [`if` statements approach][approach-if-statements].
## Approach: `sprintf` Function
**raindrops.h**
```c
#ifndef RAINDROPS_H
#define RAINDROPS_H
char *convert(char result[], int drops);
#endif
```

**raindrops.c**

```c
#include "raindrops.h"

#include <stdio.h>
#include <string.h>

#include "raindrops.h"

#include <stdio.h>
#include <string.h>

char *convert(char result[], int drops)
{
sprintf(result, "%s%s%s", drops % 3 == 0 ? "Pling" : "",
drops % 5 == 0 ? "Plang" : "", drops % 7 == 0 ? "Plong" : "");

if (strlen(result) == 0)
sprintf(result, "%d", drops);

return result;
}
```
This approach uses a single call to the [`sprintf` function][sprintf] to build the result string;
it contains a series of [ternary conditional operators][conditional-opeator].
For more information, check the [`sprintf` functon approach][approach-sprintf].
## Approach: Data-Driven Programming
**raindrops.h**
```c
#ifndef RAINDROPS_H
#define RAINDROPS_H
char *convert(char result[], int drops);
#endif
```

**raindrops.c**

```c
#include "raindrops.h"

#include <stdio.h>
#include <string.h>

typedef struct {
int factor;
const char *sound;
} sound_t;

static const sound_t SOUNDS[] = {
{ 3, "Pling" },
{ 5, "Plang" },
{ 7, "Plong" },
};

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))

char *convert(char result[], int drops)
{
for (size_t i = 0; i < ARRAY_SIZE(SOUNDS); i++) {
if (drops % SOUNDS[i].factor == 0) {
strcat(result, SOUNDS[i].sound);
}
}

if (strlen(result) == 0) {
sprintf(result, "%d", drops);
}

return result;
}
```
This approach puts some of the logic into data, simplifying the code.
For more information, check the [data-driven approach][approach-data-driven].
[modulo-operator]: https://www.geeksforgeeks.org/modulo-operator-in-c-cpp-with-examples/
[conditional-operator]: https://www.geeksforgeeks.org/conditional-or-ternary-operator-in-c/
[sprintf]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/sprintf.html
[approach-if-statements]: https://exercism.org/tracks/c/exercises/raindrops/approaches/if-statements
[approach-sprintf]: https://exercism.org/tracks/c/exercises/raindrops/approaches/sprintf
[approach-data-driven]: https://exercism.org/tracks/c/exercises/raindrops/approaches/data-driven
50 changes: 50 additions & 0 deletions exercises/practice/raindrops/.approaches/sprintf/content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
## `sprintf` Function

**raindrops.h**

```c
#ifndef RAINDROPS_H
#define RAINDROPS_H

char *convert(char result[], int drops);

#endif
```

**raindrops.c**

```c
#include "raindrops.h"

#include <stdio.h>
#include <string.h>

#include "raindrops.h"

#include <stdio.h>
#include <string.h>

char *convert(char result[], int drops)
{
sprintf(result, "%s%s%s", drops % 3 == 0 ? "Pling" : "",
drops % 5 == 0 ? "Plang" : "", drops % 7 == 0 ? "Plong" : "");

if (strlen(result) == 0)
sprintf(result, "%d", drops);

return result;
}
```
This approach allows for very concise, if not a bit obfuscated, code.
A series of [ternary conditional operators][conditional-operator] check if `drops` is a multiple of `3`, `5`, or `7` respectively.
If it is, the expression returns the appropriate raindrop sound (either `"Pling"`, `"Plang"` or `"Plong"`); otherwise, the result is the empty string (`""`).
Then, in the same statement, a single call to the [`sprintf` function][sprintf] concatenates all the above three strings.
Finally, the [`strlen` function][strlen] checks if `result` is empty: if so, `sprintf` is used again to format `drops` as string into `result`.
[conditional-operator]: https://www.geeksforgeeks.org/conditional-or-ternary-operator-in-c/
[sprintf]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/sprintf.html
[strlen]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/strlen.html
2 changes: 2 additions & 0 deletions exercises/practice/raindrops/.approaches/sprintf/snippet.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
sprintf(result, "%s%s%s", drops % 3 == 0 ? "Pling" : "",
drops % 5 == 0 ? "Plang" : "", drops % 7 == 0 ? "Plong" : "");

0 comments on commit 39c890b

Please sign in to comment.