Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Raindrops approaches #946

Merged
merged 5 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
36 changes: 36 additions & 0 deletions exercises/practice/raindrops/.approaches/config.json
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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-opeator] check if `drops` is a multiple of `3`, `5`, or `7` respectively.
ryanplusplus marked this conversation as resolved.
Show resolved Hide resolved
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] concatenates all the above three strings.
ryanplusplus marked this conversation as resolved.
Show resolved Hide resolved

Finally, the [`strlen` function] checks if `result` is empty: if so, `sprintf` is used again to format `drops` as string into `result`.
ryanplusplus marked this conversation as resolved.
Show resolved Hide resolved

[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
@@ -0,0 +1,2 @@
sprintf(result, "%s%s%s", drops % 3 == 0 ? "Pling" : "",
drops % 5 == 0 ? "Plang" : "", drops % 7 == 0 ? "Plong" : "");