diff --git a/config.json b/config.json index 80564110..722c32b0 100644 --- a/config.json +++ b/config.json @@ -401,6 +401,14 @@ "prerequisites": [], "difficulty": 1 }, + { + "slug": "matching-brackets", + "name": "Matching Brackets", + "uuid": "9f763780-2d60-4cf7-a548-86604338aa1a", + "practices": [], + "prerequisites": [], + "difficulty": 3 + }, { "slug": "matrix", "name": "Matrix", diff --git a/exercises/practice/matching-brackets/.docs/instructions.md b/exercises/practice/matching-brackets/.docs/instructions.md new file mode 100644 index 00000000..ea170842 --- /dev/null +++ b/exercises/practice/matching-brackets/.docs/instructions.md @@ -0,0 +1,5 @@ +# Instructions + +Given a string containing brackets `[]`, braces `{}`, parentheses `()`, or any combination thereof, verify that any and all pairs are matched and nested correctly. +Any other characters should be ignored. +For example, `"{what is (42)}?"` is balanced and `"[text}"` is not. diff --git a/exercises/practice/matching-brackets/.docs/introduction.md b/exercises/practice/matching-brackets/.docs/introduction.md new file mode 100644 index 00000000..0618221b --- /dev/null +++ b/exercises/practice/matching-brackets/.docs/introduction.md @@ -0,0 +1,8 @@ +# Introduction + +You're given the opportunity to write software for the Bracketeer™, an ancient but powerful mainframe. +The software that runs on it is written in a proprietary language. +Much of its syntax is familiar, but you notice _lots_ of brackets, braces and parentheses. +Despite the Bracketeer™ being powerful, it lacks flexibility. +If the source code has any unbalanced brackets, braces or parentheses, the Bracketeer™ crashes and must be rebooted. +To avoid such a scenario, you start writing code that can verify that brackets, braces, and parentheses are balanced before attempting to run it on the Bracketeer™. diff --git a/exercises/practice/matching-brackets/.meta/config.json b/exercises/practice/matching-brackets/.meta/config.json new file mode 100644 index 00000000..9a68e12a --- /dev/null +++ b/exercises/practice/matching-brackets/.meta/config.json @@ -0,0 +1,21 @@ +{ + "authors": [ + "glennj" + ], + "contributors": [ + "m-dango" + ], + "files": { + "solution": [ + "lib/MatchingBrackets.pm" + ], + "test": [ + "t/matching-brackets.t" + ], + "example": [ + ".meta/solutions/lib/MatchingBrackets.pm" + ] + }, + "blurb": "Make sure the brackets and braces all match.", + "source": "Ginna Baker" +} diff --git a/exercises/practice/matching-brackets/.meta/solutions/lib/MatchingBrackets.pm b/exercises/practice/matching-brackets/.meta/solutions/lib/MatchingBrackets.pm new file mode 100644 index 00000000..8418fcab --- /dev/null +++ b/exercises/practice/matching-brackets/.meta/solutions/lib/MatchingBrackets.pm @@ -0,0 +1,28 @@ +package MatchingBrackets; + +use strict; +use warnings; +use experimental qw; + +use Exporter qw; +our @EXPORT_OK = qw; + +sub has_matching_brackets ($text) { + my @stack = (); + my %pairs = ( ')' => '(', ']' => '[', '}' => '{' ); + + my $is_open = sub { grep { $_[0] eq $_ } values %pairs; }; + my $is_close = sub { exists $pairs{ $_[0] }; }; + + foreach my $char ( split '', $text ) { + if ( $is_open->($char) ) { + push @stack, $char; + } + elsif ( $is_close->($char) ) { + return 0 if @stack == 0 || pop(@stack) ne $pairs{$char}; + } + } + return @stack == 0; +} + +1; diff --git a/exercises/practice/matching-brackets/.meta/solutions/t/matching-brackets.t b/exercises/practice/matching-brackets/.meta/solutions/t/matching-brackets.t new file mode 120000 index 00000000..0c0664cc --- /dev/null +++ b/exercises/practice/matching-brackets/.meta/solutions/t/matching-brackets.t @@ -0,0 +1 @@ +../../../t/matching-brackets.t \ No newline at end of file diff --git a/exercises/practice/matching-brackets/.meta/template-data.yaml b/exercises/practice/matching-brackets/.meta/template-data.yaml new file mode 100644 index 00000000..e9e69825 --- /dev/null +++ b/exercises/practice/matching-brackets/.meta/template-data.yaml @@ -0,0 +1,37 @@ +subs: has_matching_brackets + +properties: + isPaired: + test: |- + use Data::Dmp; + sprintf(<<'END', dmp($case->{input}{value}), $case->{expected} ? ('T', 'True') : ('DF', 'Defined but False'), dmp($case->{description})); + is( + has_matching_brackets(%s), + %s, # %s + %s, + ); + END + +stub: |- + sub has_matching_brackets ($text) { + return undef; + } + +example: |- + sub has_matching_brackets ($text) { + my @stack = (); + my %pairs = ( ')' => '(', ']' => '[', '}' => '{' ); + + my $is_open = sub { grep {$_[0] eq $_} values %pairs; }; + my $is_close = sub { exists $pairs{$_[0]}; }; + + foreach my $char (split '', $text) { + if ($is_open->($char)) { + push @stack, $char; + } + elsif ($is_close->($char)) { + return 0 if @stack == 0 || pop(@stack) ne $pairs{$char}; + } + } + return @stack == 0; + } diff --git a/exercises/practice/matching-brackets/.meta/tests.toml b/exercises/practice/matching-brackets/.meta/tests.toml new file mode 100644 index 00000000..35a98a04 --- /dev/null +++ b/exercises/practice/matching-brackets/.meta/tests.toml @@ -0,0 +1,70 @@ +# 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. + +[81ec11da-38dd-442a-bcf9-3de7754609a5] +description = "paired square brackets" + +[287f0167-ac60-4b64-8452-a0aa8f4e5238] +description = "empty string" + +[6c3615a3-df01-4130-a731-8ef5f5d78dac] +description = "unpaired brackets" + +[9d414171-9b98-4cac-a4e5-941039a97a77] +description = "wrong ordered brackets" + +[f0f97c94-a149-4736-bc61-f2c5148ffb85] +description = "wrong closing bracket" + +[754468e0-4696-4582-a30e-534d47d69756] +description = "paired with whitespace" + +[ba84f6ee-8164-434a-9c3e-b02c7f8e8545] +description = "partially paired brackets" + +[3c86c897-5ff3-4a2b-ad9b-47ac3a30651d] +description = "simple nested brackets" + +[2d137f2c-a19e-4993-9830-83967a2d4726] +description = "several paired brackets" + +[2e1f7b56-c137-4c92-9781-958638885a44] +description = "paired and nested brackets" + +[84f6233b-e0f7-4077-8966-8085d295c19b] +description = "unopened closing brackets" + +[9b18c67d-7595-4982-b2c5-4cb949745d49] +description = "unpaired and nested brackets" + +[a0205e34-c2ac-49e6-a88a-899508d7d68e] +description = "paired and wrong nested brackets" + +[1d5c093f-fc84-41fb-8c2a-e052f9581602] +description = "paired and wrong nested brackets but innermost are correct" + +[ef47c21b-bcfd-4998-844c-7ad5daad90a8] +description = "paired and incomplete brackets" + +[a4675a40-a8be-4fc2-bc47-2a282ce6edbe] +description = "too many closing brackets" + +[a345a753-d889-4b7e-99ae-34ac85910d1a] +description = "early unexpected brackets" + +[21f81d61-1608-465a-b850-baa44c5def83] +description = "early mismatched brackets" + +[99255f93-261b-4435-a352-02bdecc9bdf2] +description = "math expression" + +[8e357d79-f302-469a-8515-2561877256a1] +description = "complex latex expression" diff --git a/exercises/practice/matching-brackets/lib/MatchingBrackets.pm b/exercises/practice/matching-brackets/lib/MatchingBrackets.pm new file mode 100644 index 00000000..aee3a017 --- /dev/null +++ b/exercises/practice/matching-brackets/lib/MatchingBrackets.pm @@ -0,0 +1,12 @@ +package MatchingBrackets; + +use v5.40; + +use Exporter qw; +our @EXPORT_OK = qw; + +sub has_matching_brackets ($text) { + return undef; +} + +1; diff --git a/exercises/practice/matching-brackets/t/matching-brackets.t b/exercises/practice/matching-brackets/t/matching-brackets.t new file mode 100755 index 00000000..42c2c19c --- /dev/null +++ b/exercises/practice/matching-brackets/t/matching-brackets.t @@ -0,0 +1,129 @@ +#!/usr/bin/env perl +use Test2::V0; + +use FindBin qw<$Bin>; +use lib "$Bin/../lib", "$Bin/../local/lib/perl5"; + +use MatchingBrackets qw; + +is( # begin: 81ec11da-38dd-442a-bcf9-3de7754609a5 + has_matching_brackets("[]"), + T, # True + "paired square brackets", +); # end: 81ec11da-38dd-442a-bcf9-3de7754609a5 + +is( # begin: 287f0167-ac60-4b64-8452-a0aa8f4e5238 + has_matching_brackets(""), + T, # True + "empty string", +); # end: 287f0167-ac60-4b64-8452-a0aa8f4e5238 + +is( # begin: 6c3615a3-df01-4130-a731-8ef5f5d78dac + has_matching_brackets("[["), + DF, # Defined but False + "unpaired brackets", +); # end: 6c3615a3-df01-4130-a731-8ef5f5d78dac + +is( # begin: 9d414171-9b98-4cac-a4e5-941039a97a77 + has_matching_brackets("}{"), + DF, # Defined but False + "wrong ordered brackets", +); # end: 9d414171-9b98-4cac-a4e5-941039a97a77 + +is( # begin: f0f97c94-a149-4736-bc61-f2c5148ffb85 + has_matching_brackets("{]"), + DF, # Defined but False + "wrong closing bracket", +); # end: f0f97c94-a149-4736-bc61-f2c5148ffb85 + +is( # begin: 754468e0-4696-4582-a30e-534d47d69756 + has_matching_brackets("{ }"), + T, # True + "paired with whitespace", +); # end: 754468e0-4696-4582-a30e-534d47d69756 + +is( # begin: ba84f6ee-8164-434a-9c3e-b02c7f8e8545 + has_matching_brackets("{[])"), + DF, # Defined but False + "partially paired brackets", +); # end: ba84f6ee-8164-434a-9c3e-b02c7f8e8545 + +is( # begin: 3c86c897-5ff3-4a2b-ad9b-47ac3a30651d + has_matching_brackets("{[]}"), + T, # True + "simple nested brackets", +); # end: 3c86c897-5ff3-4a2b-ad9b-47ac3a30651d + +is( # begin: 2d137f2c-a19e-4993-9830-83967a2d4726 + has_matching_brackets("{}[]"), + T, # True + "several paired brackets", +); # end: 2d137f2c-a19e-4993-9830-83967a2d4726 + +is( # begin: 2e1f7b56-c137-4c92-9781-958638885a44 + has_matching_brackets("([{}({}[])])"), + T, # True + "paired and nested brackets", +); # end: 2e1f7b56-c137-4c92-9781-958638885a44 + +is( # begin: 84f6233b-e0f7-4077-8966-8085d295c19b + has_matching_brackets("{[)][]}"), + DF, # Defined but False + "unopened closing brackets", +); # end: 84f6233b-e0f7-4077-8966-8085d295c19b + +is( # begin: 9b18c67d-7595-4982-b2c5-4cb949745d49 + has_matching_brackets("([{])"), + DF, # Defined but False + "unpaired and nested brackets", +); # end: 9b18c67d-7595-4982-b2c5-4cb949745d49 + +is( # begin: a0205e34-c2ac-49e6-a88a-899508d7d68e + has_matching_brackets("[({]})"), + DF, # Defined but False + "paired and wrong nested brackets", +); # end: a0205e34-c2ac-49e6-a88a-899508d7d68e + +is( # begin: 1d5c093f-fc84-41fb-8c2a-e052f9581602 + has_matching_brackets("[({}])"), + DF, # Defined but False + "paired and wrong nested brackets but innermost are correct", +); # end: 1d5c093f-fc84-41fb-8c2a-e052f9581602 + +is( # begin: ef47c21b-bcfd-4998-844c-7ad5daad90a8 + has_matching_brackets("{}["), + DF, # Defined but False + "paired and incomplete brackets", +); # end: ef47c21b-bcfd-4998-844c-7ad5daad90a8 + +is( # begin: a4675a40-a8be-4fc2-bc47-2a282ce6edbe + has_matching_brackets("[]]"), + DF, # Defined but False + "too many closing brackets", +); # end: a4675a40-a8be-4fc2-bc47-2a282ce6edbe + +is( # begin: a345a753-d889-4b7e-99ae-34ac85910d1a + has_matching_brackets(")()"), + DF, # Defined but False + "early unexpected brackets", +); # end: a345a753-d889-4b7e-99ae-34ac85910d1a + +is( # begin: 21f81d61-1608-465a-b850-baa44c5def83 + has_matching_brackets("{)()"), + DF, # Defined but False + "early mismatched brackets", +); # end: 21f81d61-1608-465a-b850-baa44c5def83 + +is( # begin: 99255f93-261b-4435-a352-02bdecc9bdf2 + has_matching_brackets("(((185 + 223.85) * 15) - 543)/2"), + T, # True + "math expression", +); # end: 99255f93-261b-4435-a352-02bdecc9bdf2 + +is( # begin: 8e357d79-f302-469a-8515-2561877256a1 + has_matching_brackets("\\left(\\begin{array}{cc} \\frac{1}{3} & x\\\\ \\mathrm{e}^{x} &... x^2 \\end{array}\\right)"), + T, # True + "complex latex expression", +); # end: 8e357d79-f302-469a-8515-2561877256a1 + +done_testing;