Skip to content

Commit

Permalink
Add reverse-string exercise (#703)
Browse files Browse the repository at this point in the history
* Add reverse-string exercise. Excluding grapheme cluster tests
* allow grapheme cluster tests
* append some instructions
  • Loading branch information
glennj authored Nov 18, 2024
1 parent 9409f95 commit e2fe854
Show file tree
Hide file tree
Showing 11 changed files with 257 additions and 0 deletions.
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,14 @@
"practices": [],
"prerequisites": [],
"difficulty": 1
},
{
"slug": "reverse-string",
"name": "Reverse String",
"uuid": "c73132d4-4f67-4085-ba17-84f38824afe1",
"practices": [],
"prerequisites": [],
"difficulty": 2
}
]
},
Expand Down
13 changes: 13 additions & 0 deletions exercises/practice/reverse-string/.docs/instructions.append.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# ignore

## Unicode and grapheme clusters

This exercise includes tests with Unicode strings that contain grapheme clusters.
Perl's builtin string processing functions are unsufficient to handle these strings.
Here are a couple of resources to help you:

* [Treat Unicode strings as grapheme clusters][foy]
* [Extract by Grapheme Instead of Codepoint (regex)][christiansen]

[foy]: https://www.effectiveperlprogramming.com/2011/06/treat-unicode-strings-as-grapheme-clusters/
[christiansen]: https://www.perl.com/pub/2012/05/perlunicookbook-extract-by-grapheme-instead-of-codepoint-regex.html/
9 changes: 9 additions & 0 deletions exercises/practice/reverse-string/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Instructions

Your task is to reverse a given string.

Some examples:

- Turn `"stressed"` into `"desserts"`.
- Turn `"strops"` into `"sports"`.
- Turn `"racecar"` into `"racecar"`.
5 changes: 5 additions & 0 deletions exercises/practice/reverse-string/.docs/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Introduction

Reversing strings (reading them from right to left, rather than from left to right) is a surprisingly common task in programming.

For example, in bioinformatics, reversing the sequence of DNA or RNA strings is often important for various analyses, such as finding complementary strands or identifying palindromic sequences that have biological significance.
19 changes: 19 additions & 0 deletions exercises/practice/reverse-string/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"authors": [
"glennj"
],
"files": {
"solution": [
"lib/ReverseString.pm"
],
"test": [
"t/reverse-string.t"
],
"example": [
".meta/solutions/lib/ReverseString.pm"
]
},
"blurb": "Reverse a given string.",
"source": "Introductory challenge to reverse an input string",
"source_url": "https://medium.freecodecamp.org/how-to-reverse-a-string-in-javascript-in-3-different-ways-75e4763c68cb"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package ReverseString;

use strict;
use warnings;
use experimental qw<signatures postderef postderef_qq>;

use Exporter qw<import>;
our @EXPORT_OK = qw<str_reverse>;

