trackler 2.2.1.15 → 2.2.1.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/lib/trackler/version.rb +1 -1
  3. data/problem-specifications/TOPICS.txt +8 -1
  4. data/tracks/c/docs/SNIPPET.txt +11 -0
  5. data/tracks/ceylon/docs/SNIPPET.txt +3 -0
  6. data/tracks/clojure/docs/SNIPPET.txt +5 -0
  7. data/tracks/csharp/build.cake +23 -16
  8. data/tracks/csharp/build.ps1 +1 -1
  9. data/tracks/csharp/build.sh +1 -1
  10. data/tracks/csharp/exercises/pangram/PangramTest.cs +9 -18
  11. data/tracks/csharp/generators/.gitignore +2 -1
  12. data/tracks/csharp/generators/Exercise.cs +11 -12
  13. data/tracks/csharp/generators/Exercises/AllYourBase.cs +2 -2
  14. data/tracks/csharp/generators/Exercises/Allergies.cs +1 -1
  15. data/tracks/csharp/generators/Exercises/Alphametics.cs +1 -1
  16. data/tracks/csharp/generators/Exercises/Anagram.cs +1 -1
  17. data/tracks/csharp/generators/Exercises/BeerSong.cs +1 -1
  18. data/tracks/csharp/generators/Exercises/BinarySearch.cs +1 -1
  19. data/tracks/csharp/generators/Exercises/BookStore.cs +1 -1
  20. data/tracks/csharp/generators/Exercises/BracketPush.cs +1 -1
  21. data/tracks/csharp/generators/Exercises/CollatzConjecture.cs +2 -2
  22. data/tracks/csharp/generators/Exercises/CryptoSquare.cs +1 -1
  23. data/tracks/csharp/generators/Exercises/FoodChain.cs +1 -1
  24. data/tracks/csharp/generators/Exercises/Gigasecond.cs +1 -1
  25. data/tracks/csharp/generators/Exercises/Hamming.cs +1 -1
  26. data/tracks/csharp/generators/Exercises/House.cs +1 -1
  27. data/tracks/csharp/generators/Exercises/Leap.cs +1 -1
  28. data/tracks/csharp/generators/Exercises/Luhn.cs +1 -1
  29. data/tracks/csharp/generators/Exercises/NthPrime.cs +1 -1
  30. data/tracks/csharp/generators/Exercises/Pangram.cs +1 -11
  31. data/tracks/csharp/generators/Exercises/PerfectNumbers.cs +1 -1
  32. data/tracks/csharp/generators/Exercises/PhoneNumber.cs +1 -1
  33. data/tracks/csharp/generators/Exercises/RailFenceCipher.cs +1 -1
  34. data/tracks/csharp/generators/Exercises/RnaTranscription.cs +1 -1
  35. data/tracks/csharp/generators/Exercises/RomanNumerals.cs +1 -1
  36. data/tracks/csharp/generators/Exercises/RunLengthEncoding.cs +1 -1
  37. data/tracks/csharp/generators/Exercises/Say.cs +2 -7
  38. data/tracks/csharp/generators/Exercises/SecretHandshake.cs +1 -1
  39. data/tracks/csharp/generators/Exercises/Sieve.cs +1 -1
  40. data/tracks/csharp/generators/Exercises/SpaceAge.cs +1 -1
  41. data/tracks/csharp/generators/Exercises/SumOfMultiples.cs +1 -1
  42. data/tracks/csharp/generators/Exercises/Transpose.cs +1 -1
  43. data/tracks/csharp/generators/Exercises/WordCount.cs +1 -1
  44. data/tracks/csharp/generators/Exercises/Wordy.cs +1 -1
  45. data/tracks/csharp/generators/Output/FormattingExtensions.cs +4 -2
  46. data/tracks/csharp/generators/Output/ValueFormatter.cs +1 -1
  47. data/tracks/delphi/docs/SNIPPET.txt +6 -13
  48. data/tracks/ecmascript/docs/SNIPPET.txt +7 -0
  49. data/tracks/elisp/docs/SNIPPET.txt +13 -0
  50. data/tracks/elixir/config.json +2 -2
  51. data/tracks/elixir/docs/SNIPPET.txt +10 -0
  52. data/tracks/erlang/docs/SNIPPET.txt +8 -0
  53. data/tracks/fsharp/docs/SNIPPET.txt +3 -0
  54. data/tracks/go/config.json +174 -174
  55. data/tracks/go/config/maintainers.json +6 -9
  56. data/tracks/go/docs/SNIPPET.txt +6 -0
  57. data/tracks/groovy/config.json +4 -4
  58. data/tracks/groovy/docs/SNIPPET.txt +12 -0
  59. data/tracks/haskell/config.json +2 -0
  60. data/tracks/haskell/docs/SNIPPET.txt +4 -0
  61. data/tracks/java/docs/SNIPPET.txt +7 -0
  62. data/tracks/java/exercises/robot-simulator/src/example/java/GridPosition.java +21 -16
  63. data/tracks/java/exercises/robot-simulator/src/test/java/RobotTest.java +24 -24
  64. data/tracks/javascript/docs/SNIPPET.txt +10 -0
  65. data/tracks/kotlin/docs/SNIPPET.txt +3 -0
  66. data/tracks/kotlin/exercises/change/src/example/kotlin/ChangeCalculator.kt +35 -0
  67. data/tracks/kotlin/exercises/change/src/test/kotlin/ChangeCalculatorTest.kt +105 -0
  68. data/tracks/lfe/docs/SNIPPET.txt +12 -0
  69. data/tracks/lua/docs/SNIPPET.txt +7 -0
  70. data/tracks/mips/docs/SNIPPET.txt +23 -0
  71. data/tracks/ocaml/docs/SNIPPET.txt +1 -0
  72. data/tracks/purescript/config.json +1 -1
  73. data/tracks/purescript/docs/SNIPPET.txt +8 -0
  74. data/tracks/python/exercises/saddle-points/README.md +1 -1
  75. data/tracks/racket/docs/SNIPPET.txt +6 -0
  76. data/tracks/ruby/docs/SNIPPET.txt +5 -0
  77. data/tracks/rust/docs/SNIPPET.txt +3 -0
  78. data/tracks/scala/docs/SNIPPET.txt +4 -0
  79. data/tracks/scala/exercises/ocr-numbers/example.scala +17 -11
  80. data/tracks/scala/exercises/ocr-numbers/src/test/scala/OcrNumbersTest.scala +153 -0
  81. data/tracks/scala/exercises/pig-latin/example.scala +11 -6
  82. data/tracks/scala/exercises/pig-latin/src/test/scala/PigLatinTest.scala +80 -10
  83. data/tracks/scala/testgen/src/main/scala/OcrNumbersTestGenerator.scala +34 -0
  84. data/tracks/scala/testgen/src/main/scala/PigLatinTestGenerator.scala +16 -0
  85. data/tracks/scheme/docs/SNIPPET.txt +7 -0
  86. data/tracks/sml/bin/generate +277 -0
  87. data/tracks/sml/config.json +30 -0
  88. data/tracks/sml/docs/ABOUT.md +32 -0
  89. data/tracks/sml/docs/LEARNING.md +37 -0
  90. data/tracks/sml/docs/SNIPPET.txt +3 -0
  91. data/tracks/sml/exercises/bob/README.md +48 -0
  92. data/tracks/sml/exercises/bob/bob.sml +2 -0
  93. data/tracks/sml/exercises/bob/example.sml +22 -0
  94. data/tracks/sml/exercises/bob/test.sml +87 -0
  95. data/tracks/sml/exercises/bob/testlib.sml +159 -0
  96. data/tracks/sml/exercises/difference-of-squares/README.md +49 -0
  97. data/tracks/sml/exercises/difference-of-squares/difference-of-squares.sml +8 -0
  98. data/tracks/sml/exercises/difference-of-squares/example.sml +11 -0
  99. data/tracks/sml/exercises/difference-of-squares/test.sml +45 -0
  100. data/tracks/sml/exercises/difference-of-squares/testlib.sml +159 -0
  101. data/tracks/sml/exercises/hello-world/README.md +51 -0
  102. data/tracks/sml/exercises/hello-world/example.sml +1 -0
  103. data/tracks/sml/exercises/hello-world/hello-world.sml +2 -0
  104. data/tracks/sml/exercises/hello-world/test.sml +15 -0
  105. data/tracks/sml/exercises/hello-world/testlib.sml +159 -0
  106. data/tracks/sml/lib/testlib.sml +159 -0
  107. data/tracks/swift/docs/SNIPPET.txt +9 -0
  108. data/tracks/typescript/docs/SNIPPET.txt +8 -0
  109. metadata +53 -6
  110. data/tracks/csharp/generators/Generators.csproj.user +0 -6
  111. data/tracks/kotlin/exercises/change/src/example/kotlin/Change.kt +0 -70
  112. data/tracks/kotlin/exercises/change/src/test/kotlin/ChangeTest.kt +0 -55
  113. data/tracks/scala/exercises/ocr-numbers/src/test/scala/OcrTest.scala +0 -138
