diff --git a/concepts/numbers/.meta/config.json b/concepts/numbers/.meta/config.json index 8988f3def..1198ff6c3 100644 --- a/concepts/numbers/.meta/config.json +++ b/concepts/numbers/.meta/config.json @@ -1,6 +1,7 @@ { "authors": [ - "pwadsworth" + "pwadsworth", + "meatball133" ], "blurb": "Basics of Haskell numeric types" } diff --git a/concepts/numbers/about.md b/concepts/numbers/about.md index 61b97be47..46955b9ae 100644 --- a/concepts/numbers/about.md +++ b/concepts/numbers/about.md @@ -1,70 +1,184 @@ -# Haskell Built-in Numeric Types +# Numbers -## Numbers - -Haskell's standard library, known as ***Prelude***, defines most basic numeric types: fixed sized integers (`Int`), arbitrary precision integers (`Integer`), single precision floating (`Float`), and double precision floating (`Double`). -Other numeric types such as rationals and complex numbers are defined in additional libraries. +Haskell has a type, `Num`, which is the base type for all numeric types. +There are four main sub-types inside the Number type: [`Int`][int], [`Integer`][integer], [`Double`][double] and [`Float`][float]. +There are two types of integers types in Haskell, which represents whole numbers. +`Int` is a fixed-size integer, while `Integer` is an arbitrary precision integer. `Integer`s hold any number no matter how big, up to the limit of your machine's memory, while `Int` corresponds to the set of positive and negative integers that can be expressed in 32 or 64 bits (guaranteed at least -2^29 to 2^29). +Note that when writting negative numbers, you should use parentheses to avoid ambiguity. + +```haskell +1 + +(-1) +``` + `Float` corresponds to the set of real numbers, limited by the precision of the computer. Operations defined on numbers usually work on one type or the other, but not both. +`Double` is a floating-point number with double precision. + +```haskell +10.5 +``` + +## Converting between integers and floats + +Using the `toFloat` function, allows you to convert an `Int` or `Integer` to a `Float`. + +```haskell +toFloat 1 +-- -> 1.0 +``` + +There is also `fromIntegral` which converts any number types to the inferred type number type needed. + +```haskell +fromIntegral 1 +-- -> 1.0 +``` + +To convert a `Float` to an `Int`, you can use the [`round`][round], [`floor`][floor], or [`ceiling`][ceiling] functions. +The `round` function rounds to the nearest integer, `floor` rounds down, and `ceiling` rounds up. + +```haskell +round 1.5 +-- -> 2 + +floor 1.5 +-- -> 1 -Functions to convert between `Float` and `Integer` include, among others, `toFloat` which converts `Int`/`Integer` to `Float` and `ceiling` which converts a `Float` to an `Int`. +ceiling 1.5 +-- -> 2 +``` ## Arithmetic operators -Haskell has three "raise to the power" operators which work differently and take different argument types. +You can use the [basic arithmetic operators][math] on the numeric types. +The operators are `+`, `-`, `*`, `/`, and `%`. +When using these operators, the types of the numbers must match. +The `fromIntegral` function will convert so that the types match. -- `**` Takes two **floating point numbers** and uses logarithms to compute the power. -- `^^` Takes a **floating point** and raises it to a positive or negative **integer** power. -- `^` Takes **any numerical type** and raises it to a **positive integer** power. +### Addition & Subtraction & Multiplication + +The `+` operator is used for addition, the `-` operator is used for subtraction, and the `*` operator is used for multiplication. -Conversion from an integer type (`Int` or `Integer`) to any other numeric type is done using the function `fromIntegral`. -The target type is inferred automatically. -For example: +| Operator | Example | +| -------- | -------------- | +| `+` | `4 + 6 => 10` | +| `-` | `15 - 10 => 5` | +| `*` | `2 * 3 => 6` | + +### Division + +Division is used to divide numbers. +The `/` operator is used for division. +The result will always be a float. ```haskell -n :: Integer -n = 6 -x :: Float -x = fromIntegral n --> x = 6.0 - -m :: Int -m = 7 -y :: Double -y = fromIntegral m --> y = 7.0 +4.0 / 2.5 +-- -> 1.6 + +4 / 2 +-- -> 2.0 ``` -Division of integers is a little complicated. -If you use the ordinary `/` operator on integers, then you will get an error message (although the expression `4/3` does work because Haskell helpfully promotes literal integers to floats where necessary). -Instead, integer division is done using a collection of named operators. +~~~~exercism/caution +In some programming languages, when dividing by zero, the result will be an error. -Haskell has a neat trick with operators: you can take any function that takes two arguments and use it like an operator by enclosing the name in back-ticks. -So the following two lines mean exactly the same thing: +In Haskell, when dividing by zero, the result is `Infinity` or `-Infinity`. +The only exception is dividing zero by zero, resulting in `NaN` (Not a Number). ```haskell - d = 7 `div` 3 - d = div 7 3 +1 / 0 +-- -> Infinity + +(-1) / 0 +-- -> -Infinity + +0 / 0 +-- -> NaN +``` +~~~~ + +## Integer division + +Integer division is used to divide numbers and get the whole part of the result. +The result will always be rounded down to an Int. + +```haskell +2 `div` 2 +-- -> 2 + +5 `div` 2 +-- -> 2 ``` -With that in mind, here are the integer division operators: +~~~~exercism/caution +Dividing by zero when using integer division results in a Exception. +This is different from normal division. +~~~~ + +### Modulus -- `quot`: Returns the quotient of the two numbers. - This is the result of division which is then truncated towards zero. -- `rem`: Returns the remainder from the quotient. -- `div`: Similar to `quot`, but is rounded down towards minus infinity. -- `mod`: Returns the modulus of the two numbers. - This is similar to `rem`, but has different rules when `div` returns a negative number. +Modulus is used to get the remainder of a division. +The `mod` operator is used for modulus. -Just as you can convert a function with two arguments into an operator, you can convert an operator into a function with two arguments by putting it in parentheses. -So the following two lines mean the same thing: +```haskell +5 `mod` 2 +-- -> 1 + +5 `mod` 3 +-- -> 2 +``` + +~~~~exercism/caution +Dividing by zero when using modulo results in an Exception. +This is different from normal division. ```haskell -(+) 3 4 -3 + 4 +1 `mod` 0 +-- Exception: divide by zero +``` +~~~~ + +## Exponentiation + +Haskell has three "raise to the power" operators which work differently and take different argument types. + +- `**` Takes two **floating point numbers** and uses logarithms to compute the power. +- `^^` Takes a **floating point** and raises it to a positive or negative **integer** power. +- `^` Takes **any numerical type** and raises it to a **positive integer** power. + +```haskell +3.0 ** 2.1 +-- -> 10.04510856630514 + +2.5 ^^ 2 +-- -> 6.25 + +2 ^ 3 +-- -> 8 +``` + +## Priority and parentheses + +Haskell allows parentheses(`()`), which can be used to group expressions. +This is useful when you want to change the order of operations. + +When using multiple arithmetic operators, the order of operations is the same as in mathematics, also known as [PEMDAS][pemdas]. +It follows the order of parentheses(`()`), exponents(`**`), multiplication(`*`) and division(`/`), and addition(`+`) and subtraction(`-`). + +```haskell +2 + 3 - 4 * 4 +-- -> -11 + +(2 + 3 - 4) * 4 +-- -> 4 ``` ---- -### Credits: -The above text is modified from __The Haskell 98 Report__ by Simon Peyton Jones, used with permission to copy, distribute and modify for any purpose with this note included. +[pemdas]: https://en.wikipedia.org/wiki/Order_of_operations +[floor]: https://hackage.haskell.org/package/base/docs/Prelude.html#v:floor +[ceiling]: https://hackage.haskell.org/package/base/docs/Prelude.html#v:ceiling +[round]: https://hackage.haskell.org/package/base/docs/Prelude.html#v:round diff --git a/concepts/numbers/introduction.md b/concepts/numbers/introduction.md index 87f92aff4..46955b9ae 100644 --- a/concepts/numbers/introduction.md +++ b/concepts/numbers/introduction.md @@ -1,24 +1,184 @@ -# Introduction +# Numbers +Haskell has a type, `Num`, which is the base type for all numeric types. +There are four main sub-types inside the Number type: [`Int`][int], [`Integer`][integer], [`Double`][double] and [`Float`][float]. + +There are two types of integers types in Haskell, which represents whole numbers. +`Int` is a fixed-size integer, while `Integer` is an arbitrary precision integer. `Integer`s hold any number no matter how big, up to the limit of your machine's memory, while `Int` corresponds to the set of positive and negative integers that can be expressed in 32 or 64 bits (guaranteed at least -2^29 to 2^29). +Note that when writting negative numbers, you should use parentheses to avoid ambiguity. + +```haskell +1 + +(-1) +``` + `Float` corresponds to the set of real numbers, limited by the precision of the computer. Operations defined on numbers usually work on one type or the other, but not both. +`Double` is a floating-point number with double precision. + +```haskell +10.5 +``` -Functions to convert between `Float` and `Integer` include, among others, `toFloat` which converts `Int`/`Integer` to `Float` and `ceiling` which converts a `Float` to an `Int`. +## Converting between integers and floats -Conversion from an integer type (`Int` or `Integer`) to any other numeric type is done using the function `fromIntegral`. -The target type is inferred automatically. -For example: +Using the `toFloat` function, allows you to convert an `Int` or `Integer` to a `Float`. ```haskell -n :: Integer -n = 6 -x :: Float -x = fromIntegral n --> x = 6.0 +toFloat 1 +-- -> 1.0 +``` + +There is also `fromIntegral` which converts any number types to the inferred type number type needed. + +```haskell +fromIntegral 1 +-- -> 1.0 +``` + +To convert a `Float` to an `Int`, you can use the [`round`][round], [`floor`][floor], or [`ceiling`][ceiling] functions. +The `round` function rounds to the nearest integer, `floor` rounds down, and `ceiling` rounds up. + +```haskell +round 1.5 +-- -> 2 + +floor 1.5 +-- -> 1 -m :: Int -m = 7 -y :: Double -y = fromIntegral m --> y = 7.0 +ceiling 1.5 +-- -> 2 ``` + +## Arithmetic operators + +You can use the [basic arithmetic operators][math] on the numeric types. +The operators are `+`, `-`, `*`, `/`, and `%`. +When using these operators, the types of the numbers must match. +The `fromIntegral` function will convert so that the types match. + +### Addition & Subtraction & Multiplication + +The `+` operator is used for addition, the `-` operator is used for subtraction, and the `*` operator is used for multiplication. + +| Operator | Example | +| -------- | -------------- | +| `+` | `4 + 6 => 10` | +| `-` | `15 - 10 => 5` | +| `*` | `2 * 3 => 6` | + +### Division + +Division is used to divide numbers. +The `/` operator is used for division. +The result will always be a float. + +```haskell +4.0 / 2.5 +-- -> 1.6 + +4 / 2 +-- -> 2.0 +``` + +~~~~exercism/caution +In some programming languages, when dividing by zero, the result will be an error. + +In Haskell, when dividing by zero, the result is `Infinity` or `-Infinity`. +The only exception is dividing zero by zero, resulting in `NaN` (Not a Number). + +```haskell +1 / 0 +-- -> Infinity + +(-1) / 0 +-- -> -Infinity + +0 / 0 +-- -> NaN +``` +~~~~ + +## Integer division + +Integer division is used to divide numbers and get the whole part of the result. +The result will always be rounded down to an Int. + +```haskell +2 `div` 2 +-- -> 2 + +5 `div` 2 +-- -> 2 +``` + +~~~~exercism/caution +Dividing by zero when using integer division results in a Exception. +This is different from normal division. +~~~~ + +### Modulus + +Modulus is used to get the remainder of a division. +The `mod` operator is used for modulus. + +```haskell +5 `mod` 2 +-- -> 1 + +5 `mod` 3 +-- -> 2 +``` + +~~~~exercism/caution +Dividing by zero when using modulo results in an Exception. +This is different from normal division. + +```haskell +1 `mod` 0 +-- Exception: divide by zero +``` +~~~~ + +## Exponentiation + +Haskell has three "raise to the power" operators which work differently and take different argument types. + +- `**` Takes two **floating point numbers** and uses logarithms to compute the power. +- `^^` Takes a **floating point** and raises it to a positive or negative **integer** power. +- `^` Takes **any numerical type** and raises it to a **positive integer** power. + +```haskell +3.0 ** 2.1 +-- -> 10.04510856630514 + +2.5 ^^ 2 +-- -> 6.25 + +2 ^ 3 +-- -> 8 +``` + +## Priority and parentheses + +Haskell allows parentheses(`()`), which can be used to group expressions. +This is useful when you want to change the order of operations. + +When using multiple arithmetic operators, the order of operations is the same as in mathematics, also known as [PEMDAS][pemdas]. +It follows the order of parentheses(`()`), exponents(`**`), multiplication(`*`) and division(`/`), and addition(`+`) and subtraction(`-`). + +```haskell +2 + 3 - 4 * 4 +-- -> -11 + +(2 + 3 - 4) * 4 +-- -> 4 +``` + +[pemdas]: https://en.wikipedia.org/wiki/Order_of_operations +[floor]: https://hackage.haskell.org/package/base/docs/Prelude.html#v:floor +[ceiling]: https://hackage.haskell.org/package/base/docs/Prelude.html#v:ceiling +[round]: https://hackage.haskell.org/package/base/docs/Prelude.html#v:round diff --git a/config.json b/config.json index 145ade90f..9f73dec58 100644 --- a/config.json +++ b/config.json @@ -49,9 +49,9 @@ ] }, { - "slug": "temperature", - "name": "Temperature", - "uuid": "6ed15adb-f022-4bbd-bbb3-22845deabc91", + "slug": "freelancer-rates", + "name": "Freelancer Rates", + "uuid": "85e17e39-9506-4dc8-bed6-ecc44c840cc1", "prerequisites": [ "basics" ], @@ -96,6 +96,16 @@ "concepts": [ "algebraic-data-types" ] + }, + { + "slug": "temperature", + "name": "Temperature", + "uuid": "6ed15adb-f022-4bbd-bbb3-22845deabc91", + "prerequisites": [ + ], + "status": "deprecated", + "concepts": [ + ] } ], "practice": [ diff --git a/exercises/concept/freelancer-rates/.docs/hints.md b/exercises/concept/freelancer-rates/.docs/hints.md new file mode 100644 index 000000000..f86d94855 --- /dev/null +++ b/exercises/concept/freelancer-rates/.docs/hints.md @@ -0,0 +1,30 @@ +# Hints + +## General + +- Read about basic arithmetic in the official [Getting Started guide][getting-started-basic-arithmetic]. +- Browse the [`Float`][float-functions] and [`Kernel`][kernel-arithmetic-operators] modules to learn about functions and operators that work with floats. + +## 1. Calculate the daily rate given an hourly rate + +- [Basic arithmetic operations][kernel-arithmetic-operators] where one argument is an integer, and the other is a float, will return a float. + +## 2. Calculate a discounted price + +- [Basic arithmetic operations][kernel-arithmetic-operators] where one argument is an integer, and the other is a float, will return a float. + +## 3. Calculate the monthly rate, given an hourly rate and a discount + +- There is a [built-in function][kernel-trunc] for changing floats to integers. +- There is a [built-in function][float-ceil] for rounding floats up. + +## 4. Calculate the number of workdays given a budget, hourly rate and discount + +- There is a [built-in function][float-floor] for rounding floats down with desired precision. + +[getting-started-basic-arithmetic]: https://hexdocs.pm/elixir/basic-types.html#basic-arithmetic +[kernel-arithmetic-operators]: https://hexdocs.pm/elixir/Kernel.html#*/2 +[kernel-trunc]: https://hexdocs.pm/elixir/Kernel.html#trunc/1 +[float-functions]: https://hexdocs.pm/elixir/Float.html#functions +[float-ceil]: https://hexdocs.pm/elixir/Float.html#ceil/2 +[float-floor]: https://hexdocs.pm/elixir/Float.html#floor/2 diff --git a/exercises/concept/freelancer-rates/.docs/instructions.md b/exercises/concept/freelancer-rates/.docs/instructions.md new file mode 100644 index 000000000..6bd3ed73d --- /dev/null +++ b/exercises/concept/freelancer-rates/.docs/instructions.md @@ -0,0 +1,57 @@ +# Instructions + +In this exercise you'll be writing code to help a freelancer communicate with a project manager by providing a few utilities to quickly calculate daily and +monthly rates, optionally with a given discount. + +We first establish a few rules between the freelancer and the project manager: + +- The daily rate is 8 times the hourly rate. +- A month has 22 billable days. + +Sometimes, the freelancer is offering to apply a discount on their daily rate (for example for their most loyal customers or for non-for-profit customers). + +Discounts are modeled as fractional numbers representing percentage, for example `25.0` (25%). + +## 1. Calculate the daily rate given an hourly rate + +Implement a function to calculate the daily rate given an hourly rate: + +```haskell +dailyRate 60 +-- -> 480.0 +``` + +The returned daily rate should be a double. + +## 2. Calculate a discounted price + +Implement a function to calculate the price after a discount. + +```haskell +applyDiscount 150 10 +-- -> 135.0 +``` + +The returned value should always be a double, not rounded in any way. + +## 3. Calculate the monthly rate, given an hourly rate and a discount + +Implement a function to calculate the monthly rate, and apply a discount: + +```haskell +monthlyRate 77 10.5 +-- -> 12130 +``` + +The returned monthly rate should be rounded up (take the ceiling) to the nearest integer. + +## 4. Calculate the number of workdays given a budget, hourly rate and discount + +Implement a function that takes a budget, an hourly rate, and a discount, and calculates how many days of work that covers. + +```haskell +daysInBudget 20000 80 11.0 +-- -> 35.1 +``` + +The returned number of days should be rounded down (take the floor) to one decimal place. diff --git a/exercises/concept/freelancer-rates/.docs/introduction.md b/exercises/concept/freelancer-rates/.docs/introduction.md new file mode 100644 index 000000000..46955b9ae --- /dev/null +++ b/exercises/concept/freelancer-rates/.docs/introduction.md @@ -0,0 +1,184 @@ +# Numbers + +Haskell has a type, `Num`, which is the base type for all numeric types. +There are four main sub-types inside the Number type: [`Int`][int], [`Integer`][integer], [`Double`][double] and [`Float`][float]. + +There are two types of integers types in Haskell, which represents whole numbers. +`Int` is a fixed-size integer, while `Integer` is an arbitrary precision integer. +`Integer`s hold any number no matter how big, up to the limit of your machine's memory, while `Int` corresponds to the set of positive and negative integers that can be expressed in 32 or 64 bits (guaranteed at least -2^29 to 2^29). + +Note that when writting negative numbers, you should use parentheses to avoid ambiguity. + +```haskell +1 + +(-1) +``` + +`Float` corresponds to the set of real numbers, limited by the precision of the computer. +Operations defined on numbers usually work on one type or the other, but not both. +`Double` is a floating-point number with double precision. + +```haskell +10.5 +``` + +## Converting between integers and floats + +Using the `toFloat` function, allows you to convert an `Int` or `Integer` to a `Float`. + +```haskell +toFloat 1 +-- -> 1.0 +``` + +There is also `fromIntegral` which converts any number types to the inferred type number type needed. + +```haskell +fromIntegral 1 +-- -> 1.0 +``` + +To convert a `Float` to an `Int`, you can use the [`round`][round], [`floor`][floor], or [`ceiling`][ceiling] functions. +The `round` function rounds to the nearest integer, `floor` rounds down, and `ceiling` rounds up. + +```haskell +round 1.5 +-- -> 2 + +floor 1.5 +-- -> 1 + +ceiling 1.5 +-- -> 2 +``` + +## Arithmetic operators + +You can use the [basic arithmetic operators][math] on the numeric types. +The operators are `+`, `-`, `*`, `/`, and `%`. +When using these operators, the types of the numbers must match. +The `fromIntegral` function will convert so that the types match. + +### Addition & Subtraction & Multiplication + +The `+` operator is used for addition, the `-` operator is used for subtraction, and the `*` operator is used for multiplication. + +| Operator | Example | +| -------- | -------------- | +| `+` | `4 + 6 => 10` | +| `-` | `15 - 10 => 5` | +| `*` | `2 * 3 => 6` | + +### Division + +Division is used to divide numbers. +The `/` operator is used for division. +The result will always be a float. + +```haskell +4.0 / 2.5 +-- -> 1.6 + +4 / 2 +-- -> 2.0 +``` + +~~~~exercism/caution +In some programming languages, when dividing by zero, the result will be an error. + +In Haskell, when dividing by zero, the result is `Infinity` or `-Infinity`. +The only exception is dividing zero by zero, resulting in `NaN` (Not a Number). + +```haskell +1 / 0 +-- -> Infinity + +(-1) / 0 +-- -> -Infinity + +0 / 0 +-- -> NaN +``` +~~~~ + +## Integer division + +Integer division is used to divide numbers and get the whole part of the result. +The result will always be rounded down to an Int. + +```haskell +2 `div` 2 +-- -> 2 + +5 `div` 2 +-- -> 2 +``` + +~~~~exercism/caution +Dividing by zero when using integer division results in a Exception. +This is different from normal division. +~~~~ + +### Modulus + +Modulus is used to get the remainder of a division. +The `mod` operator is used for modulus. + +```haskell +5 `mod` 2 +-- -> 1 + +5 `mod` 3 +-- -> 2 +``` + +~~~~exercism/caution +Dividing by zero when using modulo results in an Exception. +This is different from normal division. + +```haskell +1 `mod` 0 +-- Exception: divide by zero +``` +~~~~ + +## Exponentiation + +Haskell has three "raise to the power" operators which work differently and take different argument types. + +- `**` Takes two **floating point numbers** and uses logarithms to compute the power. +- `^^` Takes a **floating point** and raises it to a positive or negative **integer** power. +- `^` Takes **any numerical type** and raises it to a **positive integer** power. + +```haskell +3.0 ** 2.1 +-- -> 10.04510856630514 + +2.5 ^^ 2 +-- -> 6.25 + +2 ^ 3 +-- -> 8 +``` + +## Priority and parentheses + +Haskell allows parentheses(`()`), which can be used to group expressions. +This is useful when you want to change the order of operations. + +When using multiple arithmetic operators, the order of operations is the same as in mathematics, also known as [PEMDAS][pemdas]. +It follows the order of parentheses(`()`), exponents(`**`), multiplication(`*`) and division(`/`), and addition(`+`) and subtraction(`-`). + +```haskell +2 + 3 - 4 * 4 +-- -> -11 + +(2 + 3 - 4) * 4 +-- -> 4 +``` + +[pemdas]: https://en.wikipedia.org/wiki/Order_of_operations +[floor]: https://hackage.haskell.org/package/base/docs/Prelude.html#v:floor +[ceiling]: https://hackage.haskell.org/package/base/docs/Prelude.html#v:ceiling +[round]: https://hackage.haskell.org/package/base/docs/Prelude.html#v:round diff --git a/exercises/concept/freelancer-rates/.docs/introduction.md.tpl b/exercises/concept/freelancer-rates/.docs/introduction.md.tpl new file mode 100644 index 000000000..e1f4af8fb --- /dev/null +++ b/exercises/concept/freelancer-rates/.docs/introduction.md.tpl @@ -0,0 +1,3 @@ +# Introduction + +%{concept:pattern-matching-literals} diff --git a/exercises/concept/freelancer-rates/.meta/config.json b/exercises/concept/freelancer-rates/.meta/config.json new file mode 100644 index 000000000..604f07a2e --- /dev/null +++ b/exercises/concept/freelancer-rates/.meta/config.json @@ -0,0 +1,23 @@ +{ + "authors": [ + "pwadsworth", + "meatball133" + ], + "files": { + "solution": [ + "src/FreelancerRates.hs", + "package.yaml" + ], + "test": [ + "test/Tests.hs" + ], + "exemplar": [ + ".meta/exemplar/src/FreelancerRates.hs" + ], + "invalidator": [ + "stack.yaml" + ] + }, + "icon": "freelancer-rates", + "blurb": "Learn about integers and floating point numbers by helping a freelancer communicate with a project manager about billing." +} diff --git a/exercises/concept/freelancer-rates/.meta/design.md b/exercises/concept/freelancer-rates/.meta/design.md new file mode 100644 index 000000000..c8bbc964a --- /dev/null +++ b/exercises/concept/freelancer-rates/.meta/design.md @@ -0,0 +1,25 @@ +# Design + +## Learning objectives + +- Know what [pattern matching][pattern-matching] is. +- Know about constant, variable and wildcard patterns. +- Know about guards. +- Know about exhaustiveness checking in pattern matching. + +## Out of scope + +- Other patterns besides the constant and wildcard patterns. +- Lazy pattern matching. +- `case` statements. + +## Concepts + +- `pattern-matching`: know what [pattern matching][pattern-matching] is; know about constant, variable and wildcard patterns; know how to use guards; know about exhaustiveness checking in pattern matching. + +## Prerequisites + +- `basics`: function definitions +- `numbers`: numeric types and operators + +[pattern-matching]: https://learnyouahaskell.github.io/syntax-in-functions#pattern-matching diff --git a/exercises/concept/freelancer-rates/.meta/exemplar/package.yaml b/exercises/concept/freelancer-rates/.meta/exemplar/package.yaml new file mode 100644 index 000000000..67e7991cf --- /dev/null +++ b/exercises/concept/freelancer-rates/.meta/exemplar/package.yaml @@ -0,0 +1,18 @@ +name: freelancer-rates +version: 1.0.0.0 + +dependencies: + - base + +library: + exposed-modules: FreelancerRates + source-dirs: src + ghc-options: -Wall + +tests: + test: + main: Tests.hs + source-dirs: test + dependencies: + - freelancer-rates + - hspec diff --git a/exercises/concept/freelancer-rates/.meta/exemplar/src/FreelancerRates.hs b/exercises/concept/freelancer-rates/.meta/exemplar/src/FreelancerRates.hs new file mode 100644 index 000000000..0a251adde --- /dev/null +++ b/exercises/concept/freelancer-rates/.meta/exemplar/src/FreelancerRates.hs @@ -0,0 +1,14 @@ +module FreelancerRates (dailyRate, applyDiscount, monthlyRate, daysInBudget) where + +dailyRate :: Double -> Double +dailyRate hourlyRate = hourlyRate * 8 + +applyDiscount :: Double -> Double -> Double +applyDiscount amount disscount = amount * (1 - disscount / 100) + +monthlyRate :: Double -> Double -> Int +monthlyRate hourlyRate discount = ceiling (applyDiscount (dailyRate hourlyRate * 22) discount) + +daysInBudget :: Int -> Double -> Double -> Double +daysInBudget budget hourlyRate discount = + fromIntegral (floor (fromIntegral budget / applyDiscount (dailyRate hourlyRate) discount * 10) :: Int) / 10 diff --git a/exercises/concept/freelancer-rates/package.yaml b/exercises/concept/freelancer-rates/package.yaml new file mode 100644 index 000000000..67e7991cf --- /dev/null +++ b/exercises/concept/freelancer-rates/package.yaml @@ -0,0 +1,18 @@ +name: freelancer-rates +version: 1.0.0.0 + +dependencies: + - base + +library: + exposed-modules: FreelancerRates + source-dirs: src + ghc-options: -Wall + +tests: + test: + main: Tests.hs + source-dirs: test + dependencies: + - freelancer-rates + - hspec diff --git a/exercises/concept/freelancer-rates/src/FreelancerRates.hs b/exercises/concept/freelancer-rates/src/FreelancerRates.hs new file mode 100644 index 000000000..b2fe21012 --- /dev/null +++ b/exercises/concept/freelancer-rates/src/FreelancerRates.hs @@ -0,0 +1,13 @@ +module FreelancerRates (dailyRate, applyDiscount, monthlyRate, daysInBudget) where + +dailyRate :: Double -> Double +dailyRate = error "Implement this function." + +applyDiscount :: Double -> Double -> Double +applyDiscount = error "Implement this function." + +monthlyRate :: Double -> Double -> Int +monthlyRate = error "Implement this function." + +daysInBudget :: Int -> Double -> Double -> Double +daysInBudget = error "Implement this function." diff --git a/exercises/concept/freelancer-rates/stack.yaml b/exercises/concept/freelancer-rates/stack.yaml new file mode 100644 index 000000000..115878212 --- /dev/null +++ b/exercises/concept/freelancer-rates/stack.yaml @@ -0,0 +1 @@ +resolver: lts-20.18 diff --git a/exercises/concept/freelancer-rates/test/Tests.hs b/exercises/concept/freelancer-rates/test/Tests.hs new file mode 100644 index 000000000..1a8735a46 --- /dev/null +++ b/exercises/concept/freelancer-rates/test/Tests.hs @@ -0,0 +1,54 @@ +import FreelancerRates (dailyRate, applyDiscount, monthlyRate, daysInBudget) +import Test.Hspec (describe, hspec, it, shouldBe) + +main :: IO () +main = hspec $ + describe "reply" $ do + it "it's the hourly_rate times 8" $ + dailyRate 50 `shouldBe` 400.0 + + it "it always returns a float" $ + dailyRate 60 `shouldBe` 480 + + it "it does not round" $ + dailyRate 55.1 `shouldBe` 440.8 + + it "a discount of 10% leaves 90% of the original price" $ + applyDiscount 140.0 10 `shouldBe` 126.0 + + it "it always returns a double" $ + applyDiscount 100 10 `shouldBe` 90.0 + + it "it doesn't round" $ + applyDiscount 111.11 13.5 `shouldBe` 96.11015 + + it "it's the dailyRate times 22" $ + monthlyRate 62 0.0 `shouldBe` 10912 + + it "it always returns an integer" $ + monthlyRate 70 0.0 `shouldBe` 12320 + + it "the result is rounded up" $ + monthlyRate 62.8 0.0 `shouldBe` 11053 + + it "the result should be rouneded up" $ + monthlyRate 65.2 0.0 `shouldBe` 11476 + + it "gives a discount" $ + monthlyRate 67 12.0 `shouldBe` 10377 + + it "the budget divided by the daily rate" $ + daysInBudget 1600 50 0.0 `shouldBe` 4 + + it "always returns a float" $ + daysInBudget 520 65 0.0 `shouldBe` 1.0 + + it "rounds down to one decimal place" $ + daysInBudget 4410 55 0.0 `shouldBe` 10.0 + + it "rounds down down to one decimal" $ + daysInBudget 4480 55 0.0 `shouldBe` 10.1 + + it "it applies the discount" $ + daysInBudget 480 60 20 `shouldBe` 1.2 + \ No newline at end of file