diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a4b3831 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + // ReasonMl + "reason.diagnostics.tools": ["merlin", "bsb"], + "editor.formatOnSave": true, + "reason.codelens.enabled": true +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..8179fdf --- /dev/null +++ b/package-lock.json @@ -0,0 +1,14 @@ +{ + "name": "learn-reasonml-workshop", + "version": "0.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "bs-platform": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/bs-platform/-/bs-platform-2.2.3.tgz", + "integrity": "sha1-2QWuEKXzYh5qc5BB36C1hIOiF08=", + "dev": true + } + } +} diff --git a/src/exercises/01-introduction/introduction.re b/src/exercises/01-introduction/introduction.re index 2c0a041..0ea65ef 100644 --- a/src/exercises/01-introduction/introduction.re +++ b/src/exercises/01-introduction/introduction.re @@ -1,37 +1,37 @@ -/* - Hello, - - Reason is not a new language. It is syntactic sugar for the OCaml language - created in 1996. The new Reason syntax will look familiar to those who have - programmed in Javascript. - - These challenges are originally from the workshop run by Jane Street to - teach OCaml. It was ported over to Reason with the help of `refmt` (Reason - format) binary that helps you convert to/from Reason/OCaml syntax. - - This exercise is to familiarize you with the code-compile cycle. - - To compile your code, run in a terminal session in the root directory - - $ npm run build - - You should see a compilation error because it's missing the end quote. Add - the end quote and re-run. You should see that the code compiled successfully! - - For convenience you can build whenever you change the code by running the - compiler in watch mode. For that, run - - $ npm run start - - You can also execute code in Reason's REPL (rtop) directly. To start rtop from - your terminal run: - - $ rtop - - Now try pasting the code below into it. - - *Note:* Use `Ctrl-d` to exit the rtop session. - - Try these out and move on to the next exercise! - */ -let () = print_endline("Hello, World!); \ No newline at end of file +/* + Hello, + + Reason is not a new language. It is syntactic sugar for the OCaml language + created in 1996. The new Reason syntax will look familiar to those who have + programmed in Javascript. + + These challenges are originally from the workshop run by Jane Street to + teach OCaml. It was ported over to Reason with the help of `refmt` (Reason + format) binary that helps you convert to/from Reason/OCaml syntax. + + This exercise is to familiarize you with the code-compile cycle. + + To compile your code, run in a terminal session in the root directory + + $ npm run build + + You should see a compilation error because it's missing the end quote. Add + the end quote and re-run. You should see that the code compiled successfully! + + For convenience you can build whenever you change the code by running the + compiler in watch mode. For that, run + + $ npm run start + + You can also execute code in Reason's REPL (rtop) directly. To start rtop from + your terminal run: + + $ rtop + + Now try pasting the code below into it. + + *Note:* Use `Ctrl-d` to exit the rtop session. + + Try these out and move on to the next exercise! + */ +let () = print_endline("Hello, World!"); \ No newline at end of file diff --git a/src/exercises/02-basic_types/basicTypes.re b/src/exercises/02-basic_types/basicTypes.re index 5cc3340..946b06f 100644 --- a/src/exercises/02-basic_types/basicTypes.re +++ b/src/exercises/02-basic_types/basicTypes.re @@ -1,159 +1,159 @@ -/* - In Reason there are 6 basic types: int, float, char, string, bool, and unit. - - The following exercises and examples will give you a brief introduction to - these types. Feel free to play around with them in rtop. - - Note the keyword [let], which is how variable assignment is done in Reason. - - In Reason floats are distinguished from ints by their decimal points. 0 is an - int, 0. is a float. - - In addition the basic math operations are also distinguished by a decimal - point. For example, + allows you to add two ints and +. allows you to add two - floats. - - Signatures - ========== - - four is a value with the type int. We write the signature like this: - - let four : int - - Read it like this: "[four] is a value of type int". - - Signatures are similar to type declarations in other languages. They tell the - compiler (and human readers of your code!) the types of variables and - functions in your program. For example, in C, C++, and Java, the signature - above would be written like so: - - int four; - */ -let four = 4; - -/* - floatFour is a value with the type float. We write the signature like this: - - let floatFour : float - - You may have noticed that the two signatures we showed you were in comments. - Signatures are not always required! In many situations, you may omit them, - and the compiler will infer the type of values. - - However, if you do write a signature for a value, the compiler will make sure - to check that it's consistent with how that value is used. - - Try inserting an incorrect signature for [floatFour] to see what error the - compiler gives you. - */ -let floatFour = 4.; - -/* - Function signatures - =================== - - In Reason, functions are also values! And so, functions also have type - signatures. - - In a function signature, types of parameters are enclosed within paranthesis. - The return value is the described last and preceded immediately by an arrow - [=>]. - - So the signature for a function that takes two integers and returns an - integer is: - - let intAverage: (int, int) => int - - In Reason there's no explicit return statement: functions just return the - value of the last statement in that function. - */ -/* let intAverage = (x, y) => failwith("For you to implement"); */ -let intAverage = (x, y) => failwith("For you to implement"); - -/* let floatAverage : (float, float) => float */ -/* let floatAverage = (x, y) => failwith("For you to implement"); */ -let floatAverage = (x, y) => failwith("For you to implement"); - -/* - The following expression computes the average of 10 and 20: - - intAverage(10, 20) - - As in many languages strings are denoted with "" and chars are denoted with ''. - - String concatenation is done with the ++ operator. - - let firstName : string - */ -let firstName = "Fred"; - -/* You can also write type annotations in definitions */ -let lastName: string = "Flintstone"; - -/* - But Reason has very strong type inference, so you can most often omit types, - and the compiler can infer that fullName is a string. - */ -let fullName = firstName ++ " " ++ lastName; - -let aBooleanFalse: bool = false; - -/* - You can use - && for logical and - || for logical or - ! for logical not - */ -let () = assert (true || aBooleanFalse); - -/* - The [unit] type - =============== - - unit is a special type in Reason that has only one possible value written (). - It is generally used for mutation and io-operations such as printing. - - (I/O stands for input/output. Examples: printing to screen, reading a file, - sending and receiving network requests.) - - To combine several unit operations together the ; operator is used contained - within curly braces. - */ -let () = { - print_endline("Hi, My name is "); - print_endline(fullName); - print_endline(" and I am 5 years old"); -}; - -/* - The lines that follow are inline tests. Each evaluates a boolean expression. - They are run during the build, and failures -- evaluating to false -- are - treated like compile errors by the build tool and editors. - - We will see other kinds of inline tests later, and some interesting patterns - for using them. - While Reason supports polymorphic comparison, it is good practice to use - equality and comparison functions specific to each type. - - So, [Int.equal] is the [equal] function defined in the [Int] module. Its - signature is - - val equal : int -> int -> bool - - In words: [equal] takes two [int]s and returns a [bool]. The following line - is applying that function to two inputs, [5] and [int_average 5 5]. - */ -Test.runAll([ - (intAverage(5, 5) == 5, "int average"), - (floatAverage(5., 5.) == 5., "float average"), - (floatAverage(5., 10.) == 7.5, "float average"), -]); -/* - .rei files - ========== - - Check out the [basicTypes.rei] file in this directory! It declares the types for - the two functions you had to implement. If the types in the [.rei] don't - match the types of the values in the [.re], the compiler will flag that as an - error. +/* + In Reason there are 6 basic types: int, float, char, string, bool, and unit. + + The following exercises and examples will give you a brief introduction to + these types. Feel free to play around with them in rtop. + + Note the keyword [let], which is how variable assignment is done in Reason. + + In Reason floats are distinguished from ints by their decimal points. 0 is an + int, 0. is a float. + + In addition the basic math operations are also distinguished by a decimal + point. For example, + allows you to add two ints and +. allows you to add two + floats. + + Signatures + ========== + + four is a value with the type int. We write the signature like this: + + let four : int + + Read it like this: "[four] is a value of type int". + + Signatures are similar to type declarations in other languages. They tell the + compiler (and human readers of your code!) the types of variables and + functions in your program. For example, in C, C++, and Java, the signature + above would be written like so: + + int four; + */ +let four: int = 4; + +/* + floatFour is a value with the type float. We write the signature like this: + + let floatFour : float + + You may have noticed that the two signatures we showed you were in comments. + Signatures are not always required! In many situations, you may omit them, + and the compiler will infer the type of values. + + However, if you do write a signature for a value, the compiler will make sure + to check that it's consistent with how that value is used. + + Try inserting an incorrect signature for [floatFour] to see what error the + compiler gives you. + */ +let floatFour : float = 4.; + +/* + Function signatures + =================== + + In Reason, functions are also values! And so, functions also have type + signatures. + + In a function signature, types of parameters are enclosed within paranthesis. + The return value is the described last and preceded immediately by an arrow + [=>]. + + So the signature for a function that takes two integers and returns an + integer is: + + let intAverage: (int, int) => int + + In Reason there's no explicit return statement: functions just return the + value of the last statement in that function. + */ +/* let intAverage = (x, y) => failwith("For you to implement"); */ +let intAverage: (int, int) => int = (x, y) => ( x + y ) / 2; + +/* let floatAverage : (float, float) => float */ +/* let floatAverage = (x, y) => failwith("For you to implement"); */ +let floatAverage: (float, float) => float = (x, y) => (x +. y) /. 2.; + +/* + The following expression computes the average of 10 and 20: + + intAverage(10, 20) + + As in many languages strings are denoted with "" and chars are denoted with ''. + + String concatenation is done with the ++ operator. + + let firstName : string + */ +let firstName = "Fred"; + +/* You can also write type annotations in definitions */ +let lastName: string = "Flintstone"; + +/* + But Reason has very strong type inference, so you can most often omit types, + and the compiler can infer that fullName is a string. + */ +let fullName = firstName ++ " " ++ lastName; + +let aBooleanFalse: bool = false; + +/* + You can use + && for logical and + || for logical or + ! for logical not + */ +let () = assert (true || aBooleanFalse); + +/* + The [unit] type + =============== + + unit is a special type in Reason that has only one possible value written (). + It is generally used for mutation and io-operations such as printing. + + (I/O stands for input/output. Examples: printing to screen, reading a file, + sending and receiving network requests.) + + To combine several unit operations together the ; operator is used contained + within curly braces. + */ +let () = { + print_endline("Hi, My name is "); + print_endline(fullName); + print_endline(" and I am 5 years old"); +}; + +/* + The lines that follow are inline tests. Each evaluates a boolean expression. + They are run during the build, and failures -- evaluating to false -- are + treated like compile errors by the build tool and editors. + + We will see other kinds of inline tests later, and some interesting patterns + for using them. + While Reason supports polymorphic comparison, it is good practice to use + equality and comparison functions specific to each type. + + So, [Int.equal] is the [equal] function defined in the [Int] module. Its + signature is + + val equal : int -> int -> bool + + In words: [equal] takes two [int]s and returns a [bool]. The following line + is applying that function to two inputs, [5] and [int_average 5 5]. + */ +Test.runAll([ + (intAverage(5, 5) == 5, "int average"), + (floatAverage(5., 5.) == 5., "float average"), + (floatAverage(5., 10.) == 7.5, "float average"), +]); +/* + .rei files + ========== + + Check out the [basicTypes.rei] file in this directory! It declares the types for + the two functions you had to implement. If the types in the [.rei] don't + match the types of the values in the [.re], the compiler will flag that as an + error. */ \ No newline at end of file diff --git a/src/exercises/03-define_functions/defineFunctions.re b/src/exercises/03-define_functions/defineFunctions.re index f0a30d5..d204a85 100644 --- a/src/exercises/03-define_functions/defineFunctions.re +++ b/src/exercises/03-define_functions/defineFunctions.re @@ -1,33 +1,33 @@ -/* - We use let to define functions. - - Definitions take on the form: - let functionName = (arg1, arg2) => body; - - For example, here we define a function add1 that takes a single int - argument and returns that argument plus 1. - */ -let add1 = arg => arg + 1; - -/* This function uses the built-in ++ operator to append strings. */ -let stringAppend = (x, y) => x ++ y; - -/* Let's define our own functions using +, -, *, and / below. */ -let plus = (x, y) => failwith("For you to implement"); - -let times = (x, y) => failwith("For you to implement"); - -let minus = (x, y) => failwith("For you to implement"); - -let divide = (x, y) => failwith("For you to implement"); - -Test.runAll([ - (plus(1, 1) == 2, "plus"), - (plus(50, -1) == 49, "plus"), - (times(8, 8) == 64, "times"), - (times(2, -1024) == (-2048), "times"), - (minus(-2, 2) == (-4), "minus"), - (minus(1337, 337) == 1000, "minus"), - (divide(1024, 2) == 512, "divide"), - (divide(31337, 31) == 1010, "divide"), +/* + We use let to define functions. + + Definitions take on the form: + let functionName = (arg1, arg2) => body; + + For example, here we define a function add1 that takes a single int + argument and returns that argument plus 1. + */ +let add1: (int) => int = arg => arg + 1; + +/* This function uses the built-in ++ operator to append strings. */ +let stringAppend: (string, string) => string = (x, y) => x ++ y; + +/* Let's define our own functions using +, -, *, and / below. */ +let plus = (x, y) => x + y; + +let times = (x, y) => x * y; + +let minus = (x, y) => x - y; + +let divide = (x, y) => x / y ; + +Test.runAll([ + (plus(1, 1) == 2, "plus"), + (plus(50, -1) == 49, "plus"), + (times(8, 8) == 64, "times"), + (times(2, -1024) == (-2048), "times"), + (minus(-2, 2) == (-4), "minus"), + (minus(1337, 337) == 1000, "minus"), + (divide(1024, 2) == 512, "divide"), + (divide(31337, 31) == 1010, "divide"), ]); \ No newline at end of file diff --git a/src/exercises/04-call_functions/callFunctions.re b/src/exercises/04-call_functions/callFunctions.re index e047bbe..5b758d5 100644 --- a/src/exercises/04-call_functions/callFunctions.re +++ b/src/exercises/04-call_functions/callFunctions.re @@ -1,26 +1,26 @@ -/* Here are some example functions: */ -let square = x => x * x; - -let half = x => x / 2; - -let add = (x, y) => x + y; - -/* You can order function invocations with parentheses or let bindings */ -/* Parens */ -let () = Js.log("(5^2)/2 = " ++ string_of_int(half(square(5)))); - -/* Let bindings */ -let () = { - let squared = square(5); - let halved = half(squared); - let toString = string_of_int(halved); - Js.log("(5^2)/2 = " ++ toString); -}; - -/* Try to write [average] by reusing [add] and [half] */ -let average = (x, y) => failwith("For you to implement"); - -Test.runAll([ - (average(5, 5) == 5, "average"), - (average(50, 100) == 75, "average"), +/* Here are some example functions: */ +let square = x => x * x; + +let half = x => x / 2; + +let add = (x, y) => x + y; + +/* You can order function invocations with parentheses or let bindings */ +/* Parens */ +let () = Js.log("(5^2)/2 = " ++ string_of_int(half(square(5)))); + +/* Let bindings */ +let () = { + let squared = square(5); + let halved = half(squared); + let toString = string_of_int(halved); + Js.log("(5^2)/2 = " ++ toString); +}; + +/* Try to write [average] by reusing [add] and [half] */ +let average = (x, y) => add(x, y) |> half; + +Test.runAll([ + (average(5, 5) == 5, "average"), + (average(50, 100) == 75, "average"), ]); \ No newline at end of file diff --git a/src/exercises/05-twice/twice.re b/src/exercises/05-twice/twice.re index 560e357..6f0675a 100644 --- a/src/exercises/05-twice/twice.re +++ b/src/exercises/05-twice/twice.re @@ -1,38 +1,38 @@ -/* - We can easily write a function that adds 1 to any number. - Recall that the infix operator (+) will add two integers. - */ -let add1 = x => failwith("For you to implement"); - -/* - Let's write a function that squares its argument (multiplies it by itself) - */ -let square = x => failwith("For you to implement"); - -/* - Functions are first class in Reason. This means that you can take - a function and pass it around as an argument to other functions. - - Let's write a function named twice: it will take a function and apply - that function to itself two times. - - For example, if we wanted to make an "add2" function, we could do it - by writing: - let add2 = twice(add1) - */ -let twice = (f, x) => failwith("For you to implement"); - -/* Now that we have twice, write add2 and raiseToTheFourth */ -let add2 = failwith("For you to implement"); /* Hint: use add1 */ - -let raiseToTheFourth = failwith("For you to implement"); /* Hint: use square */ - -Test.runAll([ - (add1(4) == 5, "add1"), - (square(4) == 16, "square"), - (square(-4) == 16, "square"), - (twice(add1, 3) == 5, "twice"), - (add2(1335) == 1337, "add2"), - (raiseToTheFourth(1) == 1, "raiseToTheFourth"), - (raiseToTheFourth(10) == 10000, "raiseToTheFourth"), +/* + We can easily write a function that adds 1 to any number. + Recall that the infix operator (+) will add two integers. + */ +let add1 = x => x + 1; + +/* + Let's write a function that squares its argument (multiplies it by itself) + */ +let square = x => x * x; + +/* + Functions are first class in Reason. This means that you can take + a function and pass it around as an argument to other functions. + + Let's write a function named twice: it will take a function and apply + that function to itself two times. + + For example, if we wanted to make an "add2" function, we could do it + by writing: + let add2 = twice(add1) + */ +let twice = (f, x) => f(x) |> f; + +/* Now that we have twice, write add2 and raiseToTheFourth */ +let add2 = x => twice(add1, x); + +let raiseToTheFourth = x => twice(square, x); /* Hint: use square */ + +Test.runAll([ + (add1(4) == 5, "add1"), + (square(4) == 16, "square"), + (square(-4) == 16, "square"), + (twice(add1, 3) == 5, "twice"), + (add2(1335) == 1337, "add2"), + (raiseToTheFourth(1) == 1, "raiseToTheFourth"), + (raiseToTheFourth(10) == 10000, "raiseToTheFourth"), ]); \ No newline at end of file diff --git a/src/exercises/06-pattern-matching/patternMatching.re b/src/exercises/06-pattern-matching/patternMatching.re index 69b0340..194c8e5 100644 --- a/src/exercises/06-pattern-matching/patternMatching.re +++ b/src/exercises/06-pattern-matching/patternMatching.re @@ -1,23 +1,27 @@ -/* - Pattern matching lets us compare inputs to known values. - Patterns following "|" are tested in order. - On the first match, we use the result following "=>". - The "_" pattern means "could be anything". - */ -let isSuperman = x => - switch (x) { - | "Clark Kent" => true - | _ => false - }; - -/* - Let's use our own pattern matching. Write a function that returns - whether x is non zero by matching on x - */ -let nonZero = x => failwith("For you to implement"); - -Test.runAll([ - (nonZero(0) == false, "non zero"), - (nonZero(500) == true, "non zero"), - (nonZero(-400) == true, "non zero"), +/* + Pattern matching lets us compare inputs to known values. + Patterns following "|" are tested in order. + On the first match, we use the result following "=>". + The "_" pattern means "could be anything". + */ +let isSuperman = x => + switch (x) { + | "Clark Kent" => true + | _ => false + }; + +/* + Let's use our own pattern matching. Write a function that returns + whether x is non zero by matching on x + */ +let nonZero = x => + switch (x) { + | 0 => false + | _ => true + }; + +Test.runAll([ + (nonZero(0) == false, "non zero"), + (nonZero(500) == true, "non zero"), + (nonZero(-400) == true, "non zero"), ]); \ No newline at end of file diff --git a/src/exercises/07-simple_recursion/simpleRecursion.re b/src/exercises/07-simple_recursion/simpleRecursion.re index 0de885e..c362665 100644 --- a/src/exercises/07-simple_recursion/simpleRecursion.re +++ b/src/exercises/07-simple_recursion/simpleRecursion.re @@ -1,30 +1,30 @@ -/* - Remember that functions can call functions? - They can call themselves too. But only with a special keyword. - - Try changing "let rec" to "let" and you'll see the following compiler error: - "The value addEveryNumberUpTo can't be found" - */ -let rec addEveryNumberUpTo = x => { - /* make sure we don't call this on negative numbers! */ - assert (x >= 0); - switch (x) { - | 0 => 0 - | _ => x + addEveryNumberUpTo(x - 1) - }; -}; - -/* - Let's write a function to multiply every number up to x. - Remember: [factorial 0] is 1 - */ -let rec factorial = x => { - assert (x >= 0); - failwith("For you to implement"); -}; - -Test.runAll([ - (factorial(0) == 1, "factorial"), - (factorial(5) == 120, "factorial"), - (factorial(12) == 479001600, "factorial"), +/* + Remember that functions can call functions? + They can call themselves too. But only with a special keyword. + + Try changing "let rec" to "let" and you'll see the following compiler error: + "The value addEveryNumberUpTo can't be found" + */ +let rec addEveryNumberUpTo = x => { + /* make sure we don't call this on negative numbers! */ + assert (x >= 0); + switch (x) { + | 0 => 0 + | _ => x + addEveryNumberUpTo(x - 1) + }; +}; + +/* + Let's write a function to multiply every number up to x. + Remember: [factorial 0] is 1 + */ +let rec factorial = x => { + assert (x >= 0); + x <= 0 ? 1 : x * factorial(x - 1); +}; + +Test.runAll([ + (factorial(0) == 1, "factorial"), + (factorial(5) == 120, "factorial"), + (factorial(12) == 479001600, "factorial"), ]); \ No newline at end of file diff --git a/src/exercises/08-list_intro/listIntro.re b/src/exercises/08-list_intro/listIntro.re index d84aaeb..4e3e208 100644 --- a/src/exercises/08-list_intro/listIntro.re +++ b/src/exercises/08-list_intro/listIntro.re @@ -1,45 +1,49 @@ -/* - Reason natively supports linked lists as part of the language. - Lists are commonly referred to as having heads and tails. - The head is the first element of the linked list - The tail is everything else. - - [] means "the empty list". - [hd, ...tl] means "the element hd added to the front of the list tl". - - When matching on a list, it's either empty or non-empty. To say it another - way, it's either equal to [] or equal to ([hd, ...tl]) where hd is the first - element of the list and tl is all the rest of the elements of the list - (which may itself be empty). - - This function computes the length of a list. - */ -let rec length = lst => - switch (lst) { - | [] => 0 - | [_, ...tl] => 1 + length(tl) - }; - -/* Write a function to add up the elements of a list by matching on it. */ -let rec sum = lst => failwith("For you to implement"); - -/* - The signature for the append operator is - let (@) : (list('a), list('a)) => list('a) - - It's an infix operator. - */ -let listAppend = (first, second) => first @ second; - -/* - The way you put something on the head to the list uses the same kind of - syntax for matching on lists. This is called the spread syntax. - */ -let newHead = (hd, rest) => [hd, ...rest]; - -Test.runAll([ - (sum([]) == 0, "sum"), - (sum([55]) == 55, "sum"), - (sum([5, (-5), 1, (-1)]) == 0, "sum"), - (sum([5, 5, 1, 1]) == 12, "sum"), +/* + Reason natively supports linked lists as part of the language. + Lists are commonly referred to as having heads and tails. + The head is the first element of the linked list + The tail is everything else. + + [] means "the empty list". + [hd, ...tl] means "the element hd added to the front of the list tl". + + When matching on a list, it's either empty or non-empty. To say it another + way, it's either equal to [] or equal to ([hd, ...tl]) where hd is the first + element of the list and tl is all the rest of the elements of the list + (which may itself be empty). + + This function computes the length of a list. + */ +let rec length = lst => + switch (lst) { + | [] => 0 + | [_, ...tl] => 1 + length(tl) + }; + +/* Write a function to add up the elements of a list by matching on it. */ +let rec sum = lst => + switch (lst) { + | [] => 0 + | [a, ...t1] => a + sum(t1) + }; + +/* + The signature for the append operator is + let (@) : (list('a), list('a)) => list('a) + + It's an infix operator. + */ +let listAppend = (first, second) => first @ second; + +/* + The way you put something on the head to the list uses the same kind of + syntax for matching on lists. This is called the spread syntax. + */ +let newHead = (hd, rest) => [hd, ...rest]; + +Test.runAll([ + (sum([]) == 0, "sum"), + (sum([55]) == 55, "sum"), + (sum([5, (-5), 1, (-1)]) == 0, "sum"), + (sum([5, 5, 1, 1]) == 12, "sum"), ]); \ No newline at end of file diff --git a/src/exercises/09-list_range/listRange.re b/src/exercises/09-list_range/listRange.re index 3152520..f6351ee 100644 --- a/src/exercises/09-list_range/listRange.re +++ b/src/exercises/09-list_range/listRange.re @@ -1,24 +1,37 @@ -/* - The append infix operator @ concatenates two lists: - - let (@) : (list('a), list('a)) => list('a) - - This function is the same as the List.append function. - */ -let () = { - assert ([5, 1] @ [8, 4] == [5, 1, 8, 4]); - assert (List.append([5, 1], [8, 4]) == [5, 1, 8, 4]); -}; - -/* - Write a function to construct a list of all integers in the range [from,to_] - in increasing order. - - let range: (int, int) => list(int) - */ -let range = (from, to_) => failwith("For you to implement"); - -Test.runAll([ - (range(1, 4) == [1, 2, 3], "range"), - (range(-5, 3) == [(-5), (-4), (-3), (-2), (-1), 0, 1, 2], "range"), +/* + The append infix operator @ concatenates two lists: + + let (@) : (list('a), list('a)) => list('a) + + This function is the same as the List.append function. + */ +let () = { + assert ([5, 1] @ [8, 4] == [5, 1, 8, 4]); + assert (List.append([5, 1], [8, 4]) == [5, 1, 8, 4]); +}; + +/* + Write a function to construct a list of all integers in the range [from,to_] + in increasing order. + + let range: (int, int) => list(int) + + solution 1 : + let rec range = (from, to_) => + if (from < to_) { + [from, ...range(from + 1, to_)]; + } else { + []; + }; + */ +let rec range = (from, to_) => + if (from < to_) { + [from, ...range(from + 1, to_)]; + } else { + []; + }; + +Test.runAll([ + (range(1, 4) == [1, 2, 3], "range"), + (range(-5, 3) == [(-5), (-4), (-3), (-2), (-1), 0, 1, 2], "range"), ]); \ No newline at end of file diff --git a/src/exercises/10-list_product/listProduct.re b/src/exercises/10-list_product/listProduct.re index d4a21cc..f33b1a2 100644 --- a/src/exercises/10-list_product/listProduct.re +++ b/src/exercises/10-list_product/listProduct.re @@ -1,13 +1,13 @@ -/* Now let's write a function to multiply the elements of a list. */ -let rec product = xs => - switch (xs) { - | [] => failwith("For you to implement") - | _for_you_to_implement => failwith("For you to implement") - }; - -Test.runAll([ - (product([]) == 1, "product"), - (product([55]) == 55, "product"), - (product([5, (-5), 1, (-1)]) == 25, "product"), - (product([5, 5, 1, 1]) == 25, "product"), +/* Now let's write a function to multiply the elements of a list. */ +let rec product = xs => + switch (xs) { + | [] => 1 + | [elm, ...rest] => elm * product(rest) + }; + +Test.runAll([ + (product([]) == 1, "product"), + (product([55]) == 55, "product"), + (product([5, (-5), 1, (-1)]) == 25, "product"), + (product([5, 5, 1, 1]) == 25, "product"), ]); \ No newline at end of file diff --git a/src/exercises/11-sum_product/sumProduct.re b/src/exercises/11-sum_product/sumProduct.re index 0a49b1f..b8a1427 100644 --- a/src/exercises/11-sum_product/sumProduct.re +++ b/src/exercises/11-sum_product/sumProduct.re @@ -1,90 +1,94 @@ -let plus = (x, y) => x + y; - -let times = (x, y) => x * y; - -/* Sometimes, multiple functions look similar: */ -let rec addEveryNumberUpTo = x => - switch (x) { - | 0 => 0 - | _ => plus(x, addEveryNumberUpTo(x - 1)) - }; - -let rec factorial = x => - switch (x) { - | 0 => 1 - | _ => times(x, factorial(x - 1)) - }; - -/* - These functions have a lot in common: - - let rec NAME x = - switch (x) { - | 0 => ANSWER - | _ => COMBINE(x, NAME(x-1)) - } - - Reason lets us write the common parts just once. - We just add an extra input for every part that changes (other than the name): - */ -let rec upTo = (answer, combine, x) => - switch (x) { - | 0 => answer - | _ => combine(x, upTo(answer, combine, x - 1)) - }; - -/* Now we can write our original functions in one line each! */ -let simplerAddEveryNumberUpTo = x => upTo(0, plus, x); - -let simplerFactorial = x => upTo(1, times, x); - -/* - Note that with infix operators like + and *, you can actually pass them as - functions! You can do this by writing ( + ) and ( * ). So another way to - write the above two functions would be: - - let simplerAddEveryNumberUpTo = x => upTo(0, ( + ), x); - let simplerFactorial = x => upTo(1, ( * ), x); - - Remember sum and product? - */ -let rec sum = xs => - switch (xs) { - | [] => 0 - | [x, ...rest] => plus(x, sum(rest)) - }; - -let rec product = xs => - switch (xs) { - | [] => 1 - | [x, ...rest] => times(x, product(rest)) - }; - -/* - These functions look pretty similar too: - - let rec NAME xs = - switch (xs) { - | [] => ANSWER - | [x, ...rest] => COMBINE(x, NAME(rest)) - } - - Let's write the common parts just once: - */ -let rec every = (answer, combine, xs) => failwith("For you to implement"); - -/* Now let's rewrite sum and product in just one line each using every */ -let simplerSum = xs => failwith("For you to implement"); - -let simplerProduct = xs => failwith("For you to implement"); - -Test.runAll([ - (simplerProduct([]) == 1, "simpler product"), - (simplerProduct([55]) == 55, "simpler product"), - (simplerProduct([5, (-5), 1, (-1)]) == 25, "simpler product"), - (simplerProduct([5, 5, 1, 1]) == 25, "simpler product"), - (simplerSum([]) == 0, "simpler sum"), - (simplerSum([55]) == 55, "simpler sum"), - (simplerSum([5, (-5), 1, (-1)]) == 0, "simpler sum"), - (simplerSum([5, 5, 1, 1]) == 12, "simpler sum"), +let plus = (x, y) => x + y; + +let times = (x, y) => x * y; + +/* Sometimes, multiple functions look similar: */ +let rec addEveryNumberUpTo = x => + switch (x) { + | 0 => 0 + | _ => plus(x, addEveryNumberUpTo(x - 1)) + }; + +let rec factorial = x => + switch (x) { + | 0 => 1 + | _ => times(x, factorial(x - 1)) + }; + +/* + These functions have a lot in common: + + let rec NAME x = + switch (x) { + | 0 => ANSWER + | _ => COMBINE(x, NAME(x-1)) + } + + Reason lets us write the common parts just once. + We just add an extra input for every part that changes (other than the name): + */ +let rec upTo = (answer, combine, x) => + switch (x) { + | 0 => answer + | _ => combine(x, upTo(answer, combine, x - 1)) + }; + +/* Now we can write our original functions in one line each! */ +let simplerAddEveryNumberUpTo = x => upTo(0, plus, x); + +let simplerFactorial = x => upTo(1, times, x); + +/* + Note that with infix operators like + and *, you can actually pass them as + functions! You can do this by writing ( + ) and ( * ). So another way to + write the above two functions would be: + + let simplerAddEveryNumberUpTo = x => upTo(0, ( + ), x); + let simplerFactorial = x => upTo(1, ( * ), x); + + Remember sum and product? + */ +let rec sum = xs => + switch (xs) { + | [] => 0 + | [x, ...rest] => plus(x, sum(rest)) + }; + +let rec product = xs => + switch (xs) { + | [] => 1 + | [x, ...rest] => times(x, product(rest)) + }; + +/* + These functions look pretty similar too: + + let rec NAME xs = + switch (xs) { + | [] => ANSWER + | [x, ...rest] => COMBINE(x, NAME(rest)) + } + + Let's write the common parts just once: + */ +let rec every = (answer, combine, xs) => + switch (xs) { + | [] => answer + | [x, ...rest] => combine(x, every(answer, combine, rest)) + }; + +/* Now let's rewrite sum and product in just one line each using every */ +let simplerSum = xs => every(0, plus, xs); + +let simplerProduct = xs => every(1, times, xs); + +Test.runAll([ + (simplerProduct([]) == 1, "simpler product"), + (simplerProduct([55]) == 55, "simpler product"), + (simplerProduct([5, (-5), 1, (-1)]) == 25, "simpler product"), + (simplerProduct([5, 5, 1, 1]) == 25, "simpler product"), + (simplerSum([]) == 0, "simpler sum"), + (simplerSum([55]) == 55, "simpler sum"), + (simplerSum([5, (-5), 1, (-1)]) == 0, "simpler sum"), + (simplerSum([5, 5, 1, 1]) == 12, "simpler sum"), ]); \ No newline at end of file diff --git a/src/exercises/12-list_min/listMin.re b/src/exercises/12-list_min/listMin.re index bfcd118..70e3d22 100644 --- a/src/exercises/12-list_min/listMin.re +++ b/src/exercises/12-list_min/listMin.re @@ -1,17 +1,23 @@ -/* This function finds the largest element in a list: */ -let rec largest = xs => - switch (xs) { - | [] => neg_infinity - | [x, ...rest] => max(x, largest(rest)) - }; - -/* Let's write a function to find the smallest element: Hint: the opposite of - [neg_infinity] is [infinity]. */ -let rec smallest = xs => failwith("For you to implement"); - -Test.runAll([ - (smallest([]) == infinity, "smallest"), - (smallest([55.]) == 55., "smallest"), - (smallest([5., (-5.), 1., (-1.)]) == (-5.), "smallest"), - (smallest([5., 5., 1., 1.]) == 1., "smallest"), +/* This function finds the largest element in a list: */ +let rec largest = xs => + switch (xs) { + | [] => neg_infinity + | [x, ...rest] => max(x, largest(rest)) + }; + +/* Let's write a function to find the smallest element: Hint: the opposite of + [neg_infinity] is [infinity]. */ +let min = (num1, num2) => num1 >= num2 ? num2 : num1; + +let rec smallest = xs => + switch (xs) { + | [] => infinity + | [x, ...rest] => min(x, smallest(rest)) + }; + +Test.runAll([ + (smallest([]) == infinity, "smallest"), + (smallest([55.]) == 55., "smallest"), + (smallest([5., (-5.), 1., (-1.)]) == (-5.), "smallest"), + (smallest([5., 5., 1., 1.]) == 1., "smallest"), ]); \ No newline at end of file diff --git a/src/exercises/13-largest_smallest/largestSmallest.re b/src/exercises/13-largest_smallest/largestSmallest.re index 74b7856..1144a07 100644 --- a/src/exercises/13-largest_smallest/largestSmallest.re +++ b/src/exercises/13-largest_smallest/largestSmallest.re @@ -1,38 +1,38 @@ -/* Here is [every] from the "Sum Product" problem */ -let rec every = (answer, combine, xs) => - switch (xs) { - | [] => answer - | [x, ...xs] => combine(x, every(answer, combine, xs)) - }; - -/* - Here are two functions which compute the largest and smallest integers in a - list of integers: - */ -let rec largest = xs => - switch (xs) { - | [] => neg_infinity - | [x, ...ys] => max(x, largest(ys)) - }; - -let rec smallest = xs => - switch (xs) { - | [] => infinity - | [x, ...ys] => min(x, smallest(ys)) - }; - -/* Let's rewrite them using every: */ -let simplerLargest = xs => failwith("For you to implement"); - -let simplerSmallest = xs => failwith("For you to implement"); - -Test.runAll([ - (simplerSmallest([]) == infinity, "simpler smallest"), - (simplerSmallest([55.]) == 55., "simpler smallest"), - (simplerSmallest([5., (-5.), 1., (-1.)]) == (-5.), "simpler smallest"), - (simplerSmallest([5., 5., 1., 1.]) == 1., "simpler smallest"), - (simplerLargest([]) == neg_infinity, "simpler largest"), - (simplerLargest([55.]) == 55., "simpler largest"), - (simplerLargest([5., (-5.), 1., (-1.)]) == 5., "simpler largest"), - (simplerLargest([5., 5., 1., 1.]) == 5., "simpler largest"), +/* Here is [every] from the "Sum Product" problem */ +let rec every = (answer, combine, xs) => + switch (xs) { + | [] => answer + | [x, ...xs] => combine(x, every(answer, combine, xs)) + }; + +/* + Here are two functions which compute the largest and smallest integers in a + list of integers: + */ +let rec largest = xs => + switch (xs) { + | [] => neg_infinity + | [x, ...ys] => max(x, largest(ys)) + }; + +let rec smallest = xs => + switch (xs) { + | [] => infinity + | [x, ...ys] => min(x, smallest(ys)) + }; + +/* Let's rewrite them using every: */ +let simplerLargest = xs => every(neg_infinity, max, xs); + +let simplerSmallest = xs => every(infinity, min, xs); + +Test.runAll([ + (simplerSmallest([]) == infinity, "simpler smallest"), + (simplerSmallest([55.]) == 55., "simpler smallest"), + (simplerSmallest([5., (-5.), 1., (-1.)]) == (-5.), "simpler smallest"), + (simplerSmallest([5., 5., 1., 1.]) == 1., "simpler smallest"), + (simplerLargest([]) == neg_infinity, "simpler largest"), + (simplerLargest([55.]) == 55., "simpler largest"), + (simplerLargest([5., (-5.), 1., (-1.)]) == 5., "simpler largest"), + (simplerLargest([5., 5., 1., 1.]) == 5., "simpler largest"), ]); \ No newline at end of file diff --git a/src/exercises/14-variants/variants.re b/src/exercises/14-variants/variants.re index d0ac307..59b5d18 100644 --- a/src/exercises/14-variants/variants.re +++ b/src/exercises/14-variants/variants.re @@ -1,59 +1,66 @@ -/* - As in most languages, you can define your own types. - The keyword "type" introduces a type definition. - - One of the non-basic types in Reason is called the variant type. - Variant types are similar to Enums in other languages. They are - types which may take on multiple forms, where each form is marked - by an explicit tag. A variant type is defined as follows: - */ -type color = - | Red - | Green - | Blue; - -/* Variants are very useful in combination with pattern matching */ -let toString = color => - switch (color) { - | Red => "red" - | Green => "green" - | Blue => "blue" - }; - -/* - Reason variants are in many ways more powerful than Enums because the different - constructors of your variant can include data in them. Here's an example: - */ -type cardValue = - | Ace - | King - | Queen - | Jack - | Number(int); - -let oneCardValue: cardValue = Queen; - -let anotherCardValue: cardValue = Number(8); - -let cardValueToString = cardValue => - switch (cardValue) { - | Ace => "Ace" - | King => "King" - | Queen => "Queen" - | Jack => "Jack" - | Number(i) => string_of_int(i) - }; - -/* - Write a function that computes the score of a card (aces should score 11 - and face cards should score 10). - */ -let cardValueToScore = cardValue => failwith("For you to implement"); - -Test.runAll([ - (cardValueToScore(Ace) == 11, "card value to score"), - (cardValueToScore(King) == 10, "card value to score"), - (cardValueToScore(Queen) == 10, "card value to score"), - (cardValueToScore(Jack) == 10, "card value to score"), - (cardValueToScore(Number(5)) == 5, "card value to score"), +/* + As in most languages, you can define your own types. + The keyword "type" introduces a type definition. + + One of the non-basic types in Reason is called the variant type. + Variant types are similar to Enums in other languages. They are + types which may take on multiple forms, where each form is marked + by an explicit tag. A variant type is defined as follows: + */ +type color = + | Red + | Green + | Blue; + +/* Variants are very useful in combination with pattern matching */ +let toString = color => + switch (color) { + | Red => "red" + | Green => "green" + | Blue => "blue" + }; + +/* + Reason variants are in many ways more powerful than Enums because the different + constructors of your variant can include data in them. Here's an example: + */ +type cardValue = + | Ace + | King + | Queen + | Jack + | Number(int); + +let oneCardValue: cardValue = Queen; + +let anotherCardValue: cardValue = Number(8); + +let cardValueToString = cardValue => + switch (cardValue) { + | Ace => "Ace" + | King => "King" + | Queen => "Queen" + | Jack => "Jack" + | Number(i) => string_of_int(i) + }; + +/* + Write a function that computes the score of a card (aces should score 11 + and face cards should score 10). + */ +let cardValueToScore = cardValue => + switch (cardValue) { + | Ace => 11 + | King => 10 + | Queen => 10 + | Jack => 10 + | Number(i) => i + }; + +Test.runAll([ + (cardValueToScore(Ace) == 11, "card value to score"), + (cardValueToScore(King) == 10, "card value to score"), + (cardValueToScore(Queen) == 10, "card value to score"), + (cardValueToScore(Jack) == 10, "card value to score"), + (cardValueToScore(Number(5)) == 5, "card value to score"), ]); \ No newline at end of file diff --git a/src/exercises/15-tuples/tuples.re b/src/exercises/15-tuples/tuples.re index 5329b82..c80f66f 100644 --- a/src/exercises/15-tuples/tuples.re +++ b/src/exercises/15-tuples/tuples.re @@ -1,72 +1,76 @@ -/* - Another non-basic type in Reason is a tuple. A tuple is an ordered collection - of values that can each be of a different type. - */ -type intStringAndChar = (int, string, char); - -/* Tuples are created by supplying values in place of their basic types: */ -let example: intStringAndChar = (5, "hello", 'A'); - -/* You can also extract the components of a tuple: */ -let (i, s, c) = example; - -let () = { - assert (i == 5); - assert (s == "hello"); - assert (c == 'A'); -}; - -/* - Consider a coordinate type containing the x and y values of a coordinate. - Write a function that computes the sum of two coordinates. - */ -type coordinate = (int, int); - -/* TODO */ -let add = (coord1, coord2) => failwith("For you to implement"); - -/* Now consider a name type containing strings representing first and last name. */ -type name = (string, string); - -/* Or an initials type containing chars representing first and last initials */ -type initials = (char, char); - -/* - Say we want to write a function that extracts the first element from a coordinate, - name, or initials. We currently can't write that because they all have different - types. - - Lets define a new pair type which is parameterized over the type contained in - the pair. We write this as: - */ -type pair('a) = ('a, 'a); - -/* - Our types defined above could be rewritten as - - type coordinate = pair(int) - type name = pair(string) - type initials = pair(char) - - We can construct pairs just like we construct regular tuples - */ -let intPair: pair(int) = (5, 7); - -let stringPair: pair(string) = ("foo", "bar"); - -let nestedCharPair: pair(pair(char)) = (('a', 'b'), ('c', 'd')); - -/* Write functions to extract the first and second elements from a pair. */ -/* let first: pair('a) => 'a */ -/* TODO */ -let first = pair => failwith("For you to implement"); - -/* let second: pair('a) => 'a */ -/* TODO */ -let second = pair => failwith("For you to implement"); - -Test.runAll([ - (add((1, 2), (3, 4)) == (4, 6), "add"), - (first(("foo", "bar")) == "foo", "first"), - (second(('a', 'b')) == 'b', "second"), +/* + Another non-basic type in Reason is a tuple. A tuple is an ordered collection + of values that can each be of a different type. + */ +type intStringAndChar = (int, string, char); + +/* Tuples are created by supplying values in place of their basic types: */ +let example: intStringAndChar = (5, "hello", 'A'); + +/* You can also extract the components of a tuple: */ +let (i, s, c) = example; + +let () = { + assert (i == 5); + assert (s == "hello"); + assert (c == 'A'); +}; + +/* + Consider a coordinate type containing the x and y values of a coordinate. + Write a function that computes the sum of two coordinates. + */ +type coordinate = (int, int); + +/* TODO */ +let add = (coord1, coord2) => { + let (a1, b1) = coord1; + let (a2, b2) = coord2; + (a1 + a2, b1 + b2); +}; + +/* Now consider a name type containing strings representing first and last name. */ +type name = (string, string); + +/* Or an initials type containing chars representing first and last initials */ +type initials = (char, char); + +/* + Say we want to write a function that extracts the first element from a coordinate, + name, or initials. We currently can't write that because they all have different + types. + + Lets define a new pair type which is parameterized over the type contained in + the pair. We write this as: + */ +type pair('a) = ('a, 'a); + +/* + Our types defined above could be rewritten as + + type coordinate = pair(int) + type name = pair(string) + type initials = pair(char) + + We can construct pairs just like we construct regular tuples + */ +let intPair: pair(int) = (5, 7); + +let stringPair: pair(string) = ("foo", "bar"); + +let nestedCharPair: pair(pair(char)) = (('a', 'b'), ('c', 'd')); + +/* Write functions to extract the first and second elements from a pair. */ +/* let first: pair('a) => 'a */ +/* TODO try with fst */ +let first = pair => fst(pair); + +/* let second: pair('a) => 'a */ +/* TODO snd */ +let second = pair => snd(pair); + +Test.runAll([ + (add((1, 2), (3, 4)) == (4, 6), "add"), + (first(("foo", "bar")) == "foo", "first"), + (second(('a', 'b')) == 'b', "second"), ]); \ No newline at end of file diff --git a/src/exercises/16-labelled_arguments/labelledArguments.re b/src/exercises/16-labelled_arguments/labelledArguments.re index f575d52..407e986 100644 --- a/src/exercises/16-labelled_arguments/labelledArguments.re +++ b/src/exercises/16-labelled_arguments/labelledArguments.re @@ -1,51 +1,51 @@ -/* - The following function has the signature: - - let divide : int -> int -> int - - Looking at just the signature, it's not obvious which int argument is - the dividend and which is the divisor. - */ -let divide = (dividend, divisor) => dividend / divisor; - -/* - We can fix this using labelled arguments. - - To label an argument in a signature, and when defining a function, we - put a tilde (~) before the name of the argument. - - The following function has the signature: - - let divide: (~dividend:int, ~divisor:int) => int - */ -let divide = (~dividend, ~divisor) => dividend / divisor; - -/* - We can then call it using: divide(~dividend=9, ~divisor=3) - - Labelled arguments can be passed in in any order. - - We can also pass variables into the labelled argument: - - let dividend = 9; - let divisor = 3; - divide(~dividend=dividend, ~divisor=divisor) - - If the variable name happens to be the same as the labelled argument, we - don't even have to write it twice: - - let dividend = 9; - let divisor = 3; - divide(~dividend, ~divisor) - - This short-hand syntax is called punning. - - Now implement [modulo(~dividend, ~divisor)] using our version of divide with - labelled arguments (e.g. [modulo(~dividend:7, ~divisor:2)] should equal 1) - */ -let modulo = (~dividend, ~divisor) => failwith("For you to implement"); - -Test.runAll([ - (modulo(~dividend=17, ~divisor=5) == 2, "modulo"), - (modulo(~dividend=99, ~divisor=9) == 0, "modulo"), +/* + The following function has the signature: + + let divide : int -> int -> int + + Looking at just the signature, it's not obvious which int argument is + the dividend and which is the divisor. + */ +let divide = (dividend, divisor) => dividend / divisor; + +/* + We can fix this using labelled arguments. + + To label an argument in a signature, and when defining a function, we + put a tilde (~) before the name of the argument. + + The following function has the signature: + + let divide: (~dividend:int, ~divisor:int) => int + */ +let divide = (~dividend, ~divisor) => dividend / divisor; + +/* + We can then call it using: divide(~dividend=9, ~divisor=3) + + Labelled arguments can be passed in in any order. + + We can also pass variables into the labelled argument: + + let dividend = 9; + let divisor = 3; + divide(~dividend=dividend, ~divisor=divisor) + + If the variable name happens to be the same as the labelled argument, we + don't even have to write it twice: + + let dividend = 9; + let divisor = 3; + divide(~dividend, ~divisor) + + This short-hand syntax is called punning. + + Now implement [modulo(~dividend, ~divisor)] using our version of divide with + labelled arguments (e.g. [modulo(~dividend:7, ~divisor:2)] should equal 1) + */ +let modulo = (~dividend, ~divisor) => dividend mod divisor; + +Test.runAll([ + (modulo(~dividend=17, ~divisor=5) == 2, "modulo"), + (modulo(~dividend=99, ~divisor=9) == 0, "modulo"), ]); \ No newline at end of file diff --git a/src/exercises/17-options/options.re b/src/exercises/17-options/options.re index 8960527..5e2b64d 100644 --- a/src/exercises/17-options/options.re +++ b/src/exercises/17-options/options.re @@ -1,51 +1,55 @@ -/* - Many languages have a concept of "Null", which describes that some data is - absent. In Reason, we can model the presence/absence data using ordinary - variants. - - Note: we're defining the [option] type here to show you that it isn't magic. - In real life you would always use the [option] type provided by the standard - library. - */ -type option('a) = - | None - | Some('a); - -/* - An [option('a)] is either [None], meaning absence of data, or [Some x] - meaning the data exists, and that data specifically is [x]. Here's an - example: - */ -let whatNumberAmIThinking = (myNumber: option(int)) => - switch (myNumber) { - | None => "I'm not thinking of any number!" - | Some(number) => "My number is: " ++ string_of_int(number) - }; - -assert (whatNumberAmIThinking(None) == "I'm not thinking of any number!"); - -assert (whatNumberAmIThinking(Some(7)) == "My number is: 7"); - -/* - Implement the function [safeDivide(~dividend, ~divisor)], which takes two - ints and returns an int option. It should return None if [divisor = 0], and - otherwise returns [Some(x)] where [x] is the division result - */ -let safeDivide = (~dividend, ~divisor) => failwith("For you to implement"); - -Test.runAll([ - ( - switch (safeDivide(~dividend=3, ~divisor=2)) { - | Some(1) => true - | _ => false - }, - "safe divide", - ), - ( - switch (safeDivide(~dividend=3, ~divisor=0)) { - | None => true - | _ => false - }, - "safe divide", - ), +/* + Many languages have a concept of "Null", which describes that some data is + absent. In Reason, we can model the presence/absence data using ordinary + variants. + + Note: we're defining the [option] type here to show you that it isn't magic. + In real life you would always use the [option] type provided by the standard + library. + */ +type option('a) = + | None + | Some('a); + +/* + An [option('a)] is either [None], meaning absence of data, or [Some x] + meaning the data exists, and that data specifically is [x]. Here's an + example: + */ +let whatNumberAmIThinking = (myNumber: option(int)) => + switch (myNumber) { + | None => "I'm not thinking of any number!" + | Some(number) => "My number is: " ++ string_of_int(number) + }; + +assert (whatNumberAmIThinking(None) == "I'm not thinking of any number!"); + +assert (whatNumberAmIThinking(Some(7)) == "My number is: 7"); + +/* + Implement the function [safeDivide(~dividend, ~divisor)], which takes two + ints and returns an int option. It should return None if [divisor = 0], and + otherwise returns [Some(x)] where [x] is the division result + */ +let safeDivide = (~dividend, ~divisor: int) => + switch (divisor) { + | 0 => None + | _ => Some(dividend / divisor) + }; + +Test.runAll([ + ( + switch (safeDivide(~dividend=3, ~divisor=2)) { + | Some(1) => true + | _ => false + }, + "safe divide", + ), + ( + switch (safeDivide(~dividend=3, ~divisor=0)) { + | None => true + | _ => false + }, + "safe divide", + ), ]); \ No newline at end of file diff --git a/src/exercises/18-anonymous_functions/anonymousFunctions.re b/src/exercises/18-anonymous_functions/anonymousFunctions.re index 6c4006b..57f0c96 100644 --- a/src/exercises/18-anonymous_functions/anonymousFunctions.re +++ b/src/exercises/18-anonymous_functions/anonymousFunctions.re @@ -1,58 +1,62 @@ -/* - In Reason, functions are values, so we can pass them in as - arguments to other functions. - - To represent a function in a signature, you wrap its type in parenthesis, - with arrows separating arguments and the type of the functions result. - - Recall: a function called add1 which takes an integer and returns an integer - has the type - let add1 : int => int - - so, to use that signature in a type, we'd write - (int => int) - - We now define a function called mapOption. - mapOption takes a function and an option. - - If the option has a value of None, mapOption returns None - If the option has a value of Some x, the function is called on x, and - wrapped up in a Some. - - This may seem unintuitive, but this kind of function is very useful. - It means that you can continue working on data, and ignore if - the data isn't there (no null pointer exceptions!) - - The signature for the function is - - let mapOption : (('a => 'b), option('a)) => option('b) - */ -let mapOption = (f, opt) => - switch (opt) { - | None => None - | Some(i) => Some(f(i)) - }; - -let double = i => 2 * i; - -let () = assert (mapOption(double, None) == None); - -let () = assert (mapOption(double, Some(2)) == Some(4)); - -/* - Instead of defining the function double beforehand, we can use an anonymous - function. - */ -let () = assert (mapOption(i => 2 * i, Some(2)) == Some(4)); - -/* - Define a function applyIfNonzero which takes a function from (int => int) - and an int, and applies the function if the integer is not zero, and - otherwise just returns 0. - */ -let applyIfNonzero = (f, i) => failwith("For you to implement"); - -Test.runAll([ - (applyIfNonzero(x => 10 / x, 0) == 0, "apply if non-zero"), - (applyIfNonzero(x => 10 / x, 5) == 2, "apply if non-zero"), +/* + In Reason, functions are values, so we can pass them in as + arguments to other functions. + + To represent a function in a signature, you wrap its type in parenthesis, + with arrows separating arguments and the type of the functions result. + + Recall: a function called add1 which takes an integer and returns an integer + has the type + let add1 : int => int + + so, to use that signature in a type, we'd write + (int => int) + + We now define a function called mapOption. + mapOption takes a function and an option. + + If the option has a value of None, mapOption returns None + If the option has a value of Some x, the function is called on x, and + wrapped up in a Some. + + This may seem unintuitive, but this kind of function is very useful. + It means that you can continue working on data, and ignore if + the data isn't there (no null pointer exceptions!) + + The signature for the function is + + let mapOption : (('a => 'b), option('a)) => option('b) + */ +let mapOption = (f, opt) => + switch (opt) { + | None => None + | Some(i) => Some(f(i)) + }; + +let double = i => 2 * i; + +let () = assert (mapOption(double, None) == None); + +let () = assert (mapOption(double, Some(2)) == Some(4)); + +/* + Instead of defining the function double beforehand, we can use an anonymous + function. + */ +let () = assert (mapOption(i => 2 * i, Some(2)) == Some(4)); + +/* + Define a function applyIfNonzero which takes a function from (int => int) + and an int, and applies the function if the integer is not zero, and + otherwise just returns 0. + */ +let applyIfNonzero = (f, opt) => + switch (opt) { + | 0 => 0 + | _ => f(opt) + }; + +Test.runAll([ + (applyIfNonzero(x => 10 / x, 0) == 0, "apply if non-zero"), + (applyIfNonzero(x => 10 / x, 5) == 2, "apply if non-zero"), ]); \ No newline at end of file diff --git a/src/exercises/19-list_operations/listOperations.re b/src/exercises/19-list_operations/listOperations.re index 0fbc972..f77d9bb 100644 --- a/src/exercises/19-list_operations/listOperations.re +++ b/src/exercises/19-list_operations/listOperations.re @@ -1,95 +1,100 @@ -/* - It is common in all programming languages to want to store and operate on - collections of the same data type. As you have seen in previous exercises, - we can achieve this in Reason using the list('a) type, e.g. list(int), - list(bool), list(list(string)). - - When you first learn to program in languages like C and Java, you use "for" - loops to operate on all the elements of an array, e.g.: - - for (int i = 0; i < array.length(); i++) { do_something_with(array[i]); } - - In Reason, we use "higher order functions", in other words, functions which - take other functions as input. Let's take a look at the [List.map] function, - which has signature: - - let map : (('a => 'b), list('a)) => list('b) - - Let's read this signature together. It takes two arguments: - 1) a function from some type ['a] to some other type ['b] - 2) a list of ['a]s - and then it returns a list of ['b]s. - - What map(f, la) does is take your function [f], apply it to each element of - [la], and returns a new list [lb] here the the [i]th element of [lb] is - equal to the function [f] applied to the [i]th element of [la]. - - Let's see some examples: - */ -let myInts: list(int) = [1, 2, 3, 4, 5]; - -let doubleMyInts = ints : list(int) => List.map(x => x * 2, ints); - -let () = assert (doubleMyInts(myInts) == [2, 4, 6, 8, 10]); - -let myStrings = ints : list(string) => List.map(string_of_int, ints); - -let () = assert (myStrings(myInts) == ["1", "2", "3", "4", "5"]); - -/* - Exercise: implement the value [myNewInts], which is obtained by adding 1 to - each element of [myInts] - */ -let myNewInts = ints => failwith("For you to implement"); - -/* - If the function you want to perform on each element of your list is one that - returns [unit], meaning that all it does is perform some side-effect (like - [Js.log]), there is a higher-order function called [List.iter] which has the - following signature: - - let iter: ('a => unit, list('a)) => unit - */ -let () = List.iter(i => Js.log("here's an int: " ++ i), myStrings(myInts)); - -/* - Another thing you might want to do with a list is combine all the elements - together in some way. Here is the signature of [List.fold_left]: - - let fold_left: (('b, 'a) => 'b, 'b, list('a)) => 'b - - Let's say your list [l] contains [a1, a2, a3]. Then if you call - fold_left(f, i, l), then it will end up computing: - - (f (f (f i a1) a2) a3) - - Here's an example of using [fold_left] to compute a sum: - */ -let sumOfMyInts = ints : int => - List.fold_left((total, myInt) => total + myInt, 0, ints); - -let () = assert (sumOfMyInts(myInts) == 15); - -/* - Exercise: use [List.fold_left] to compute the number of elements of - [myInts] that are even - - Hint: Use the infix operator `mod`. - (4 mod 2 == 0) - */ -let numEvenInts = ints => failwith("For you to implement"); - -/* - Here's one more example of a useful list function: [List.find]: - - let find: ('a => bool, list('a)) => 'a - - */ -let firstNumGreaterThan3 = ints => List.find(x => x > 3, ints); - -let () = assert (firstNumGreaterThan3(myInts) == 4); - -Test.runAll([ - (myNewInts(myInts) == [2, 3, 4, 5, 6], "my new ints"), - (numEvenInts(myInts) == 2, "num even ints"), +/* + It is common in all programming languages to want to store and operate on + collections of the same data type. As you have seen in previous exercises, + we can achieve this in Reason using the list('a) type, e.g. list(int), + list(bool), list(list(string)). + + When you first learn to program in languages like C and Java, you use "for" + loops to operate on all the elements of an array, e.g.: + + for (int i = 0; i < array.length(); i++) { do_something_with(array[i]); } + + In Reason, we use "higher order functions", in other words, functions which + take other functions as input. Let's take a look at the [List.map] function, + which has signature: + + let map : (('a => 'b), list('a)) => list('b) + + Let's read this signature together. It takes two arguments: + 1) a function from some type ['a] to some other type ['b] + 2) a list of ['a]s + and then it returns a list of ['b]s. + + What map(f, la) does is take your function [f], apply it to each element of + [la], and returns a new list [lb] here the the [i]th element of [lb] is + equal to the function [f] applied to the [i]th element of [la]. + + Let's see some examples: + */ +let myInts: list(int) = [1, 2, 3, 4, 5]; + +let doubleMyInts = ints : list(int) => List.map(x => x * 2, ints); + +let () = assert (doubleMyInts(myInts) == [2, 4, 6, 8, 10]); + +let myStrings = ints : list(string) => List.map(string_of_int, ints); + +let () = assert (myStrings(myInts) == ["1", "2", "3", "4", "5"]); + +/* + Exercise: implement the value [myNewInts], which is obtained by adding 1 to + each element of [myInts] + */ +let myNewInts = ints => List.map(x => x + 1, ints); + +/* + If the function you want to perform on each element of your list is one that + returns [unit], meaning that all it does is perform some side-effect (like + [Js.log]), there is a higher-order function called [List.iter] which has the + following signature: + + let iter: ('a => unit, list('a)) => unit + */ +let () = List.iter(i => Js.log("here's an int: " ++ i), myStrings(myInts)); + +/* + Another thing you might want to do with a list is combine all the elements + together in some way. Here is the signature of [List.fold_left]: + + let fold_left: (('b, 'a) => 'b, 'b, list('a)) => 'b + + Let's say your list [l] contains [a1, a2, a3]. Then if you call + fold_left(f, i, l), then it will end up computing: + + (f (f (f i a1) a2) a3) + + Here's an example of using [fold_left] to compute a sum: + */ +let sumOfMyInts = ints : int => + List.fold_left((total, myInt) => total + myInt, 0, ints); + +let () = assert (sumOfMyInts(myInts) == 15); + +/* + Exercise: use [List.fold_left] to compute the number of elements of + [myInts] that are even + + Hint: Use the infix operator `mod`. + (4 mod 2 == 0) + */ +let numEvenInts = ints => + List.fold_left( + (total, myInt) => myInt mod 2 == 0 ? total + 1 : total, + 0, + ints, + ); + +/* + Here's one more example of a useful list function: [List.find]: + + let find: ('a => bool, list('a)) => 'a + + */ +let firstNumGreaterThan3 = ints => List.find(x => x > 3, ints); + +let () = assert (firstNumGreaterThan3(myInts) == 4); + +Test.runAll([ + (myNewInts(myInts) == [2, 3, 4, 5, 6], "my new ints"), + (numEvenInts(myInts) == 2, "num even ints"), ]); \ No newline at end of file diff --git a/src/exercises/20-reading_sigs/readingSigs.re b/src/exercises/20-reading_sigs/readingSigs.re index 4f26ef8..d3df77b 100644 --- a/src/exercises/20-reading_sigs/readingSigs.re +++ b/src/exercises/20-reading_sigs/readingSigs.re @@ -1,80 +1,86 @@ -/* - Reason, like many other languages, provides a way to interact with code via - interfaces. This allows implementation details to be hidden away, and for - grouped units of code to restrict how they are used. - - Here's an example of a module signature coupled with an implementation. The - signature is wrapped in curly braces similar to the implementation. - */ -module Example: { - /* Here, 'let' indicates that we are exposing a value. This value is an integer */ - let theMeaningOfLifeTheUniverseAndEverything: int; - /* - To declare functions, again we use 'let' - in Reason, functions are values. - This value takes an integer as a parameter and returns an integer - */ - let subtractOne: int => int; -} = { - let theMeaningOfLifeTheUniverseAndEverything = 42; - let subtractOne = x => x - 1; -}; - -/* Here's how we use these values */ -let oneLessThanTheMeaningOfLifeEtc = - Example.subtractOne(Example.theMeaningOfLifeTheUniverseAndEverything); - -assert (oneLessThanTheMeaningOfLifeEtc == 41); - -/* - Types can be exposed via signatures in Reason as well. Here's an example of declaring - an "abstract" type - one where the definition of the type is not exposed. - */ -module AbstractTypeExample: { - /* We do not let the user know that [t] is an integer */ - type t; - /* This function allows [t] to be coerced into an integer */ - let toInt: t => int; - /* Users need some way to start with some [t] */ - let zero: t; - let one: t; - /* Let them do something with the [t]*/ - let add: (t, t) => t; -} = { - type t = int; - let toInt = x => x; - let zero = 0; - let one = 1; - let add = (+); -}; - -/* Here's an example of adding 2 and 2 */ -let two = - AbstractTypeExample.add(AbstractTypeExample.one, AbstractTypeExample.one); - -let four = AbstractTypeExample.toInt(AbstractTypeExample.add(two, two)); - -assert (four == 4); - -module Fraction: { - type t; - /* - TODO: Add signatures for the create and value functions to expose them in - the Fraction module. - */ -} = { - type t = (int, int); - let create = (~numerator, ~denominator) => (numerator, denominator); - let value = ((numerator, denominator)) => - float_of_int(numerator) /. float_of_int(denominator); -}; -/* TODO: After adding signatures above uncomment the tests below */ -/* Test.runAll([ - ( - Fraction.value(Fraction.create(~numerator=5, ~denominator=2)) == 2.5, - "Fraction.value", - ), - ( - Fraction.value(Fraction.create(~numerator=4, ~denominator=10)) == 0.4, - "Fraction.value", - ), - ]); */ \ No newline at end of file +/* + Reason, like many other languages, provides a way to interact with code via + interfaces. This allows implementation details to be hidden away, and for + grouped units of code to restrict how they are used. + + Here's an example of a module signature coupled with an implementation. The + signature is wrapped in curly braces similar to the implementation. + */ +module Example: { + /* Here, 'let' indicates that we are exposing a value. This value is an integer */ + let theMeaningOfLifeTheUniverseAndEverything: int; + /* + To declare functions, again we use 'let' - in Reason, functions are values. + This value takes an integer as a parameter and returns an integer + */ + let subtractOne: int => int; +} = { + let theMeaningOfLifeTheUniverseAndEverything = 42; + let subtractOne = x => x - 1; +}; + +/* Here's how we use these values */ +let oneLessThanTheMeaningOfLifeEtc = + Example.subtractOne(Example.theMeaningOfLifeTheUniverseAndEverything); + +assert (oneLessThanTheMeaningOfLifeEtc == 41); + +/* + Types can be exposed via signatures in Reason as well. Here's an example of declaring + an "abstract" type - one where the definition of the type is not exposed. + */ +module AbstractTypeExample: { + /* We do not let the user know that [t] is an integer */ + type t; + /* This function allows [t] to be coerced into an integer */ + let toInt: t => int; + /* Users need some way to start with some [t] */ + let zero: t; + let one: t; + /* Let them do something with the [t]*/ + let add: (t, t) => t; +} = { + type t = int; + let toInt = x => x; + let zero = 0; + let one = 1; + let add = (+); +}; + +/* Here's an example of adding 2 and 2 */ +let two = + AbstractTypeExample.add(AbstractTypeExample.one, AbstractTypeExample.one); + +let four = AbstractTypeExample.toInt(AbstractTypeExample.add(two, two)); + +assert (four == 4); + +module Fraction: { + type t; + /* + TODO: Add signatures for the create and value functions to expose them in + the Fraction module. + */ + let create: (~numerator: int, ~denominator: int) => t; + let value: t => float; +} = { + type t = (int, int); + let create = (~numerator: int, ~denominator: int) => ( + numerator, + denominator, + ); + let value = ((numerator, denominator): t) => + float_of_int(numerator) /. float_of_int(denominator); +}; + +/* TODO: After adding signatures above uncomment the tests below */ +Test.runAll([ + ( + Fraction.value(Fraction.create(~numerator=5, ~denominator=2)) == 2.5, + "Fraction.value", + ), + ( + Fraction.value(Fraction.create(~numerator=4, ~denominator=10)) == 0.4, + "Fraction.value", + ), +]); \ No newline at end of file diff --git a/src/exercises/21-writing_list_operations/writingListOperations.re b/src/exercises/21-writing_list_operations/writingListOperations.re index ad65411..3ed6923 100644 --- a/src/exercises/21-writing_list_operations/writingListOperations.re +++ b/src/exercises/21-writing_list_operations/writingListOperations.re @@ -1,120 +1,127 @@ -/* - As we've seen, in Reason, instead of for loops we use "higher order functions" - to itereate over collections like lists. Higher order functions, in other - words, are functions that take other functions as input. Let's take a deeper - look at the [List.fold_left] function, which has the signature: - - let fold_left: (('a, 'b) => 'a, 'a, list('b)) => 'a - - 'a is the type of the accumulator, and 'b is the type of the values in the - input list. - - The 1st argument is a function for updating the accumulator. The 2nd - argument is the initial accumulator value. The final argument is the list - to process. - - List.fold_left walks over the list from left to right, updating the - accumulator at each step and returning the final value of the accumulator - when it's done. - - Let's revisit the sumOfMyInts example we've seen before. In this case, types - 'a and 'b are both equal to int. - */ -let ints = [1, 2, 3]; - -let sumOfMyInts = List.fold_left((total, myInt) => total + myInt, 0, ints); - -let () = assert (6 == sumOfMyInts); - -/* Now let's use List.fold_left to write some other useful List functions. */ -module MyList: { - /* - map(f, list) takes a function [f] from ('a => 'b) and a list('a) and - returns a list('b) (e.g. [f] applied to each element) - */ - let map: ('a => 'b, list('a)) => list('b); - /* - iter(f, list) calls [f] on each element in [list]. Since [f] returns - [unit], there is nothing to return - */ - let iter: ('a => unit, list('a)) => unit; - /* - filter(f, list) runs [f] on each element in [list] and returns a new list - consisting of all elements [f] returned [true] for - */ - let filter: ('a => bool, list('a)) => list('a); -} = { - /* TODO */ - let map = (f, lst) => failwith("For you to implement"); - /* TODO */ - let iter = (f, lst) => failwith("For you to implement"); - /* TODO */ - let filter = (f, lst) => failwith("For you to implement"); -}; - -/* - Here are some other list functions that you may find useful in future - exercises. - - You can see the full signature of the List module here: - - https://reasonml.github.io/api/List.html - - List.hd returns the first element of the list. It raises an exception if - called on an empty list. The signature is: - - let hd: list('a) => 'a; - */ -let () = assert (List.hd([1, 2, 3]) == 1); - -/* - Similarly, List.tl returns all but the first element of the list. It also raises - an exception if called on an empty list. The signature is: - - let tl: list('a) => list('a); - */ -let () = assert (List.tl([1, 2, 3]) == [2, 3]); - -/* - List.rev returns the reverse of the input list. - - let rev: list('a) => list('a); - */ -let () = assert (List.rev([1, 2, 3]) == [3, 2, 1]); - -/* - List.mem returns a bool indicating if the given element is contained in the - list. - - let mem: ('a, list('a)) => bool; - */ -let () = assert (List.mem(3, [1, 2, 3])); - -/* - List.sort returns a sorted list in increasing order according to the specified - comparison function. The comparison function should return a negative number to - indicate the first element is smaller, 0 to indicate they are equal, and a positive - number to indicate the first element is larger. - - let sort: (('a, 'a) => int, list('a)) => list('a); - */ -let () = assert (List.sort((x, y) => x - y, [3, 1, 2]) == [1, 2, 3]); - -let acc = ref(0); - -MyList.iter( - x => - if (x > acc^) { - acc := x; - }, - [1, 8, 5, 2, 7, 3], -); - -Test.runAll([ - (MyList.map(x => 2 * x, [1, 2, 3, 4]) == [2, 4, 6, 8], "MyList.map"), - (acc^ == 8, "MyList.iter"), - ( - MyList.filter(x => x mod 2 == 0, [1, 3, 7, 8, 9, 2]) == [8, 2], - "MyList.filter", - ), +/* + As we've seen, in Reason, instead of for loops we use "higher order functions" + to itereate over collections like lists. Higher order functions, in other + words, are functions that take other functions as input. Let's take a deeper + look at the [List.fold_left] function, which has the signature: + + let fold_left: (('a, 'b) => 'a, 'a, list('b)) => 'a + + 'a is the type of the accumulator, and 'b is the type of the values in the + input list. + + The 1st argument is a function for updating the accumulator. The 2nd + argument is the initial accumulator value. The final argument is the list + to process. + + List.fold_left walks over the list from left to right, updating the + accumulator at each step and returning the final value of the accumulator + when it's done. + + Let's revisit the sumOfMyInts example we've seen before. In this case, types + 'a and 'b are both equal to int. + */ +let ints = [1, 2, 3]; + +let sumOfMyInts = List.fold_left((total, myInt) => total + myInt, 0, ints); + +let () = assert (6 == sumOfMyInts); + +/* Now let's use List.fold_left to write some other useful List functions. */ +module MyList: { + /* + map(f, list) takes a function [f] from ('a => 'b) and a list('a) and + returns a list('b) (e.g. [f] applied to each element) + */ + let map: ('a => 'b, list('a)) => list('b); + /* + iter(f, list) calls [f] on each element in [list]. Since [f] returns + [unit], there is nothing to return + */ + let iter: ('a => unit, list('a)) => unit; + /* + filter(f, list) runs [f] on each element in [list] and returns a new list + consisting of all elements [f] returned [true] for + */ + let filter: ('a => bool, list('a)) => list('a); +} = { + /* TODO */ + let map = (f, lst) => + List.fold_left((acc, value) => [f(value), ...acc], [], lst) |> List.rev; + /* TODO */ + let iter = (f, lst) => List.fold_left((_, value) => f(value), (), lst); + /* TODO */ + let filter = (f, lst) => + List.fold_left( + (acc, value) => f(value) ? [value, ...acc] : acc, + [], + lst, + ) + |> List.rev; +}; + +/* + Here are some other list functions that you may find useful in future + exercises. + + You can see the full signature of the List module here: + + https://reasonml.github.io/api/List.html + + List.hd returns the first element of the list. It raises an exception if + called on an empty list. The signature is: + + let hd: list('a) => 'a; + */ +let () = assert (List.hd([1, 2, 3]) == 1); + +/* + Similarly, List.tl returns all but the first element of the list. It also raises + an exception if called on an empty list. The signature is: + + let tl: list('a) => list('a); + */ +let () = assert (List.tl([1, 2, 3]) == [2, 3]); + +/* + List.rev returns the reverse of the input list. + + let rev: list('a) => list('a); + */ +let () = assert (List.rev([1, 2, 3]) == [3, 2, 1]); + +/* + List.mem returns a bool indicating if the given element is contained in the + list. + + let mem: ('a, list('a)) => bool; + */ +let () = assert (List.mem(3, [1, 2, 3])); + +/* + List.sort returns a sorted list in increasing order according to the specified + comparison function. The comparison function should return a negative number to + indicate the first element is smaller, 0 to indicate they are equal, and a positive + number to indicate the first element is larger. + + let sort: (('a, 'a) => int, list('a)) => list('a); + */ +let () = assert (List.sort((x, y) => x - y, [3, 1, 2]) == [1, 2, 3]); + +let acc = ref(0); + +MyList.iter( + x => + if (x > acc^) { + acc := x; + }, + [1, 8, 5, 2, 7, 3], +); + +Test.runAll([ + (MyList.map(x => 2 * x, [1, 2, 3, 4]) == [2, 4, 6, 8], "MyList.map"), + (acc^ == 8, "MyList.iter"), + ( + MyList.filter(x => x mod 2 == 0, [1, 3, 7, 8, 9, 2]) == [8, 2], + "MyList.filter", + ), ]); \ No newline at end of file diff --git a/src/exercises/22-records/records.re b/src/exercises/22-records/records.re index 862d61c..6d3223a 100644 --- a/src/exercises/22-records/records.re +++ b/src/exercises/22-records/records.re @@ -1,97 +1,101 @@ -/* - Reason allows you to define record types. These are like structs in C. - Or in Python, Ruby & Java, these are similar to data members or static - variables of a class. - */ -/* Here is a [person] record type that contains four fields. */ -/* The first field, called "age" is of type int. */ -type person = { - /* The name of the type is [person] */ - age: int, - firstName: string, - lastName: string, - numberOfCars: int, -}; - -/* - We can create a [person] like this. When defining and matching on a record, - the fields can be listed in any order. - */ -let anExample: person = { - firstName: "Cotton-eyed", - lastName: "Joe", - age: 22, - numberOfCars: 0, -}; - -/* - In order to get a field out of a record we use the "." operator: - variable.field - */ -let age: int = anExample.age; - -let () = assert (age == 22); - -/* We can also match on records to get field information. */ -let print_info = ({firstName, lastName, age, numberOfCars}) => { - print_endline(firstName); - print_endline(lastName); - print_endline("Age: " ++ string_of_int(age)); - print_endline("# of cars: " ++ string_of_int(numberOfCars)); -}; - -/* If we don't care about an argument we can ignore it using "= _" */ -let print_name = ({firstName, lastName, age: _, numberOfCars: _}) => { - print_endline(firstName); - print_endline(lastName); -}; - -/* - Finally, we can perform "functional updates" by replacing the value of a - field, yielding a brand new record. We use the ... spread operator to do - this. The original record isn't mutated. - - let addOneToAge : person -> person - */ -let addOneToAge = person => {...person, age: person.age + 1}; - -let () = assert (23 == addOneToAge(anExample).age); - -/* - Write a function that does different things for different people: - When the person's first name is "Jan", you should return a record with - the age set to 30. - - Otherwise, you should increase the number of cars by 6. - - let modify_person : person -> person - */ -let modifyPerson = (person: person) => failwith("For you to implement"); - -module ForTesting = { - let test_ex1: person = { - firstName: "Jan", - lastName: "Saffer", - age: 55, - numberOfCars: 0, - }; - let test_ex1': person = {...test_ex1, age: 30}; - let test_ex2: person = { - firstName: "Hugo", - lastName: "Heuzard", - age: 4, - numberOfCars: 55, - }; - let test_ex2': person = {...test_ex2, numberOfCars: 61}; -}; - -Test.runAll([ - ( - modifyPerson(ForTesting.test_ex1) == ForTesting.test_ex1', - "modifyPerson", - ), - ( - modifyPerson(ForTesting.test_ex2) == ForTesting.test_ex2', - "modifyPerson", - ), +/* + Reason allows you to define record types. These are like structs in C. + Or in Python, Ruby & Java, these are similar to data members or static + variables of a class. + */ +/* Here is a [person] record type that contains four fields. */ +/* The first field, called "age" is of type int. */ +type person = { + /* The name of the type is [person] */ + age: int, + firstName: string, + lastName: string, + numberOfCars: int, +}; + +/* + We can create a [person] like this. When defining and matching on a record, + the fields can be listed in any order. + */ +let anExample: person = { + firstName: "Cotton-eyed", + lastName: "Joe", + age: 22, + numberOfCars: 0, +}; + +/* + In order to get a field out of a record we use the "." operator: + variable.field + */ +let age: int = anExample.age; + +let () = assert (age == 22); + +/* We can also match on records to get field information. */ +let print_info = ({firstName, lastName, age, numberOfCars}) => { + print_endline(firstName); + print_endline(lastName); + print_endline("Age: " ++ string_of_int(age)); + print_endline("# of cars: " ++ string_of_int(numberOfCars)); +}; + +/* If we don't care about an argument we can ignore it using "= _" */ +let print_name = ({firstName, lastName, age: _, numberOfCars: _}) => { + print_endline(firstName); + print_endline(lastName); +}; + +/* + Finally, we can perform "functional updates" by replacing the value of a + field, yielding a brand new record. We use the ... spread operator to do + this. The original record isn't mutated. + + let addOneToAge : person -> person + */ +let addOneToAge = person => {...person, age: person.age + 1}; + +let () = assert (23 == addOneToAge(anExample).age); + +/* + Write a function that does different things for different people: + When the person's first name is "Jan", you should return a record with + the age set to 30. + + Otherwise, you should increase the number of cars by 6. + + let modify_person : person -> person + */ +let modifyPerson = (person: person) => + switch (person.firstName) { + | "Jan" => {...person, age: 30} + | _ => {...person, numberOfCars: person.numberOfCars + 6} + }; + +module ForTesting = { + let test_ex1: person = { + firstName: "Jan", + lastName: "Saffer", + age: 55, + numberOfCars: 0, + }; + let test_ex1': person = {...test_ex1, age: 30}; + let test_ex2: person = { + firstName: "Hugo", + lastName: "Heuzard", + age: 4, + numberOfCars: 55, + }; + let test_ex2': person = {...test_ex2, numberOfCars: 61}; +}; + +Test.runAll([ + ( + modifyPerson(ForTesting.test_ex1) == ForTesting.test_ex1', + "modifyPerson", + ), + ( + modifyPerson(ForTesting.test_ex2) == ForTesting.test_ex2', + "modifyPerson", + ), ]); \ No newline at end of file diff --git a/src/exercises/23-mutable_records/mutableRecords.re b/src/exercises/23-mutable_records/mutableRecords.re index 073c9b9..7d40693 100644 --- a/src/exercises/23-mutable_records/mutableRecords.re +++ b/src/exercises/23-mutable_records/mutableRecords.re @@ -1,59 +1,64 @@ -/* - Sometimes rather than redefining the record you would like to have a field or - a set of fields that you can modify on the fly. - - In Reason if you want to have a field in a record that can be updated in place, - you must use some additional syntax. The mutable keyword makes the field - modifiable. - - Then you can update those fields in place with the = operator. - */ -type color = - | Red - | Yellow - | Green; - -type stoplight = { - location: string, /* stoplights don't usually move */ - mutable color /* but they often change color */ -}; - -/* On creation mutable fields are defined just like normal fields */ -let anExample: stoplight = { - location: "The corner of Vesey Street and the West Side highway", - color: Red, -}; - -/* - Now rather than using a functional update we can use a mutable update. - This doesn't return a new stoplight, it modifies the input stoplight. - */ -let setColor = (stoplight, color) => stoplight.color = color; - -/* - Since we know that stoplights always go from Green to Yellow, Yellow to - Red, and Red to Green, we can just write a function to advance the color - of the light without taking an input color. - */ -let advanceColor = stoplight => failwith("For you to implement"); - -module ForTesting = { - let test_ex_red: stoplight = {location: "", color: Red}; - let test_ex_red': stoplight = {...test_ex_red, color: Green}; - let test_ex_yellow: stoplight = {location: "", color: Yellow}; - let test_ex_yellow': stoplight = {...test_ex_red, color: Red}; - let test_ex_green: stoplight = {location: "", color: Green}; - let test_ex_green': stoplight = {...test_ex_red, color: Yellow}; -}; - -advanceColor(ForTesting.test_ex_red); - -advanceColor(ForTesting.test_ex_yellow); - -advanceColor(ForTesting.test_ex_green); - -Test.runAll([ - (ForTesting.test_ex_red == ForTesting.test_ex_red', "advance color"), - (ForTesting.test_ex_yellow == ForTesting.test_ex_yellow', "advance color"), - (ForTesting.test_ex_green == ForTesting.test_ex_green', "advance color"), +/* + Sometimes rather than redefining the record you would like to have a field or + a set of fields that you can modify on the fly. + + In Reason if you want to have a field in a record that can be updated in place, + you must use some additional syntax. The mutable keyword makes the field + modifiable. + + Then you can update those fields in place with the = operator. + */ +type color = + | Red + | Yellow + | Green; + +type stoplight = { + location: string, /* stoplights don't usually move */ + mutable color /* but they often change color */ +}; + +/* On creation mutable fields are defined just like normal fields */ +let anExample: stoplight = { + location: "The corner of Vesey Street and the West Side highway", + color: Red, +}; + +/* + Now rather than using a functional update we can use a mutable update. + This doesn't return a new stoplight, it modifies the input stoplight. + */ +let setColor = (stoplight, color) => stoplight.color = color; + +/* + Since we know that stoplights always go from Green to Yellow, Yellow to + Red, and Red to Green, we can just write a function to advanbs.jsce the color + of the light without taking an input color. + */ +let advanceColor = stoplight => + switch (stoplight.color) { + | Green => stoplight.color = Yellow + | Yellow => stoplight.color = Red + | Red => stoplight.color = Green + }; + +module ForTesting = { + let test_ex_red: stoplight = {location: "", color: Red}; + let test_ex_red': stoplight = {...test_ex_red, color: Green}; + let test_ex_yellow: stoplight = {location: "", color: Yellow}; + let test_ex_yellow': stoplight = {...test_ex_red, color: Red}; + let test_ex_green: stoplight = {location: "", color: Green}; + let test_ex_green': stoplight = {...test_ex_red, color: Yellow}; +}; + +advanceColor(ForTesting.test_ex_red); + +advanceColor(ForTesting.test_ex_yellow); + +advanceColor(ForTesting.test_ex_green); + +Test.runAll([ + (ForTesting.test_ex_red == ForTesting.test_ex_red', "advance color"), + (ForTesting.test_ex_yellow == ForTesting.test_ex_yellow', "advance color"), + (ForTesting.test_ex_green == ForTesting.test_ex_green', "advance color"), ]); \ No newline at end of file diff --git a/src/exercises/24-refs/refs.re b/src/exercises/24-refs/refs.re index 4076979..030d145 100644 --- a/src/exercises/24-refs/refs.re +++ b/src/exercises/24-refs/refs.re @@ -1,28 +1,78 @@ -/* - It is sometimes useful to create a single mutable value. We can do this - using a ref. We can create an int ref containing 0 as follows: - */ -let x = ref(0); - -/* - Then we can access the value in the ref using the ^ operator, and - we can update it using the := operator. So, we could increment our - ref as follows: - */ -let () = x := x^ + 1; - -/* - Write a function minAndMax which returns a tuple containing the minimum - and maximum values in a non-empty list of positive integers. - - Your function should iterate over the list and maintain refs of the minimum - and maximum values seen so far. - - Hint: [max_int] or [min_int]. - */ -let minAndMax = lst => failwith("For you to implement"); - -Test.runAll([ - (minAndMax([5, 9, 2, 4, 3]) == (2, 9), "min and max"), - (minAndMax([11, 15, 7, 34]) == (7, 34), "min and max"), +/* + It is sometimes useful to create a single mutable value. We can do this + using a ref. We can create an int ref containing 0 as follows: + */ +let x = ref(0); + +/* + Then we can access the value in the ref using the ^ operator, and + we can update it using the := operator. So, we could increment our + ref as follows: + */ +let () = x := x^ + 1; + +/* + Write a function minAndMax which returns a tuple containing the minimum + and maximum values in a non-empty list of positive integers. + + Your function should iterate over the list and maintain refs of the minimum + and maximum values seen so far. + + Hint: [max_int] or [min_int]. + */ +/* let minAndMax = lst => { + let largest = ref(min_int); + let smallest = ref(max_int); + lst + |> List.iter(x => { + largest := + ( + if (x > largest^) { + x; + } else { + largest^; + } + ); + smallest := + ( + if (x < smallest^) { + x; + } else { + smallest^; + } + ); + }); + Js.log((smallest^, largest^)); + + (smallest^, largest^); + }; */ + +let minAndMax = lst => { + let greatest = ref(min_int); + let smallest = ref(max_int); + lst + |> List.iter(value => { + greatest := + ( + if (value > greatest^) { + value; + } else { + greatest^; + } + ); + smallest := + ( + if (value < smallest^) { + value; + } else { + smallest^; + } + ); + }); + (smallest^, greatest^); +}; + +Test.runAll([ + (minAndMax([5, 9, 2, 4, 3]) == (2, 9), "min and max"), + (minAndMax([11, 15, 7, 34]) == (7, 34), "min and max"), ]); \ No newline at end of file