sub str_reverse ($text) {
my $rev = '';

# \X matches "extended grapheme clusters"
my @chars = $text =~ /(\X)/g;

# the obvious boring solution
# $rev = reverse $text

# iterating
# foreach my $char (@chars) {
# $rev = $char . $rev;
# }

# iterating tersely
# $rev = $_ . $rev for @chars;

# iterating functionally
# use List::Util qw(reduce);
# $rev = reduce {$b . $a} '', @chars;

# fewer iterations
for ( my ( $i, $j ) = ( 0, $#chars ); $i < $j; $i++, $j-- ) {
( $chars[$i], $chars[$j] ) = ( $chars[$j], $chars[$i] );
}
$rev = join '', @chars;

return $rev;
}

1;
50 changes: 50 additions & 0 deletions exercises/practice/reverse-string/.meta/template-data.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
subs: str_reverse

properties:
reverse:
test: |-
use Data::Dmp;
local $Data::Dmp::OPT_STRINGIFY_NUMBERS = 1;
sprintf(<<'END', map {dmp($_)} $case->{input}{value}, $case->@{qw<expected description>});
is(
str_reverse(%s),
%s,
%s,
);
END
example: |-
sub str_reverse ($text) {
my $rev = '';
# \X matches "extended grapheme clusters"
my @chars = $text =~ /(\X)/g;
# the obvious boring solution
# $rev = reverse $text
# iterating
# foreach my $char (@chars) {
# $rev = $char . $rev;
# }
# iterating tersely
# $rev = $_ . $rev for @chars;
# iterating functionally
# use List::Util qw(reduce);
# $rev = reduce {$b . $a} '', @chars;
# fewer iterations
for (my ($i, $j) = (0, $#chars); $i < $j; $i++, $j--) {
($chars[$i], $chars[$j]) = ($chars[$j], $chars[$i]);
}
$rev = join '', @chars;
return $rev;
}
stub: |-
sub str_reverse ($text) {
return undef;
}
37 changes: 37 additions & 0 deletions exercises/practice/reverse-string/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# This is an auto-generated file.
#
# Regenerating this file via `configlet sync` will:
# - Recreate every `description` key/value pair
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
# - Preserve any other key/value pair
#
# As user-added comments (using the # character) will be removed when this file
# is regenerated, comments can be added via a `comment` key.

[c3b7d806-dced-49ee-8543-933fd1719b1c]
description = "an empty string"

[01ebf55b-bebb-414e-9dec-06f7bb0bee3c]
description = "a word"

[0f7c07e4-efd1-4aaa-a07a-90b49ce0b746]
description = "a capitalized word"

[71854b9c-f200-4469-9f5c-1e8e5eff5614]
description = "a sentence with punctuation"

[1f8ed2f3-56f3-459b-8f3e-6d8d654a1f6c]
description = "a palindrome"

[b9e7dec1-c6df-40bd-9fa3-cd7ded010c4c]
description = "an even-sized word"

[1bed0f8a-13b0-4bd3-9d59-3d0593326fa2]
description = "wide characters"

[93d7e1b8-f60f-4f3c-9559-4056e10d2ead]
description = "grapheme cluster with pre-combined form"

[1028b2c1-6763-4459-8540-2da47ca512d9]
description = "grapheme clusters"
12 changes: 12 additions & 0 deletions exercises/practice/reverse-string/lib/ReverseString.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package ReverseString;

use v5.40;

use Exporter qw<import>;
our @EXPORT_OK = qw<str_reverse>;

sub str_reverse ($text) {
return undef;
}

1;
63 changes: 63 additions & 0 deletions exercises/practice/reverse-string/t/reverse-string.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env perl
use Test2::V0;

use FindBin qw<$Bin>;
use lib "$Bin/../lib", "$Bin/../local/lib/perl5";

use ReverseString qw<str_reverse>;

is( # begin: c3b7d806-dced-49ee-8543-933fd1719b1c
str_reverse(""),
"",
"an empty string",
); # end: c3b7d806-dced-49ee-8543-933fd1719b1c

is( # begin: 01ebf55b-bebb-414e-9dec-06f7bb0bee3c
str_reverse("robot"),
"tobor",
"a word",
); # end: 01ebf55b-bebb-414e-9dec-06f7bb0bee3c

is( # begin: 0f7c07e4-efd1-4aaa-a07a-90b49ce0b746
str_reverse("Ramen"),
"nemaR",
"a capitalized word",
); # end: 0f7c07e4-efd1-4aaa-a07a-90b49ce0b746

is( # begin: 71854b9c-f200-4469-9f5c-1e8e5eff5614
str_reverse("I'm hungry!"),
"!yrgnuh m'I",
"a sentence with punctuation",
); # end: 71854b9c-f200-4469-9f5c-1e8e5eff5614

is( # begin: 1f8ed2f3-56f3-459b-8f3e-6d8d654a1f6c
str_reverse("racecar"),
"racecar",
"a palindrome",
); # end: 1f8ed2f3-56f3-459b-8f3e-6d8d654a1f6c

is( # begin: b9e7dec1-c6df-40bd-9fa3-cd7ded010c4c
str_reverse("drawer"),
"reward",
"an even-sized word",
); # end: b9e7dec1-c6df-40bd-9fa3-cd7ded010c4c

is( # begin: 1bed0f8a-13b0-4bd3-9d59-3d0593326fa2
str_reverse("\x{5B50}\x{732B}"),
"\x{732B}\x{5B50}",
"wide characters",
); # end: 1bed0f8a-13b0-4bd3-9d59-3d0593326fa2

is( # begin: 93d7e1b8-f60f-4f3c-9559-4056e10d2ead
str_reverse("Wu\x{308}rstchenstand"),
"dnatsnehctsru\x{308}W",
"grapheme cluster with pre-combined form",
); # end: 93d7e1b8-f60f-4f3c-9559-4056e10d2ead

is( # begin: 1028b2c1-6763-4459-8540-2da47ca512d9
str_reverse("\x{E1C}\x{E39}\x{E49}\x{E40}\x{E02}\x{E35}\x{E22}\x{E19}\x{E42}\x{E1B}\x{E23}\x{E41}\x{E01}\x{E23}\x{E21}"),
"\x{E21}\x{E23}\x{E01}\x{E41}\x{E23}\x{E1B}\x{E42}\x{E19}\x{E22}\x{E02}\x{E35}\x{E40}\x{E1C}\x{E39}\x{E49}",
"grapheme clusters",
); # end: 1028b2c1-6763-4459-8540-2da47ca512d9

done_testing;

0 comments on commit e2fe854

Please sign in to comment.