trackler 2.2.1.68 → 2.2.1.69
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/trackler/version.rb +1 -1
- data/problem-specifications/exercises/pov/description.md +0 -2
- data/problem-specifications/exercises/protein-translation/canonical-data.json +3 -3
- data/problem-specifications/exercises/simple-cipher/description.md +4 -6
- data/tracks/csharp/exercises/all-your-base/AllYourBaseTest.cs +7 -7
- data/tracks/csharp/exercises/dominoes/README.md +2 -11
- data/tracks/csharp/exercises/reverse-string/ReverseStringTest.cs +2 -2
- data/tracks/dart/CONTRIBUTING.md +7 -4
- data/tracks/delphi/config.json +23 -20
- data/tracks/ecmascript/CONTRIBUTING.md +3 -0
- data/tracks/ecmascript/README.md +2 -5
- data/tracks/fsharp/config.json +11 -0
- data/tracks/fsharp/exercises/Exercises.sln +6 -0
- data/tracks/fsharp/exercises/all-your-base/AllYourBaseTest.fs +7 -7
- data/tracks/fsharp/exercises/diamond/Diamond.fsproj +1 -0
- data/tracks/fsharp/exercises/diamond/DiamondTest.fs +111 -114
- data/tracks/fsharp/exercises/diamond/README.md +2 -1
- data/tracks/fsharp/exercises/dominoes/README.md +2 -5
- data/tracks/fsharp/exercises/palindrome-products/Example.fs +27 -19
- data/tracks/fsharp/exercises/palindrome-products/PalindromeProducts.fs +3 -3
- data/tracks/fsharp/exercises/palindrome-products/PalindromeProductsTest.fs +52 -49
- data/tracks/fsharp/exercises/reverse-string/ReverseStringTest.fs +2 -2
- data/tracks/fsharp/exercises/spiral-matrix/Example.fs +35 -0
- data/tracks/fsharp/exercises/spiral-matrix/Program.fs +1 -0
- data/tracks/fsharp/exercises/spiral-matrix/README.md +25 -0
- data/tracks/fsharp/exercises/spiral-matrix/SpiralMatrix.fs +3 -0
- data/tracks/fsharp/exercises/spiral-matrix/SpiralMatrix.fsproj +23 -0
- data/tracks/fsharp/exercises/spiral-matrix/SpiralMatrixTest.fs +47 -0
- data/tracks/fsharp/generators/Generators.fs +42 -5
- data/tracks/fsharp/generators/Properties/launchSettings.json +1 -2
- data/tracks/java/config.json +7 -2
- data/tracks/java/exercises/binary-search-tree/src/test/java/BinarySearchTreeTest.java +0 -1
- data/tracks/javascript/.eslintignore +1 -2
- data/tracks/javascript/exercises/wordy/example.js +2 -2
- data/tracks/perl6/exercises/atbash-cipher/AtbashCipher.pm6 +6 -0
- data/tracks/perl6/exercises/atbash-cipher/atbash-cipher.t +2 -9
- data/tracks/perl6/exercises/atbash-cipher/example.yaml +8 -9
- data/tracks/perl6/exercises/grade-school/Example.pm6 +1 -1
- data/tracks/perl6/exercises/grade-school/GradeSchool.pm6 +1 -1
- data/tracks/perl6/exercises/grade-school/example.yaml +19 -12
- data/tracks/perl6/exercises/grade-school/grade-school.t +19 -12
- data/tracks/python/.github/stale.yml +6 -3
- data/tracks/python/config.json +17 -0
- data/tracks/python/exercises/dot-dsl/.meta/hints.md +14 -0
- data/tracks/python/exercises/dot-dsl/README.md +52 -0
- data/tracks/python/exercises/dot-dsl/dot_dsl.py +27 -0
- data/tracks/python/exercises/dot-dsl/dot_dsl_test.py +117 -0
- data/tracks/python/exercises/dot-dsl/example.py +52 -0
- data/tracks/python/exercises/proverb/README.md +5 -2
- data/tracks/python/exercises/proverb/example.py +5 -5
- data/tracks/python/exercises/proverb/proverb.py +1 -1
- data/tracks/python/exercises/proverb/proverb_test.py +42 -52
- data/tracks/python/exercises/say/example.py +2 -2
- data/tracks/python/exercises/say/say_test.py +2 -2
- data/tracks/rust/README.md +2 -0
- data/tracks/rust/_test/count-ignores.sh +15 -8
- data/tracks/rust/bin/test-exercise +4 -1
- data/tracks/rust/exercises/perfect-numbers/.meta/ignore-count-ignores +6 -0
- data/tracks/rust/exercises/perfect-numbers/tests/perfect-numbers.rs +32 -22
- data/tracks/typescript/config.json +16 -0
- data/tracks/typescript/exercises/two-bucket/README.md +66 -0
- data/tracks/typescript/exercises/two-bucket/package.json +36 -0
- data/tracks/typescript/exercises/two-bucket/tsconfig.json +22 -0
- data/tracks/typescript/exercises/two-bucket/tslint.json +127 -0
- data/tracks/typescript/exercises/two-bucket/two-bucket.example.ts +97 -0
- data/tracks/typescript/exercises/two-bucket/two-bucket.test.ts +47 -0
- data/tracks/typescript/exercises/two-bucket/two-bucket.ts +0 -0
- data/tracks/typescript/exercises/two-bucket/yarn.lock +2624 -0
- metadata +23 -2
@@ -449,19 +449,35 @@ type OcrNumbers() =
|
|
449
449
|
|
450
450
|
type Pangram() =
|
451
451
|
inherit Exercise()
|
452
|
-
|
453
|
-
type
|
452
|
+
|
453
|
+
type PalindromeProducts() =
|
454
454
|
inherit Exercise()
|
455
455
|
|
456
|
-
let
|
456
|
+
let toFactors (value: obj) =
|
457
|
+
let jArray = value :?> JArray
|
458
|
+
let factors = jArray.ToObject<int list>()
|
459
|
+
sprintf "(%A, %A)" factors.[0] factors.[1]
|
460
|
+
|
461
|
+
let toPalindromeProducts (value: obj) =
|
462
|
+
let jObject = value :?> JObject
|
463
|
+
let palindromeValue = jObject.Value<int>("value")
|
464
|
+
let factors =
|
465
|
+
jObject.Value<JArray>("factors")
|
466
|
+
|> normalizeJArray
|
467
|
+
|> Seq.map toFactors
|
468
|
+
|> formatList
|
469
|
+
|
470
|
+
sprintf "(%d, %s)" palindromeValue factors
|
457
471
|
|
458
472
|
override this.RenderExpected (canonicalDataCase, key, value) =
|
459
473
|
value
|
460
|
-
|> Option.ofNonError
|
461
|
-
|> Option.map
|
474
|
+
|> Option.ofNonError
|
475
|
+
|> Option.map toPalindromeProducts
|
462
476
|
|> formatOption
|
463
477
|
|> parenthesizeOption
|
464
478
|
|
479
|
+
override this.PropertiesUsedAsSutParameter canonicalDataCase = ["input_min"; "input_max"]
|
480
|
+
|
465
481
|
type PascalsTriangle() =
|
466
482
|
inherit Exercise()
|
467
483
|
|
@@ -491,6 +507,18 @@ type PascalsTriangle() =
|
|
491
507
|
| _ -> base.IdentifierTypeAnnotation (canonicalDataCase, key, value)
|
492
508
|
|
493
509
|
override this.ToTestMethodBodyAssertTemplate canonicalDataCase = "AssertEqual"
|
510
|
+
|
511
|
+
type PerfectNumbers() =
|
512
|
+
inherit Exercise()
|
513
|
+
|
514
|
+
let toClassification value = string value |> String.humanize
|
515
|
+
|
516
|
+
override this.RenderExpected (canonicalDataCase, key, value) =
|
517
|
+
value
|
518
|
+
|> Option.ofNonError
|
519
|
+
|> Option.map toClassification
|
520
|
+
|> formatOption
|
521
|
+
|> parenthesizeOption
|
494
522
|
|
495
523
|
type PhoneNumber() =
|
496
524
|
inherit Exercise()
|
@@ -717,6 +745,15 @@ type RomanNumerals() =
|
|
717
745
|
type ScrabbleScore() =
|
718
746
|
inherit Exercise()
|
719
747
|
|
748
|
+
type SpiralMatrix() =
|
749
|
+
inherit Exercise()
|
750
|
+
|
751
|
+
override this.RenderExpected (canonicalDataCase, key, value) =
|
752
|
+
(value :?> JArray)
|
753
|
+
|> normalizeJArray
|
754
|
+
|> Seq.map formatValue
|
755
|
+
|> formatMultiLineList
|
756
|
+
|
720
757
|
type TwelveDays() =
|
721
758
|
inherit Exercise()
|
722
759
|
|
data/tracks/java/config.json
CHANGED
@@ -721,7 +721,12 @@
|
|
721
721
|
"core": true,
|
722
722
|
"difficulty": 6,
|
723
723
|
"slug": "binary-search",
|
724
|
-
"topics":
|
724
|
+
"topics": [
|
725
|
+
"recursion",
|
726
|
+
"arrays",
|
727
|
+
"searching",
|
728
|
+
"generics"
|
729
|
+
],
|
725
730
|
"unlocked_by": null,
|
726
731
|
"uuid": "50136dc3-caf7-4fa1-b7bd-0cba1bea9176"
|
727
732
|
},
|
@@ -1150,4 +1155,4 @@
|
|
1150
1155
|
],
|
1151
1156
|
"language": "Java",
|
1152
1157
|
"solution_pattern": "reference"
|
1153
|
-
}
|
1158
|
+
}
|
@@ -47,11 +47,11 @@ WordProblem.prototype.evaluate = function () {
|
|
47
47
|
var out = 0;
|
48
48
|
var m = this.matches;
|
49
49
|
|
50
|
-
if (m[1]
|
50
|
+
if ( (typeof m[1]) === 'string' && (typeof m[2]) === 'string' && (typeof m[3]) === 'string') {
|
51
51
|
out = this.operate(m[2], m[1], m[3]);
|
52
52
|
}
|
53
53
|
|
54
|
-
if (m[4]
|
54
|
+
if ( (typeof m[4]) === 'string' && (typeof m[5]) === 'string') {
|
55
55
|
out = this.operate(m[4], out, m[5]);
|
56
56
|
}
|
57
57
|
|
@@ -7,7 +7,7 @@ use JSON::Fast;
|
|
7
7
|
my Str:D $exercise := 'AtbashCipher';
|
8
8
|
my Version:D $version = v1;
|
9
9
|
my Str $module //= $exercise;
|
10
|
-
plan
|
10
|
+
plan 14;
|
11
11
|
|
12
12
|
use-ok $module or bail-out;
|
13
13
|
require ::($module);
|
@@ -22,14 +22,7 @@ if ::($exercise).^ver !~~ $version {
|
|
22
22
|
require ::($module) <&encode &decode>;
|
23
23
|
|
24
24
|
my $c-data = from-json $=pod.pop.contents;
|
25
|
-
for
|
26
|
-
my $test = .<description> ~~ 'encode' ?? 'encode' !! 'decode';
|
27
|
-
subtest $test => {
|
28
|
-
my @cases = |.<cases>;
|
29
|
-
plan +@cases;
|
30
|
-
is &::($test)(.<phrase>), |.<expected description> for @cases;
|
31
|
-
}
|
32
|
-
}
|
25
|
+
is .<phrase>.&::(.<property>), |.<expected description> for $c-data<cases>»<cases>».Array.flat;
|
33
26
|
|
34
27
|
=head2 Canonical Data
|
35
28
|
=begin code
|
@@ -1,16 +1,9 @@
|
|
1
1
|
exercise: AtbashCipher
|
2
2
|
version: 1
|
3
|
-
plan:
|
3
|
+
plan: 14
|
4
4
|
imports: '&encode &decode'
|
5
5
|
tests: |-
|
6
|
-
for
|
7
|
-
my $test = .<description> ~~ 'encode' ?? 'encode' !! 'decode';
|
8
|
-
subtest $test => {
|
9
|
-
my @cases = |.<cases>;
|
10
|
-
plan +@cases;
|
11
|
-
is &::($test)(.<phrase>), |.<expected description> for @cases;
|
12
|
-
}
|
13
|
-
}
|
6
|
+
is .<phrase>.&::(.<property>), |.<expected description> for $c-data<cases>»<cases>».Array.flat;
|
14
7
|
|
15
8
|
unit: module
|
16
9
|
example: |-
|
@@ -26,3 +19,9 @@ example: |-
|
|
26
19
|
.subst( /\W/, '', :g )
|
27
20
|
.trans( [ 'a'..'z' ] => ['a'..'z'].reverse );
|
28
21
|
}
|
22
|
+
stub: |-
|
23
|
+
sub encode ($phrase) is export {
|
24
|
+
}
|
25
|
+
|
26
|
+
sub decode ($phrase) is export {
|
27
|
+
}
|
@@ -1,26 +1,33 @@
|
|
1
1
|
exercise: GradeSchool
|
2
|
-
version:
|
3
|
-
plan:
|
2
|
+
version: 2
|
3
|
+
plan: 19
|
4
4
|
imports: Roster
|
5
5
|
tests: |-
|
6
6
|
subtest "Roster class methods", {
|
7
7
|
plan 3;
|
8
8
|
ok ::('Roster').can($_), $_ for <add-student list-grade list-all>;
|
9
9
|
}
|
10
|
+
|
10
11
|
my $roster = ::('Roster').new;
|
12
|
+
|
11
13
|
ok $roster.?add-student(:name('Jim'), :2grade), 'Add Jim to grade 2';
|
12
|
-
|
14
|
+
cmp-ok $roster.?list-grade(2), '~~', <Jim>, 'List grade 2';
|
15
|
+
|
13
16
|
ok $roster.?add-student(:name('Zoe'), :2grade), 'Add Zoe to grade 2';
|
14
17
|
ok $roster.?add-student(:name('Barb'), :1grade), 'Add Barb to grade 1';
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
18
|
+
cmp-ok $roster.?list-grade(2), '~~', <Jim Zoe>, 'List grade 2';
|
19
|
+
cmp-ok $roster.?list-grade(1), '~~', <Barb>, 'List grade 1';
|
20
|
+
|
21
|
+
cmp-ok $roster.?list-all, '~~', ('Grade 1', <Barb>, 'Grade 2', <Jim Zoe>), 'List all';
|
22
|
+
|
23
|
+
ok $roster.?add-student(:name($_), :1grade), "Add $_ to grade 1" for <Charlie Anna>;
|
24
|
+
ok $roster.?add-student(:name('Alex'), :2grade), 'Add Alex to grade 2';
|
25
|
+
ok $roster.?add-student(:name($_), :3grade), "Add $_ to grade 3" for <Tom Dick Harry>;
|
26
|
+
|
27
|
+
cmp-ok $roster.?list-grade(1), '~~', <Anna Barb Charlie>, 'List grade 1';
|
28
|
+
cmp-ok $roster.?list-grade(2), '~~', <Alex Jim Zoe>, 'List grade 2';
|
29
|
+
cmp-ok $roster.?list-grade(3), '~~', <Dick Harry Tom>, 'List grade 3';
|
30
|
+
cmp-ok $roster.?list-all, '~~', ('Grade 1', <Anna Barb Charlie>, 'Grade 2', <Alex Jim Zoe>, 'Grade 3', <Dick Harry Tom>), 'List all';
|
24
31
|
|
25
32
|
unit: module
|
26
33
|
example: |-
|
@@ -4,9 +4,9 @@ use Test;
|
|
4
4
|
use lib $?FILE.IO.dirname;
|
5
5
|
|
6
6
|
my Str:D $exercise := 'GradeSchool';
|
7
|
-
my Version:D $version =
|
7
|
+
my Version:D $version = v2;
|
8
8
|
my Str $module //= $exercise;
|
9
|
-
plan
|
9
|
+
plan 19;
|
10
10
|
|
11
11
|
use-ok $module or bail-out;
|
12
12
|
require ::($module);
|
@@ -24,19 +24,26 @@ subtest "Roster class methods", {
|
|
24
24
|
plan 3;
|
25
25
|
ok ::('Roster').can($_), $_ for <add-student list-grade list-all>;
|
26
26
|
}
|
27
|
+
|
27
28
|
my $roster = ::('Roster').new;
|
29
|
+
|
28
30
|
ok $roster.?add-student(:name('Jim'), :2grade), 'Add Jim to grade 2';
|
29
|
-
|
31
|
+
cmp-ok $roster.?list-grade(2), '~~', <Jim>, 'List grade 2';
|
32
|
+
|
30
33
|
ok $roster.?add-student(:name('Zoe'), :2grade), 'Add Zoe to grade 2';
|
31
34
|
ok $roster.?add-student(:name('Barb'), :1grade), 'Add Barb to grade 1';
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
35
|
+
cmp-ok $roster.?list-grade(2), '~~', <Jim Zoe>, 'List grade 2';
|
36
|
+
cmp-ok $roster.?list-grade(1), '~~', <Barb>, 'List grade 1';
|
37
|
+
|
38
|
+
cmp-ok $roster.?list-all, '~~', ('Grade 1', <Barb>, 'Grade 2', <Jim Zoe>), 'List all';
|
39
|
+
|
40
|
+
ok $roster.?add-student(:name($_), :1grade), "Add $_ to grade 1" for <Charlie Anna>;
|
41
|
+
ok $roster.?add-student(:name('Alex'), :2grade), 'Add Alex to grade 2';
|
42
|
+
ok $roster.?add-student(:name($_), :3grade), "Add $_ to grade 3" for <Tom Dick Harry>;
|
43
|
+
|
44
|
+
cmp-ok $roster.?list-grade(1), '~~', <Anna Barb Charlie>, 'List grade 1';
|
45
|
+
cmp-ok $roster.?list-grade(2), '~~', <Alex Jim Zoe>, 'List grade 2';
|
46
|
+
cmp-ok $roster.?list-grade(3), '~~', <Dick Harry Tom>, 'List grade 3';
|
47
|
+
cmp-ok $roster.?list-all, '~~', ('Grade 1', <Anna Barb Charlie>, 'Grade 2', <Alex Jim Zoe>, 'Grade 3', <Dick Harry Tom>), 'List all';
|
41
48
|
|
42
49
|
INIT { $module = 'Example' if %*ENV<EXERCISM> }
|
@@ -1,16 +1,19 @@
|
|
1
1
|
# Number of days of inactivity before an issue becomes stale
|
2
|
-
daysUntilStale:
|
2
|
+
daysUntilStale: 21
|
3
3
|
# Number of days of inactivity before a stale issue is closed
|
4
4
|
daysUntilClose: 7
|
5
5
|
# Issues with these labels will never be considered stale
|
6
6
|
exemptLabels:
|
7
7
|
- discussion
|
8
8
|
- pinned
|
9
|
+
- epic
|
10
|
+
- enhancement
|
11
|
+
- beginner friendly
|
9
12
|
# Label to use when marking an issue as stale
|
10
|
-
staleLabel:
|
13
|
+
staleLabel: abandoned
|
11
14
|
# Comment to post when marking an issue as stale. Set to `false` to disable
|
12
15
|
markComment: >
|
13
|
-
This issue has been automatically marked as `
|
16
|
+
This issue has been automatically marked as `abandoned` because it has not had
|
14
17
|
recent activity. It will be closed if no further activity occurs. Thank you
|
15
18
|
for your contributions.
|
16
19
|
# Comment to post when closing a stale issue. Set to `false` to disable
|
data/tracks/python/config.json
CHANGED
@@ -763,6 +763,23 @@
|
|
763
763
|
"object_oriented_programming"
|
764
764
|
]
|
765
765
|
},
|
766
|
+
{
|
767
|
+
"uuid": "a9c2fbda-a1e4-42dd-842f-4de5bb361b91",
|
768
|
+
"slug": "dot-dsl",
|
769
|
+
"core": false,
|
770
|
+
"unlocked_by": null,
|
771
|
+
"difficulty": 5,
|
772
|
+
"topics": [
|
773
|
+
"equality",
|
774
|
+
"classes",
|
775
|
+
"lists",
|
776
|
+
"domain_specific_languages",
|
777
|
+
"graphs",
|
778
|
+
"object_oriented_programming",
|
779
|
+
"test_driven_development",
|
780
|
+
"transforming"
|
781
|
+
]
|
782
|
+
},
|
766
783
|
{
|
767
784
|
"uuid": "dc6e61a2-e9b9-4406-ba5c-188252afbba1",
|
768
785
|
"slug": "transpose",
|
@@ -0,0 +1,14 @@
|
|
1
|
+
## Description of DSL
|
2
|
+
|
3
|
+
A graph, in this DSL, is an object of type `Graph`, taking a list of one
|
4
|
+
or more
|
5
|
+
|
6
|
+
+ attributes
|
7
|
+
+ nodes
|
8
|
+
+ edges
|
9
|
+
|
10
|
+
described as tuples.
|
11
|
+
|
12
|
+
The implementations of `Node` and `Edge` provided in `dot_dsl.py`.
|
13
|
+
|
14
|
+
Observe the test cases in `dot_dsl_test.py` to understand the DSL's design.
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# Dot Dsl
|
2
|
+
|
3
|
+
Write a Domain Specific Language similar to the Graphviz dot language.
|
4
|
+
|
5
|
+
A [Domain Specific Language
|
6
|
+
(DSL)](https://en.wikipedia.org/wiki/Domain-specific_language) is a
|
7
|
+
small language optimized for a specific domain.
|
8
|
+
|
9
|
+
For example the dot language of [Graphviz](http://graphviz.org) allows
|
10
|
+
you to write a textual description of a graph which is then transformed
|
11
|
+
into a picture by one of the graphviz tools (such as `dot`). A simple
|
12
|
+
graph looks like this:
|
13
|
+
|
14
|
+
graph {
|
15
|
+
graph [bgcolor="yellow"]
|
16
|
+
a [color="red"]
|
17
|
+
b [color="blue"]
|
18
|
+
a -- b [color="green"]
|
19
|
+
}
|
20
|
+
|
21
|
+
Putting this in a file `example.dot` and running `dot example.dot -T png
|
22
|
+
-o example.png` creates an image `example.png` with red and blue circle
|
23
|
+
connected by a green line on a yellow background.
|
24
|
+
|
25
|
+
Create a DSL similar to the dot language.
|
26
|
+
|
27
|
+
## Description of DSL
|
28
|
+
|
29
|
+
A graph, in this DSL, is an object of type `Graph`, taking a list of one
|
30
|
+
or more
|
31
|
+
|
32
|
+
+ attributes
|
33
|
+
+ nodes
|
34
|
+
+ edges
|
35
|
+
|
36
|
+
described as tuples.
|
37
|
+
|
38
|
+
The implementations of `Node` and `Edge` provided in `dot_dsl.py`.
|
39
|
+
|
40
|
+
Observe the test cases in `dot_dsl_test.py` to understand the DSL's design.
|
41
|
+
|
42
|
+
## Submitting Exercises
|
43
|
+
|
44
|
+
Note that, when trying to submit an exercise, make sure the solution is in the `exercism/python/<exerciseName>` directory.
|
45
|
+
|
46
|
+
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
|
47
|
+
|
48
|
+
For more detailed information about running tests, code style and linting, please see the [help page](http://exercism.io/languages/python).
|
49
|
+
|
50
|
+
## Submitting Incomplete Solutions
|
51
|
+
|
52
|
+
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
|
@@ -0,0 +1,27 @@
|
|
1
|
+
NODE, EDGE, ATTR = range(3)
|
2
|
+
|
3
|
+
|
4
|
+
class Node(object):
|
5
|
+
def __init__(self, name, attrs={}):
|
6
|
+
self.name = name
|
7
|
+
self.attrs = attrs
|
8
|
+
|
9
|
+
def __eq__(self, other):
|
10
|
+
return self.name == other.name and self.attrs == other.attrs
|
11
|
+
|
12
|
+
|
13
|
+
class Edge(object):
|
14
|
+
def __init__(self, src, dst, attrs={}):
|
15
|
+
self.src = src
|
16
|
+
self.dst = dst
|
17
|
+
self.attrs = attrs
|
18
|
+
|
19
|
+
def __eq__(self, other):
|
20
|
+
return (self.src == other.src and
|
21
|
+
self.dst == other.dst and
|
22
|
+
self.attrs == other.attrs)
|
23
|
+
|
24
|
+
|
25
|
+
class Graph(object):
|
26
|
+
def __init__(self, data=[]):
|
27
|
+
pass
|
@@ -0,0 +1,117 @@
|
|
1
|
+
import unittest
|
2
|
+
|
3
|
+
from dot_dsl import Graph, Node, Edge, NODE, EDGE, ATTR
|
4
|
+
|
5
|
+
|
6
|
+
class DotDslTest(unittest.TestCase):
|
7
|
+
def test_empty_graph(self):
|
8
|
+
g = Graph()
|
9
|
+
|
10
|
+
self.assertEqual(g.nodes, [])
|
11
|
+
self.assertEqual(g.edges, [])
|
12
|
+
self.assertEqual(g.attrs, {})
|
13
|
+
|
14
|
+
def test_graph_with_one_node(self):
|
15
|
+
g = Graph([
|
16
|
+
(NODE, "a", {})
|
17
|
+
])
|
18
|
+
|
19
|
+
self.assertEqual(g.nodes, [Node("a")])
|
20
|
+
self.assertEqual(g.edges, [])
|
21
|
+
self.assertEqual(g.attrs, {})
|
22
|
+
|
23
|
+
def test_graph_with_one_node_with_keywords(self):
|
24
|
+
g = Graph([
|
25
|
+
(NODE, "a", {"color": "green"})
|
26
|
+
])
|
27
|
+
|
28
|
+
self.assertEqual(g.nodes, [Node("a", {"color": "green"})])
|
29
|
+
self.assertEqual(g.edges, [])
|
30
|
+
self.assertEqual(g.attrs, {})
|
31
|
+
|
32
|
+
def test_graph_with_one_edge(self):
|
33
|
+
g = Graph([
|
34
|
+
(EDGE, "a", "b", {})
|
35
|
+
])
|
36
|
+
|
37
|
+
self.assertEqual(g.nodes, [])
|
38
|
+
self.assertEqual(g.edges, [Edge("a", "b", {})])
|
39
|
+
self.assertEqual(g.attrs, {})
|
40
|
+
|
41
|
+
def test_graph_with_one_attribute(self):
|
42
|
+
g = Graph([
|
43
|
+
(ATTR, "foo", "1")
|
44
|
+
])
|
45
|
+
|
46
|
+
self.assertEqual(g.nodes, [])
|
47
|
+
self.assertEqual(g.edges, [])
|
48
|
+
self.assertEqual(g.attrs, {"foo": "1"})
|
49
|
+
|
50
|
+
def test_graph_with_attributes(self):
|
51
|
+
g = Graph([
|
52
|
+
(ATTR, "foo", "1"),
|
53
|
+
(ATTR, "title", "Testing Attrs"),
|
54
|
+
(NODE, "a", {"color": "green"}),
|
55
|
+
(NODE, "c", {}),
|
56
|
+
(NODE, "b", {"label", "Beta!"}),
|
57
|
+
(EDGE, "b", "c", {}),
|
58
|
+
(EDGE, "a", "b", {"color": "blue"}),
|
59
|
+
(ATTR, "bar", "true")
|
60
|
+
])
|
61
|
+
|
62
|
+
self.assertEqual(g.nodes, [Node("a", {"color": "green"}),
|
63
|
+
Node("c", {}),
|
64
|
+
Node("b", {"label", "Beta!"})])
|
65
|
+
self.assertEqual(g.edges, [Edge("b", "c", {}),
|
66
|
+
Edge("a", "b", {"color": "blue"})])
|
67
|
+
self.assertEqual(g.attrs, {
|
68
|
+
"foo": "1",
|
69
|
+
"title": "Testing Attrs",
|
70
|
+
"bar": "true"
|
71
|
+
})
|
72
|
+
|
73
|
+
def test_malformed_graph(self):
|
74
|
+
with self.assertRaises(TypeError):
|
75
|
+
Graph(1)
|
76
|
+
|
77
|
+
with self.assertRaises(TypeError):
|
78
|
+
Graph("problematic")
|
79
|
+
|
80
|
+
def test_malformed_graph_item(self):
|
81
|
+
with self.assertRaises(TypeError):
|
82
|
+
Graph([
|
83
|
+
()
|
84
|
+
])
|
85
|
+
|
86
|
+
with self.assertRaises(TypeError):
|
87
|
+
Graph([
|
88
|
+
(ATTR, )
|
89
|
+
])
|
90
|
+
|
91
|
+
def test_malformed_attr(self):
|
92
|
+
with self.assertRaises(ValueError):
|
93
|
+
Graph([
|
94
|
+
(ATTR, 1, 2, 3)
|
95
|
+
])
|
96
|
+
|
97
|
+
def test_malformed_node(self):
|
98
|
+
with self.assertRaises(ValueError):
|
99
|
+
Graph([
|
100
|
+
(NODE, 1, 2, 3)
|
101
|
+
])
|
102
|
+
|
103
|
+
def test_malformed_EDGE(self):
|
104
|
+
with self.assertRaises(ValueError):
|
105
|
+
Graph([
|
106
|
+
(EDGE, 1, 2)
|
107
|
+
])
|
108
|
+
|
109
|
+
def test_unknown_item(self):
|
110
|
+
with self.assertRaises(ValueError):
|
111
|
+
Graph([
|
112
|
+
(99, 1, 2)
|
113
|
+
])
|
114
|
+
|
115
|
+
|
116
|
+
if __name__ == '__main__':
|
117
|
+
unittest.main()
|
@@ -0,0 +1,52 @@
|
|
1
|
+
NODE, EDGE, ATTR = range(3)
|
2
|
+
|
3
|
+
|
4
|
+
class Node(object):
|
5
|
+
def __init__(self, name, attrs={}):
|
6
|
+
self.name = name
|
7
|
+
self.attrs = attrs
|
8
|
+
|
9
|
+
def __eq__(self, other):
|
10
|
+
return self.name == other.name and self.attrs == other.attrs
|
11
|
+
|
12
|
+
|
13
|
+
class Edge(object):
|
14
|
+
def __init__(self, src, dst, attrs={}):
|
15
|
+
self.src = src
|
16
|
+
self.dst = dst
|
17
|
+
self.attrs = attrs
|
18
|
+
|
19
|
+
def __eq__(self, other):
|
20
|
+
return (self.src == other.src and
|
21
|
+
self.dst == other.dst and
|
22
|
+
self.attrs == other.attrs)
|
23
|
+
|
24
|
+
|
25
|
+
class Graph(object):
|
26
|
+
def __init__(self, data=[]):
|
27
|
+
self.nodes = []
|
28
|
+
self.edges = []
|
29
|
+
self.attrs = {}
|
30
|
+
|
31
|
+
if not isinstance(data, list):
|
32
|
+
raise TypeError("Graph data malformed")
|
33
|
+
|
34
|
+
for item in data:
|
35
|
+
if len(item) < 3:
|
36
|
+
raise TypeError("Graph item incomplete")
|
37
|
+
|
38
|
+
type_ = item[0]
|
39
|
+
if type_ == ATTR:
|
40
|
+
if len(item) != 3:
|
41
|
+
raise ValueError("ATTR malformed")
|
42
|
+
self.attrs[item[1]] = item[2]
|
43
|
+
elif type_ == NODE:
|
44
|
+
if len(item) != 3:
|
45
|
+
raise ValueError("NODE malformed")
|
46
|
+
self.nodes.append(Node(item[1], item[2]))
|
47
|
+
elif type_ == EDGE:
|
48
|
+
if len(item) != 4:
|
49
|
+
raise ValueError("EDGE malformed")
|
50
|
+
self.edges.append(Edge(item[1], item[2], item[3]))
|
51
|
+
else:
|
52
|
+
raise ValueError("Unknown item {}".format(item[0]))
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
For want of a horseshoe nail, a kingdom was lost, or so the saying goes.
|
4
4
|
|
5
|
-
|
5
|
+
Given a list of inputs, generate the relevant proverb. For example, given the list `["nail", "shoe", "horse", "rider", "message", "battle", "kingdom"]`, you will output the full text of this proverbial rhyme:
|
6
6
|
|
7
7
|
```text
|
8
8
|
For want of a nail the shoe was lost.
|
@@ -11,8 +11,11 @@ For want of a horse the rider was lost.
|
|
11
11
|
For want of a rider the message was lost.
|
12
12
|
For want of a message the battle was lost.
|
13
13
|
For want of a battle the kingdom was lost.
|
14
|
-
And all for the want of a
|
14
|
+
And all for the want of a nail.
|
15
15
|
```
|
16
|
+
|
17
|
+
Note that the list of inputs may vary; your solution should be able to handle lists of arbitrary length and content. No line of the output text should be a static, unchanging string; all should vary according to the input given.
|
18
|
+
|
16
19
|
## Submitting Exercises
|
17
20
|
|
18
21
|
Note that, when trying to submit an exercise, make sure the solution is in the `exercism/python/<exerciseName>` directory.
|
@@ -1,7 +1,7 @@
|
|
1
|
-
def proverb(
|
1
|
+
def proverb(rhyme_items):
|
2
|
+
if not rhyme_items:
|
3
|
+
return ""
|
2
4
|
phrases = ['For want of a {0} the {1} was lost.'.format(el1, el2)
|
3
|
-
for el1, el2 in zip(
|
4
|
-
|
5
|
-
phrases.append('And all for the want of a {0}{1}.'.format(qualifier,
|
6
|
-
itens[0]))
|
5
|
+
for el1, el2 in zip(rhyme_items, rhyme_items[1:])]
|
6
|
+
phrases.append('And all for the want of a {0}.'.format(rhyme_items[0]))
|
7
7
|
return '\n'.join(phrases)
|
@@ -1,2 +1,2 @@
|
|
1
|
-
def proverb(
|
1
|
+
def proverb(rhyme_items):
|
2
2
|
pass
|