Skip to content

Commit ac77905

Browse files
[ Implementation ] Run-Length Encoding (#744)
💜²⁹ -- 🤝 https://forum.exercism.org/t/new-exercise-contributions/4077
1 parent 8f6ec2c commit ac77905

9 files changed

+290
-0
lines changed

config.json

+8
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,14 @@
743743
"practices": [],
744744
"prerequisites": [],
745745
"difficulty": 1
746+
},
747+
{
748+
"slug": "run-length-encoding",
749+
"name": "Run-Length Encoding",
750+
"uuid": "8997f6f2-a8c3-42b5-94fc-12d0d386a64e",
751+
"practices": [],
752+
"prerequisites": [],
753+
"difficulty": 1
746754
}
747755
]
748756
},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Instructions
2+
3+
Implement run-length encoding and decoding.
4+
5+
Run-length encoding (RLE) is a simple form of data compression, where runs (consecutive data elements) are replaced by just one data value and count.
6+
7+
For example we can represent the original 53 characters with only 13.
8+
9+
```text
10+
"WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB" -> "12WB12W3B24WB"
11+
```
12+
13+
RLE allows the original data to be perfectly reconstructed from the compressed data, which makes it a lossless data compression.
14+
15+
```text
16+
"AABCCCDEEEE" -> "2AB3CD4E" -> "AABCCCDEEEE"
17+
```
18+
19+
For simplicity, you can assume that the unencoded string will only contain the letters A through Z (either lower or upper case) and whitespace.
20+
This way data to be encoded will never contain any numbers and numbers inside data to be decoded always represent the count for the following character.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"authors": [
3+
"habere-et-dispertire"
4+
],
5+
"files": {
6+
"solution": [
7+
"lib/RunLengthEncoding.rakumod"
8+
],
9+
"test": [
10+
"t/run-length-encoding.rakutest"
11+
],
12+
"example": [
13+
".meta/solutions/lib/RunLengthEncoding.rakumod"
14+
]
15+
},
16+
"blurb": "Implement run-length encoding and decoding.",
17+
"source": "Wikipedia",
18+
"source_url": "https://en.wikipedia.org/wiki/Run-length_encoding"
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
unit module RunLengthEncoding;
2+
3+
grammar RLE {
4+
token TOP { <pair>+ }
5+
token pair { <tally>? <element> }
6+
token tally { <digit>+ }
7+
token element { <alpha> | ' ' }
8+
}
9+
class RLE::Decode {
10+
method TOP ($/) { make $<pair>.map( *.made ).join }
11+
method pair ($/) { make $<element>.made x ( $<tally>.made // 1 ) }
12+
method tally ($/) { make $/.Int }
13+
method element ($/) { make $/.Str }
14+
}
15+
sub rle-decode ($compressed) is export {
16+
RLE.parse( $compressed, actions => RLE::Decode ).made
17+
}
18+
sub rle-encode ($raw) is export {
19+
given $raw.comb( / ( [<digit>+]? ) [<alpha> | ' ']+ % <same> / )
20+
.map( { .chars == 1 ?? $_ !! .chars ~ .comb.head } )
21+
.join
22+
-> $compressed {
23+
fail unless $raw eq rle-decode($compressed);
24+
return $compressed
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../../t/run-length-encoding.rakutest
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
properties:
2+
encode:
3+
test: |-
4+
sprintf(q :to 'END', %case<input><string>.Str.raku, %case<expected>.Str.raku, %case<description>.raku);
5+
cmp-ok(
6+
rle-encode(%s),
7+
"eq",
8+
%s,
9+
%s,
10+
);
11+
END
12+
decode:
13+
test: |-
14+
sprintf(q :to 'END', %case<input><string>.Str.raku, %case<expected>.Str.raku, %case<description>.raku);
15+
cmp-ok(
16+
rle-decode(%s),
17+
"eq",
18+
%s,
19+
%s,
20+
);
21+
END
22+
consistency:
23+
test: |-
24+
sprintf(q :to 'END', %case<input><string>.Str.raku, %case<expected>.Str.raku, %case<description>.raku);
25+
cmp-ok(
26+
rle-decode(rle-encode(%s)),
27+
"eq",
28+
%s,
29+
%s,
30+
);
31+
END
32+
33+
unit: module
34+
example: |-
35+
grammar RLE {
36+
token TOP { <pair>+ }
37+
token pair { <tally>? <element> }
38+
token tally { <digit>+ }
39+
token element { <alpha> | ' ' }
40+
}
41+
class RLE::Decode {
42+
method TOP ($/) { make $<pair>.map( *.made ).join }
43+
method pair ($/) { make $<element>.made x ( $<tally>.made // 1 ) }
44+
method tally ($/) { make $/.Int }
45+
method element ($/) { make $/.Str }
46+
}
47+
sub rle-decode ($compressed) is export {
48+
RLE.parse( $compressed, actions => RLE::Decode ).made
49+
}
50+
sub rle-encode ($raw) is export {
51+
given $raw.comb( / ( [<digit>+]? ) [<alpha> | ' ']+ % <same> / )
52+
.map( { .chars == 1 ?? $_ !! .chars ~ .comb.head } )
53+
.join
54+
-> $compressed {
55+
fail unless $raw eq rle-decode($compressed);
56+
return $compressed
57+
}
58+
}
59+
60+
stub: |-
61+
sub rle-encode ($raw) is export {
62+
}
63+
sub rle-decode ($compressed) is export {
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[ad53b61b-6ffc-422f-81a6-61f7df92a231]
13+
description = "run-length encode a string -> empty string"
14+
15+
[52012823-b7e6-4277-893c-5b96d42f82de]
16+
description = "run-length encode a string -> single characters only are encoded without count"
17+
18+
[b7868492-7e3a-415f-8da3-d88f51f80409]
19+
description = "run-length encode a string -> string with no single characters"
20+
21+
[859b822b-6e9f-44d6-9c46-6091ee6ae358]
22+
description = "run-length encode a string -> single characters mixed with repeated characters"
23+
24+
[1b34de62-e152-47be-bc88-469746df63b3]
25+
description = "run-length encode a string -> multiple whitespace mixed in string"
26+
27+
[abf176e2-3fbd-40ad-bb2f-2dd6d4df721a]
28+
description = "run-length encode a string -> lowercase characters"
29+
30+
[7ec5c390-f03c-4acf-ac29-5f65861cdeb5]
31+
description = "run-length decode a string -> empty string"
32+
33+
[ad23f455-1ac2-4b0e-87d0-b85b10696098]
34+
description = "run-length decode a string -> single characters only"
35+
36+
[21e37583-5a20-4a0e-826c-3dee2c375f54]
37+
description = "run-length decode a string -> string with no single characters"
38+
39+
[1389ad09-c3a8-4813-9324-99363fba429c]
40+
description = "run-length decode a string -> single characters with repeated characters"
41+
42+
[3f8e3c51-6aca-4670-b86c-a213bf4706b0]
43+
description = "run-length decode a string -> multiple whitespace mixed in string"
44+
45+
[29f721de-9aad-435f-ba37-7662df4fb551]
46+
description = "run-length decode a string -> lowercase string"
47+
48+
[2a762efd-8695-4e04-b0d6-9736899fbc16]
49+
description = "encode and then decode -> encode followed by decode gives original string"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
unit module RunLengthEncoding;
2+
3+
sub rle-encode ($raw) is export {
4+
}
5+
sub rle-decode ($compressed) is export {
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#!/usr/bin/env raku
2+
use Test;
3+
use lib $?FILE.IO.parent(2).add('lib');
4+
use RunLengthEncoding;
5+
6+
cmp-ok( # begin: ad53b61b-6ffc-422f-81a6-61f7df92a231
7+
rle-encode(""),
8+
"eq",
9+
"",
10+
"run-length encode a string: empty string",
11+
); # end: ad53b61b-6ffc-422f-81a6-61f7df92a231
12+
13+
cmp-ok( # begin: 52012823-b7e6-4277-893c-5b96d42f82de
14+
rle-encode("XYZ"),
15+
"eq",
16+
"XYZ",
17+
"run-length encode a string: single characters only are encoded without count",
18+
); # end: 52012823-b7e6-4277-893c-5b96d42f82de
19+
20+
cmp-ok( # begin: b7868492-7e3a-415f-8da3-d88f51f80409
21+
rle-encode("AABBBCCCC"),
22+
"eq",
23+
"2A3B4C",
24+
"run-length encode a string: string with no single characters",
25+
); # end: b7868492-7e3a-415f-8da3-d88f51f80409
26+
27+
cmp-ok( # begin: 859b822b-6e9f-44d6-9c46-6091ee6ae358
28+
rle-encode("WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB"),
29+
"eq",
30+
"12WB12W3B24WB",
31+
"run-length encode a string: single characters mixed with repeated characters",
32+
); # end: 859b822b-6e9f-44d6-9c46-6091ee6ae358
33+
34+
cmp-ok( # begin: 1b34de62-e152-47be-bc88-469746df63b3
35+
rle-encode(" hsqq qww "),
36+
"eq",
37+
"2 hs2q q2w2 ",
38+
"run-length encode a string: multiple whitespace mixed in string",
39+
); # end: 1b34de62-e152-47be-bc88-469746df63b3
40+
41+
cmp-ok( # begin: abf176e2-3fbd-40ad-bb2f-2dd6d4df721a
42+
rle-encode("aabbbcccc"),
43+
"eq",
44+
"2a3b4c",
45+
"run-length encode a string: lowercase characters",
46+
); # end: abf176e2-3fbd-40ad-bb2f-2dd6d4df721a
47+
48+
cmp-ok( # begin: 7ec5c390-f03c-4acf-ac29-5f65861cdeb5
49+
rle-decode(""),
50+
"eq",
51+
"",
52+
"run-length decode a string: empty string",
53+
); # end: 7ec5c390-f03c-4acf-ac29-5f65861cdeb5
54+
55+
cmp-ok( # begin: ad23f455-1ac2-4b0e-87d0-b85b10696098
56+
rle-decode("XYZ"),
57+
"eq",
58+
"XYZ",
59+
"run-length decode a string: single characters only",
60+
); # end: ad23f455-1ac2-4b0e-87d0-b85b10696098
61+
62+
cmp-ok( # begin: 21e37583-5a20-4a0e-826c-3dee2c375f54
63+
rle-decode("2A3B4C"),
64+
"eq",
65+
"AABBBCCCC",
66+
"run-length decode a string: string with no single characters",
67+
); # end: 21e37583-5a20-4a0e-826c-3dee2c375f54
68+
69+
cmp-ok( # begin: 1389ad09-c3a8-4813-9324-99363fba429c
70+
rle-decode("12WB12W3B24WB"),
71+
"eq",
72+
"WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB",
73+
"run-length decode a string: single characters with repeated characters",
74+
); # end: 1389ad09-c3a8-4813-9324-99363fba429c
75+
76+
cmp-ok( # begin: 3f8e3c51-6aca-4670-b86c-a213bf4706b0
77+
rle-decode("2 hs2q q2w2 "),
78+
"eq",
79+
" hsqq qww ",
80+
"run-length decode a string: multiple whitespace mixed in string",
81+
); # end: 3f8e3c51-6aca-4670-b86c-a213bf4706b0
82+
83+
cmp-ok( # begin: 29f721de-9aad-435f-ba37-7662df4fb551
84+
rle-decode("2a3b4c"),
85+
"eq",
86+
"aabbbcccc",
87+
"run-length decode a string: lowercase string",
88+
); # end: 29f721de-9aad-435f-ba37-7662df4fb551
89+
90+
cmp-ok( # begin: 2a762efd-8695-4e04-b0d6-9736899fbc16
91+
rle-decode(rle-encode("zzz ZZ zZ")),
92+
"eq",
93+
"zzz ZZ zZ",
94+
"encode and then decode: encode followed by decode gives original string",
95+
); # end: 2a762efd-8695-4e04-b0d6-9736899fbc16
96+
97+
done-testing;

0 commit comments

Comments
 (0)