@@ -0,0 +1,16 @@
1
+ import java.io.File
2
+
3
+ import testgen.TestSuiteBuilder._
4
+ import testgen._
5
+
6
+ object PigLatinTestGenerator {
7
+ def main(args: Array[String]): Unit = {
8
+ val file = new File("src/main/resources/pig-latin.json")
9
+
10
+ val code =
11
+ TestSuiteBuilder.build(file, fromLabeledTest("input"))
12
+ println(s"-------------")
13
+ println(code)
14
+ println(s"-------------")
15
+ }
16
+ }
@@ -0,0 +1,7 @@
1
+ (define-module (hello-world)
2
+ #:export (hello))
3
+
4
+ (define hello
5
+ (lambda* (#:optional name)
6
+ (let ((target (or name "World")))
7
+ (string-concatenate (list "Hello, " target "!")))))
@@ -0,0 +1,277 @@
1
+ #!/usr/bin/env python
2
+
3
+ import json
4
+ import re
5
+ import shutil
6
+
7
+ from pathlib import Path
8
+ from collections import OrderedDict
9
+ from urllib.request import urlopen, HTTPError
10
+ from http import HTTPStatus
11
+
12
+
13
+ canonical_data_url = 'https://raw.githubusercontent.com/exercism' + \
14
+ '/problem-specifications/master' + \
15
+ '/exercises/%s/canonical-data.json'
16
+
17
+
18
+ def fetch(exercise):
19
+ try:
20
+ with urlopen(canonical_data_url % exercise) as response:
21
+ return json.loads(response.read().decode('utf8'))
22
+ except HTTPError as e:
23
+ if e.status == HTTPStatus.NOT_FOUND.value:
24
+ msg = "Exercise '%s' doesn't exist." % exercise
25
+ else:
26
+ msg = 'Unexpected error, please try again later.'
27
+ raise Exception(msg)
28
+
29
+
30
+ def load(exercise):
31
+ root = Path(__file__).absolute().parent.parent.parent
32
+ path = root / 'problem-specifications/exercises'
33
+ with (path / exercise / 'canonical-data.json').open() as f:
34
+ return json.loads(f.read())
35
+
36
+
37
+ def indent(n, s):
38
+ return s.rjust(len(s) + n, ' ')
39
+
40
+
41
+ def camelize(s):
42
+ return re.sub('[-_](\w)', lambda x: x.group(1).upper(), s)
43
+
44
+
45
+ def sml_type(value):
46
+ if value is None:
47
+ return 'option'
48
+ if isinstance(value, list):
49
+ if not value:
50
+ return "'a list"
51
+ s = {sml_type(v)
52
+ for v in value
53
+ # }
54
+ if not (isinstance(v, list) and not v)}
55
+ s.discard('exn')
56
+ if not s or len(s) > 2:
57
+ raise Exception('Failed to infer a proper type')
58
+ if len(s) == 1:
59
+ return '%s list' % s.pop()
60
+ if 'option' not in s:
61
+ raise Exception('Failed to infer a proper type, got multiple types', value, s)
62
+ s.remove('option')
63
+ return '%s option list' % s.pop()
64
+ if isinstance(value, dict):
65
+ if 'error' in value:
66
+ return 'exn'
67
+ return '{%s}' % ', '.join(
68
+ '%s: %s' % (key, sml_type(value[key]))
69
+ for key in sorted(value.keys())
70
+ )
71
+ types = {int: 'int', str: 'string', float: 'real', bool: 'bool'}
72
+ return types[type(value)]
73
+
74
+
75
+ def sml_value(value, smltype=''):
76
+ if value is None:
77
+ return 'NONE'
78
+ _value = None
79
+ if isinstance(value, str):
80
+ _value = json.dumps(value)
81
+ if isinstance(value, bool):
82
+ _value = str(value).lower()
83
+ if isinstance(value, (int, float)):
84
+ if value < 0:
85
+ _value = '~%s' % -value
86
+ else:
87
+ _value = str(value)
88
+ if isinstance(value, list):
89
+ _value = '[%s]' % ', '.join(sml_value(v) for v in value)
90
+ if isinstance(value, dict):
91
+ if 'error' in value:
92
+ return 'Fail "%s"' % value['error']
93
+ _value = '{%s}' % ', '.join('%s = %s' % (k, sml_value(v))
94
+ for k, v in value.items())
95
+ return _value if 'option' not in smltype else ('SOME ' + _value)
96
+
97
+
98
+ def get_fn_args(case):
99
+ ignored_keys = {'property', 'expected', 'description', 'comments'}
100
+ return OrderedDict(
101
+ (k, case[k])
102
+ for k in sorted(case.keys())
103
+ if k not in ignored_keys
104
+ )
105
+
106
+
107
+ def extract_signatures(data):
108
+ funcs = OrderedDict()
109
+ exercise = data['exercise']
110
+ ignored_keys = {'property', 'expected', 'description', 'comments'}
111
+
112
+ def type_aux(values):
113
+ s = {sml_type(val) for val in values}
114
+ if len(s) == 1:
115
+ return s.pop()
116
+ if len(s) == 2 and 'option' in s:
117
+ s.remove('option')
118
+ return '%s option' % s.pop()
119
+ raise Exception('Failed to infer a proper type, can not unify different types: %s' % s)
120
+
121
+ def signature(xs):
122
+ output = []
123
+ args = OrderedDict()
124
+ for x in xs:
125
+ output.append(x['output'])
126
+ for arg, val in x['args'].items():
127
+ args.setdefault(arg, []).append(val)
128
+ return {
129
+ 'args': OrderedDict((arg, type_aux(vals)) for arg, vals in args.items()),
130
+ 'output': type_aux(output),
131
+ }
132
+
133
+ def collect(cases):
134
+ for case in cases:
135
+ if 'cases' in case:
136
+ collect(case['cases'])
137
+ else:
138
+ # assumes there's at least one test with non "nullish" vars
139
+ fn = camelize(case.get('property', exercise))
140
+ if sml_type(case['expected']) == 'exn':
141
+ continue
142
+ args = get_fn_args(case)
143
+ if not all(args.values()) or sml_type(case['expected']) == "'a list":# or not case['expected']:
144
+ continue
145
+ funcs.setdefault(fn, []).append({
146
+ 'args': args,
147
+ 'output': case['expected']
148
+ })
149
+
150
+ collect(data['cases'])
151
+ return {fn: signature(funcs[fn]) for fn in funcs}
152
+
153
+
154
+ def expectation(signature, fn, args, expected):
155
+ tmpl = '(fn _ => %s |> Expect.%s)'
156
+ output = sml_value(expected, signature['output'])
157
+ invocation = '%s (%s)' % (
158
+ fn,
159
+ ', '.join((
160
+ sml_value(val, signature['args'][arg])
161
+ for arg, val in args.items())
162
+ )
163
+ )
164
+ if sml_type(expected) == 'exn':
165
+ return tmpl % (
166
+ '(fn _ => %s)' % invocation,
167
+ 'error (%s)' % output
168
+ )
169
+ if signature['output'] == 'bool':
170
+ return tmpl % (
171
+ invocation,
172
+ 'truthy' if expected else 'falsy'
173
+ )
174
+ if signature['output'] == 'real':
175
+ return tmpl % (
176
+ invocation,
177
+ 'nearTo %s' % output
178
+ )
179
+ return tmpl % (
180
+ invocation,
181
+ ('equalTo (%s)' if 'SOME ' in output else 'equalTo %s') % output
182
+ )
183
+
184
+
185
+ def generate_test_content(data, signature):
186
+ exercise = data['exercise']
187
+
188
+ def fmt(case, depth=0):
189
+ if 'property' not in case:
190
+ print('WARNING: property not found using exercise name as function name')
191
+ fn = camelize(case.get('property', exercise))
192
+ expected = case['expected']
193
+ args = get_fn_args(case)
194
+ return '\n'.join((
195
+ indent(depth, 'test "%s"' % case.get('description', fn)),
196
+ indent(depth + 2, expectation(signature[fn], fn, args, expected))
197
+ ))
198
+
199
+ def traverse(cases, depth=0):
200
+ acc = []
201
+ for case in cases:
202
+ if 'cases' in case:
203
+ acc.append('\n'.join((
204
+ indent(depth * 2, 'describe "%s" [' % case['description']),
205
+ ',\n\n'.join(traverse(case['cases'], depth + 1)),
206
+ indent(depth * 2, ']')
207
+ )))
208
+ else:
209
+ acc.append(fmt(case, depth * 2))
210
+ return acc
211
+
212
+ return '\n'.join([
213
+ '(* version %s *)' % data['version'],
214
+ '',
215
+ 'use "%s.sml";' % data['exercise'],
216
+ 'use "testlib.sml";',
217
+ '',
218
+ 'infixr |>',
219
+ 'fun x |> f = f x',
220
+ '',
221
+ 'val testsuite =',
222
+ ' describe "%s" [' % data['exercise'],
223
+ ',\n\n'.join(traverse(data['cases'], depth=2)),
224
+ ' ]',
225
+ '',
226
+ 'val _ = Test.run testsuite'
227
+ ])
228
+
229
+
230
+ def generate_exercise_content(signatures):
231
+ def fmt(args):
232
+ return ', '.join('%s: %s' % (name, value)
233
+ for name, value in args.items())
234
+
235
+ return '\n\n'.join(
236
+ 'fun {0} ({1}): {2} =\n'
237
+ ' raise Fail "\'{0}\' is not implemented"'.format(
238
+ fn,
239
+ fmt(signature['args']),
240
+ signature['output']
241
+ )
242
+ for fn, signature in signatures.items()
243
+ )
244
+
245
+
246
+ def write(path, content):
247
+ with path.open('w') as f:
248
+ f.write(content)
249
+
250
+
251
+ def generate(exercise):
252
+ root = Path(__file__).parent.parent.absolute()
253
+ path = root / 'exercises' / exercise
254
+ if not path.exists():
255
+ path.mkdir()
256
+ data = fetch(exercise)
257
+ signatures = extract_signatures(data)
258
+ write(path / 'test.sml', generate_test_content(data, signatures))
259
+ content = generate_exercise_content(signatures)
260
+ write(path / ('%s.sml' % exercise), content)
261
+ write(path / 'example.sml', content)
262
+ shutil.copyfile(
263
+ (root / 'lib/testlib.sml').as_posix(),
264
+ (path / 'testlib.sml').as_posix()
265
+ )
266
+
267
+
268
+ if __name__ == '__main__':
269
+ import sys
270
+
271
+ for exercise in sys.argv[1:]:
272
+ try:
273
+ generate(exercise)
274
+ except FileNotFoundError:
275
+ print('[%s]: canonical-data.json not found' % exercise)
276
+ except Exception as e:
277
+ print('[%s]: %s' % (exercise, e))
@@ -4,6 +4,26 @@
4
4
  "active": false,
5
5
  "test_pattern": "test[.]sml$",
6
6
  "exercises": [
7
+ {
8
+ "uuid": "4450455d-0786-6f80-7672-48368a115df16e0aa7e",
9
+ "slug": "hello-world",
10
+ "core": true,
11
+ "unlocked_by": null,
12
+ "difficulty": 1,
13
+ "topics": [
14
+
15
+ ]
16
+ },
17
+ {
18
+ "uuid": "9d8e76f1-0e85-a280-b779-16335f29d1a96ce3a72",
19
+ "slug": "bob",
20
+ "core": false,
21
+ "unlocked_by": null,
22
+ "difficulty": 1,
23
+ "topics": [
24
+
25
+ ]
26
+ },
7
27
  {
8
28
  "uuid": "333c2cb9-f03c-473c-9eeb-096072a321b2",
9
29
  "slug": "accumulate",
@@ -74,6 +94,16 @@
74
94
 
75
95
  ]
76
96
  },
97
+ {
98
+ "uuid": "b6713a74-08d5-4480-6524-f9dbf5cf2563d2b2a91",
99
+ "slug": "difference-of-squares",
100
+ "core": false,
101
+ "unlocked_by": null,
102
+ "difficulty": 1,
103
+ "topics": [
104
+
105
+ ]
106
+ },
77
107
  {
78
108
  "uuid": "225cfd7d-81a3-4a58-b1aa-1de2e40e7a93",
79
109
  "slug": "binary",
@@ -0,0 +1,32 @@
1
+ [Standard ML (SML)](https://en.wikipedia.org/wiki/Standard_ml) is one of the two main dialects of the ML programming
2
+ language. [ML](https://en.wikipedia.org/wiki/ML_programming_language) was the first strong statically typed language,
3
+ developed in the early 1970s at the University of Edinburgh.
4
+
5
+ Despite it's age, SML feels very young in many ways; SML had features that mainstream languages would not pick up for decades and are still being experimented with today.
6
+
7
+ Here are some of ML's "cutting-edge" features:
8
+
9
+ - [**strong** static typing](https://en.wikipedia.org/wiki/Type_system#Static_typing)
10
+ - [automatic type inference](https://en.wikipedia.org/wiki/Type_inference)
11
+ - exception handling
12
+ - [pattern matching](https://en.wikipedia.org/wiki/Pattern_matching)
13
+ - parametric polymorphism
14
+ - [first class functions](https://en.wikipedia.org/wiki/First-class_function)
15
+
16
+ SML was originally designed for developping proofs about first-order predicate calculus (read: _computer programs_)
17
+ and it can have a distinct academic feel about it.
18
+
19
+ However it's emphasis on immutability and strong typing has led SML to be used in many fields where program correctness is paramount (compiler design, code analysis, financial systems, medical systems, etc...).
20
+
21
+ Learning SML makes you a better programmer, because it forces you to write code that is stateless and to use closures
22
+ effectively.
23
+
24
+ It's also many programmers first introduction to pattern matching and (truely) strong typing. And because SML's type system is so strong and well-thought out, it often feels like you are working in a dynamically typed language instead.
25
+
26
+ There are several popular implementations:
27
+
28
+ - [MLton](http://mlton.org/)
29
+ - [SML/NJ](http://www.smlnj.org/)
30
+ - [PolyML](http://www.polyml.org/).
31
+
32
+ You can find information on the language on each implementation's sites.
@@ -0,0 +1,37 @@
1
+ ## Recommended Learning Resources
2
+
3
+ Exercism provides exercises and feedback but ML can be difficult to jump into for those learning SML for the first time.
4
+ These resources can help you get started:
5
+
6
+ ### I've never programmed before
7
+
8
+ ML is probably not the best language for new programmers. You are probably better off starting with a language like
9
+ Ruby or Python, but if you are feeling particularly bold you might want to read [Programming in Standard ML '97:
10
+ An On-line Tutorial](https://www.cs.cmu.edu/~rwh/introsml/) I suggest skipping the introduction and jumping straight
11
+ to [Simple applicative programming ](http://homepages.inf.ed.ac.uk/stg/NOTES/node13.html), because the introduction
12
+ is very technical.
13
+
14
+ Coursera offers a course on [programming languages](https://www.coursera.org/learn/programming-languages).
15
+ The course is aimed at somewhat experienced programmers, but it's hard to find a better introduction to the ML language.
16
+
17
+
18
+ ### I've heard of functional programming
19
+
20
+ A good read is [Programming in Standard ML '97: An On-line Tutorial](https://www.cs.cmu.edu/~rwh/introsml/).
21
+
22
+ Another is [A Gentle Introduction to ML](http://www.soc.napier.ac.uk/course-notes/sml/manual.html).
23
+
24
+ Coursera offers a course on [programming languages](https://www.coursera.org/learn/programming-languages).
25
+ It's hard to find a better introduction to the ML language.
26
+
27
+ The [SML/NJ site](http://smlnj.org/doc/literature.html) lists a number of books. Most are quite old and are probably hard to find though.
28
+ The language hasn't changed much so if you can find them, don't think that they are that out of date. Also check the
29
+ Four Lectures on Standard ML listed on the page.
30
+
31
+
32
+ ## I am a confident programmer
33
+
34
+ If you are a confident programmer and if you are very familiar with functional languages, than you can get a good overview of the
35
+ syntax at [Learn X in Y minutes](https://learnxinyminutes.com/docs/standard-ml/)
36
+
37
+ The [Four Lectures on Standard ML](http://smlnj.org/doc/literature.html) are a good introduction.
@@ -0,0 +1,3 @@
1
+ fun fib 0 = 0
2
+ | fib 1 = 1
3
+ | fib n = fib (n - 1) + fib (n - 2)
@@ -0,0 +1,48 @@
1
+ # Bob
2
+
3
+ Bob is a lackadaisical teenager. In conversation, his responses are very limited.
4
+
5
+ Bob answers 'Sure.' if you ask him a question.
6
+
7
+ He answers 'Whoa, chill out!' if you yell at him.
8
+
9
+ He says 'Fine. Be that way!' if you address him without actually saying
10
+ anything.
11
+
12
+ He answers 'Whatever.' to anything else.
13
+
14
+ ## Loading your exercise implementation in PolyML
15
+
16
+ ```
17
+ $ poly --use {exercise}.sml
18
+ ```
19
+
20
+ Or:
21
+
22
+ ```
23
+ $ poly
24
+ > use "{exercise}.sml";
25
+ ```
26
+
27
+ **Note:** You have to replace {exercise}.
28
+
29
+ ## Running the tests
30
+
31
+ ```
32
+ $ poly -q --use test.sml
33
+ ```
34
+
35
+ ## Feedback, Issues, Pull Requests
36
+
37
+ The [exercism/sml](https://github.com/exercism/sml) repository on
38
+ GitHub is the home for all of the Standard ML exercises.
39
+
40
+ If you have feedback about an exercise, or want to help implementing a new
41
+ one, head over there and create an issue. We'll do our best to help you!
42
+
43
+ ## Source
44
+
45
+ Inspired by the 'Deaf Grandma' exercise in Chris Pine's Learn to Program tutorial. [http://pine.fm/LearnToProgram/?Chapter=06](http://pine.fm/LearnToProgram/?Chapter=06)
46
+
47
+ ## Submitting Incomplete Solutions
48
+ It's possible to submit an incomplete solution so you can see how others have completed the exercise.