diff --git a/concepts/symbol/.meta/config.json b/concepts/symbol/.meta/config.json new file mode 100644 index 00000000..8adc4faa --- /dev/null +++ b/concepts/symbol/.meta/config.json @@ -0,0 +1,5 @@ +{ + "blurb": "Symbols represent unique identifiers throughout the program, which is created at compile time", + "authors": ["meatball133"], + "contributors": ["ryanplusplus"] +} diff --git a/concepts/symbol/.meta/template.md b/concepts/symbol/.meta/template.md new file mode 100644 index 00000000..cda2d9f1 --- /dev/null +++ b/concepts/symbol/.meta/template.md @@ -0,0 +1 @@ +{% $symbol %} \ No newline at end of file diff --git a/concepts/symbol/about.md b/concepts/symbol/about.md new file mode 100644 index 00000000..5224074f --- /dev/null +++ b/concepts/symbol/about.md @@ -0,0 +1,43 @@ +# About + +[Symbols][symbols] are named identifiers that can be used to refer to a value. +Symbols are created through a symbol literal, which is by prefixing a name with a `:` character, e.g. `:foo`. +They also allow for being written with quotes, e.g. `:"foo"`, which allows, for example, spaces in the name. + +```crystal +:foo # => :foo +:"foo boo" # => :"foo boo" +``` + +Symbols are used in many places in the language, including as keys in namedtuples and to represent method and variable names. + +## Symbols in Crystal + +Symbols in Crystal are quite different from Ruby. +In Crystal a symbol is a type of constant and is thereby is assigned at compile time. +This means that symbols can't be created dynamically, which is possible in Ruby. + +Symbols in Crystal are represented as `Int32`s which makes them very efficient. + +## Identifier + +What makes symbols different from strings is that they are identifiers and do not represent data or text. +This means that two symbols with the same name are always the same object. + +```ruby +"foo".object_id # => 60 +"foo".object_id # => 80 +:foo.object_id # => 1086748 +:foo.object_id # => 1086748 +``` + +## Conversion + +Symbols can be converted to strings but not vice versa. +This is because symbols are created at compile time and strings are created at runtime. + +```crystal +:foo.to_s # => "foo" +``` + +[symbols]: https://crystal-lang.org/reference/syntax_and_semantics/literals/symbol.html diff --git a/concepts/symbol/introduction.md b/concepts/symbol/introduction.md new file mode 100644 index 00000000..5224074f --- /dev/null +++ b/concepts/symbol/introduction.md @@ -0,0 +1,43 @@ +# About + +[Symbols][symbols] are named identifiers that can be used to refer to a value. +Symbols are created through a symbol literal, which is by prefixing a name with a `:` character, e.g. `:foo`. +They also allow for being written with quotes, e.g. `:"foo"`, which allows, for example, spaces in the name. + +```crystal +:foo # => :foo +:"foo boo" # => :"foo boo" +``` + +Symbols are used in many places in the language, including as keys in namedtuples and to represent method and variable names. + +## Symbols in Crystal + +Symbols in Crystal are quite different from Ruby. +In Crystal a symbol is a type of constant and is thereby is assigned at compile time. +This means that symbols can't be created dynamically, which is possible in Ruby. + +Symbols in Crystal are represented as `Int32`s which makes them very efficient. + +## Identifier + +What makes symbols different from strings is that they are identifiers and do not represent data or text. +This means that two symbols with the same name are always the same object. + +```ruby +"foo".object_id # => 60 +"foo".object_id # => 80 +:foo.object_id # => 1086748 +:foo.object_id # => 1086748 +``` + +## Conversion + +Symbols can be converted to strings but not vice versa. +This is because symbols are created at compile time and strings are created at runtime. + +```crystal +:foo.to_s # => "foo" +``` + +[symbols]: https://crystal-lang.org/reference/syntax_and_semantics/literals/symbol.html diff --git a/concepts/symbol/links.json b/concepts/symbol/links.json new file mode 100644 index 00000000..a6d207ca --- /dev/null +++ b/concepts/symbol/links.json @@ -0,0 +1,10 @@ +[ + { + "url": "https://crystal-lang.org/reference/syntax_and_semantics/literals/symbol.html", + "description": "Crystal docs: Symbol" + }, + { + "url": "https://crystal-lang.org/api/Symbol.html", + "description": "Crystal api: Symbol" + } +] diff --git a/concepts/tuple/.meta/config.json b/concepts/tuple/.meta/config.json new file mode 100644 index 00000000..29cb12ae --- /dev/null +++ b/concepts/tuple/.meta/config.json @@ -0,0 +1,5 @@ +{ + "blurb": "Tuples are data structures which store a fixed number of elements. Tuples are indexable and immutable", + "authors": ["meatball133"], + "contributors": ["ryanplusplus"] +} diff --git a/concepts/tuple/.meta/template.md b/concepts/tuple/.meta/template.md new file mode 100644 index 00000000..1427769f --- /dev/null +++ b/concepts/tuple/.meta/template.md @@ -0,0 +1 @@ +{% $tuple \ $"tuple"."Tuples definition shorthand" %} \ No newline at end of file diff --git a/concepts/tuple/about.md b/concepts/tuple/about.md new file mode 100644 index 00000000..7370396f --- /dev/null +++ b/concepts/tuple/about.md @@ -0,0 +1,122 @@ +# Tuples + +A [tuple][tuple] is a finite ordered list of elements which is immutable. +Tuples requires all positions to have a fixed type. +This in turns means that the compiler knows what type is at each position. +The types used in a tuple can be different at each position, but the types must be known at compile time. + +## Creating a Tuple + +Depending on if the tuples values types can be interpreted under compilation, the tuple can be created in different ways. +If the values are known at compile time, the tuple can be created using the tuple literal syntax, otherwise they need to be explicitly declared. +It is also important that the types of the values match the types specified in the tuple and that the number of values matches the number of types specified. +Here is an example of defning through tuple literal syntax: + +```crystal +tuple = {1, "foo", 'c'} # Tuple(Int32, String, Char) +``` + +There is also the possibility to create a tuple using the `Tuple` class. + +```crystal +tuple = Tuple(Int32, String, Char).new(1, "foo", 'c') +``` + +Alternatively, you can explicitly specify the type of the variable assigned to the tuple. + +```crystal +tuple : Tuple(Int32, String, Char) = {1, "foo", 'c'} +``` + +Explicitly specifying the type of the tuple can be useful since that allows for defining that a position should hold a union type. +This means that a position can hold multiple types. + +```crystal +tuple : Tuple(Int32 | String, String, Char) = {1, "foo", 'c'} +``` + +## Conversion + +### Creating a tuple from an array + +You can create a tuple from an array using the `Tuple` class's `from` method. +This requires that the type of the tuple is specified. + +```crystal +array = [1, "foo", 'c'] +tuple = Tuple(Int32, String, Char).from(array) +``` + +### Conversion to Array + +You can convert a tuple to an array using the `to_a` method. +The resulting array's element type is the union of the type of each field in the tuple. + +```crystal +tuple = {1, "foo", 'c'} +array = tuple.to_a +array # => [1, "foo", 'c'] +``` + +### Tuples definition shorthand + +There is a shorthand for defining explicit tuple types. +That is by writting the types inside of the tuple literal. + +```crystal +tuple : {Int32, String, Char} = {1, "foo", 'c'} +``` + +## Accessing Elements + +Like arrays, tuples are zero-indexed, meaning that the first element is at index 0. +However, unlike arrays, the type of each element is fixed and known at compile time, therefore when indexing a tuple, the type of the element is specific to the position. +To access an element in a tuple, you can use the `[]` operator. + +```crystal +array = [1, "foo", 'c'] +array[0] # => 1 +typeof(array[0]) # => Int32 | String | Char + +tuple = {1, "foo", 'c'} +tuple[0] # => 1 +typeof(tuple[0]) # => Int32 +``` + +Another difference when it comes to accessing elements from arrays is that if the index is specified, then the compiler will check that the index is within the bounds of the tuple. +This means you will get a compile time error instead of a runtime error. + +```crystal +tuple = {1, "foo", 'c'} +tuple[3] +# => Error: index out of bounds for Tuple(Int32, String, Char) (3 not in -3..2) +``` + +However, if the index is stored in a variable, then the compiler will not be able to check if the index is within the bounds of the tuple at compile time and will instead give a runtime error. + +## Subtuple + +You can get a subtuple of a tuple by using the `[]` operator with a range. +What is returned is a new tuple with the elements from the range specified. +The range has to be given at compile time, otherwise the compiler will not be able to know the types of the elements in the subtuple. +This means that the range has to be a range literal and not assigned to a variable. + +```crystal +tuple = {1, "foo", 'c'} +subtuple = tuple[0..1] # Tuple(Int32, String) + +i = 0..1 +tuple[i] +# Error: Tuple#[](Range) can only be called with range literals known at compile-time +``` + +## When to use a Tuple + +Tuples are useful when you want to group a fixed number of values together where the types of the values are known at compile time. +This is because tuples require less memory and are faster than arrays due to the immutability of tuples. +Another use case is returning multiple values from a method. +This is particularly helpful if the values have different types since each position in the tuple can have a different type. + +Tuples should not be used when a data structure is needed that can grow or shrink in size or often needs to be modified. + +[tuple]: https://crystal-lang.org/reference/syntax_and_semantics/literals/tuple.html diff --git a/concepts/tuple/introduction.md b/concepts/tuple/introduction.md new file mode 100644 index 00000000..dacb9d5f --- /dev/null +++ b/concepts/tuple/introduction.md @@ -0,0 +1,113 @@ +# Tuples + +A [tuple][tuple] is a finite ordered list of elements which is immutable. +Tuples requires all positions to have a fixed type. +This in turns means that the compiler knows what type is at each position. +The types used in a tuple can be different at each position, but the types must be known at compile time. + +## Creating a Tuple + +Depending on if the tuples values types can be interpreted under compilation, the tuple can be created in different ways. +If the values are known at compile time, the tuple can be created using the tuple literal syntax, otherwise they need to be explicitly declared. +It is also important that the types of the values match the types specified in the tuple and that the number of values matches the number of types specified. +Here is an example of defning through tuple literal syntax: + +```crystal +tuple = {1, "foo", 'c'} # Tuple(Int32, String, Char) +``` + +There is also the possibility to create a tuple using the `Tuple` class. + +```crystal +tuple = Tuple(Int32, String, Char).new(1, "foo", 'c') +``` + +Alternatively, you can explicitly specify the type of the variable assigned to the tuple. + +```crystal +tuple : Tuple(Int32, String, Char) = {1, "foo", 'c'} +``` + +Explicitly specifying the type of the tuple can be useful since that allows for defining that a position should hold a union type. +This means that a position can hold multiple types. + +```crystal +tuple : Tuple(Int32 | String, String, Char) = {1, "foo", 'c'} +``` + +## Conversion + +### Creating a tuple from an array + +You can create a tuple from an array using the `Tuple` class's `from` method. +This requires that the type of the tuple is specified. + +```crystal +array = [1, "foo", 'c'] +tuple = Tuple(Int32, String, Char).from(array) +``` + +### Conversion to Array + +You can convert a tuple to an array using the `to_a` method. +The resulting array's element type is the union of the type of each field in the tuple. + +```crystal +tuple = {1, "foo", 'c'} +array = tuple.to_a +array # => [1, "foo", 'c'] +``` + +## Accessing Elements + +Like arrays, tuples are zero-indexed, meaning that the first element is at index 0. +However, unlike arrays, the type of each element is fixed and known at compile time, therefore when indexing a tuple, the type of the element is specific to the position. +To access an element in a tuple, you can use the `[]` operator. + +```crystal +array = [1, "foo", 'c'] +array[0] # => 1 +typeof(array[0]) # => Int32 | String | Char + +tuple = {1, "foo", 'c'} +tuple[0] # => 1 +typeof(tuple[0]) # => Int32 +``` + +Another difference when it comes to accessing elements from arrays is that if the index is specified, then the compiler will check that the index is within the bounds of the tuple. +This means you will get a compile time error instead of a runtime error. + +```crystal +tuple = {1, "foo", 'c'} +tuple[3] +# => Error: index out of bounds for Tuple(Int32, String, Char) (3 not in -3..2) +``` + +However, if the index is stored in a variable, then the compiler will not be able to check if the index is within the bounds of the tuple at compile time and will instead give a runtime error. + +## Subtuple + +You can get a subtuple of a tuple by using the `[]` operator with a range. +What is returned is a new tuple with the elements from the range specified. +The range has to be given at compile time, otherwise the compiler will not be able to know the types of the elements in the subtuple. +This means that the range has to be a range literal and not assigned to a variable. + +```crystal +tuple = {1, "foo", 'c'} +subtuple = tuple[0..1] # Tuple(Int32, String) + +i = 0..1 +tuple[i] +# Error: Tuple#[](Range) can only be called with range literals known at compile-time +``` + +## When to use a Tuple + +Tuples are useful when you want to group a fixed number of values together where the types of the values are known at compile time. +This is because tuples require less memory and are faster than arrays due to the immutability of tuples. +Another use case is returning multiple values from a method. +This is particularly helpful if the values have different types since each position in the tuple can have a different type. + +Tuples should not be used when a data structure is needed that can grow or shrink in size or often needs to be modified. + +[tuple]: https://crystal-lang.org/reference/syntax_and_semantics/literals/tuple.html diff --git a/concepts/tuple/links.json b/concepts/tuple/links.json new file mode 100644 index 00000000..77bd76b6 --- /dev/null +++ b/concepts/tuple/links.json @@ -0,0 +1,10 @@ +[ + { + "url": "https://crystal-lang.org/reference/syntax_and_semantics/literals/tuple.html", + "description": "Crystal docs: Array" + }, + { + "url": "https://crystal-lang.org/api/Tuple.html", + "description": "Crystal api: Tuple" + } +] diff --git a/config.json b/config.json index 64be0d85..b00f1044 100644 --- a/config.json +++ b/config.json @@ -287,7 +287,21 @@ ], "prerequisites": [ "procs-blocks" - ] + ], + "status": "beta" + }, + { + "slug": "kitchen-calculator", + "name": "Kitchen Calculator", + "uuid": "e7250ff1-967d-4b5d-8fa8-a57fdbbe24c5", + "concepts": [ + "symbol", + "tuple" + ], + "prerequisites": [ + "iteration" + ], + "status": "beta" } ], "practice": [ @@ -1576,6 +1590,16 @@ "uuid": "0455ecbd-dc84-4799-b49e-bc9bf0a9fc71", "slug": "iteration", "name": "Iteration" + }, + { + "uuid": "a36e93cb-5ab0-4693-a6ab-ce698a3550ce", + "slug": "tuple", + "name": "Tuple" + }, + { + "uuid": "268c4865-1a15-4f65-88d9-5a1bb1ba5ac4", + "slug": "symbol", + "name": "Symbol" } ], "key_features": [ diff --git a/exercises/concept/kitchen-calculator/.docs/hints.md b/exercises/concept/kitchen-calculator/.docs/hints.md new file mode 100644 index 00000000..c05ed978 --- /dev/null +++ b/exercises/concept/kitchen-calculator/.docs/hints.md @@ -0,0 +1,26 @@ +# Hints + +## General + +- [Tuples][tuple] are data structures which are immutable, have a fixed size, and can hold any data-type. +- Symbols may be used to denote finite states: this exercise uses `:cup`, `:fluid_ounce`, `:teaspoon`, `:tablespoon`, `:milliliter` to denote the units used. + +## 1. Get the numeric component from a volume-pair + +- Consider indexing the tuple to return the volume-pair's numeric component. + +## 2. Convert the volume-pair to milliliters + +- Use control flow logic to determine which logic to apply based on the unit. +- Implement the method for all units to milliliters, including milliliters to milliliters. + +## 3. Convert the milliliter volume-pair to another unit + +- Use control flow logic to determine which logic to apply based on the unit. +- Implement the method for milliliters to all units, including milliliters to milliliters. + +## 4. Convert from any unit to any unit + +- Reuse the methods already created to perform the conversion in the `KitchenCalculator.convert` method. + +[tuple]: https://crystal-lang.org/reference/syntax_and_semantics/literals/tuple.html diff --git a/exercises/concept/kitchen-calculator/.docs/instructions.md b/exercises/concept/kitchen-calculator/.docs/instructions.md new file mode 100644 index 00000000..0dc29650 --- /dev/null +++ b/exercises/concept/kitchen-calculator/.docs/instructions.md @@ -0,0 +1,56 @@ +# Instructions + +While preparing to bake cookies for your friends, you have found that you have to convert some of the measurements used in the recipe. +Only being familiar with the metric system, you need to come up with a way to convert common US baking measurements to milliliters (mL) for your own ease. + +Use this conversion chart for your solution: + +| Unit to convert | volume | in milliliters (mL) | +| --------------- | ------ | ------------------- | +| mL | 1 | 1 | +| US cup | 1 | 240 | +| US fluid ounce | 1 | 30 | +| US teaspoon | 1 | 5 | +| US tablespoon | 1 | 15 | + +Being a talented programmer in training, you decide to use milliliters as a transition unit to facilitate the conversion from any unit listed to any other (even itself). + +## 1. Get the numeric component from a volume-pair + +Implement the `KitchenCalculator.get_volume` method. +Given a volume-pair tuple, it should return just the numeric component. + +```crystal +KitchenCalculator.get_volume({:cup, 2.0}) +# => 2.0 +``` + +## 2. Convert the volume-pair to milliliters + +Implement the `KitchenCalculator.to_milliliter` method. +Given a volume-pair tuple, it should convert the volume to milliliters using the conversion chart. + +```crystal +KitchenCalculator.to_milliliter({:cup, 2.5}) +# => {:milliliter, 600.0} +``` + +## 3. Convert the milliliter volume-pair to another unit + +Implement the `KitchenCalculator.from_milliliter` method. +Given a volume-pair tuple and the desired unit, it should convert the volume to the desired unit using the conversion chart. + +```crystal +KitchenCalculator.from_milliliter({:milliliter, 1320.0}, :cup) +# => {:cup, 5.5} +``` + +## 4. Convert from any unit to any unit + +Implement the `KitchenCalculator.convert` method. +Given a volume-pair tuple and the desired unit, it should convert the given volume to the desired unit. + +```crystal +KitchenCalculator.convert({:teaspoon, 9.0}, :tablespoon) +# => {:tablespoon, 3.0} +``` diff --git a/exercises/concept/kitchen-calculator/.docs/introduction.md b/exercises/concept/kitchen-calculator/.docs/introduction.md new file mode 100644 index 00000000..e700ee82 --- /dev/null +++ b/exercises/concept/kitchen-calculator/.docs/introduction.md @@ -0,0 +1,156 @@ +# Tuples + +A [tuple][tuple] is a finite ordered list of elements which is immutable. +Tuples requires all positions to have a fixed type. +This in turns means that the compiler knows what type is at each position. +The types used in a tuple can be different at each position, but the types must be known at compile time. + +## Creating a Tuple + +Depending on if the tuples values types can be interpreted under compilation, the tuple can be created in different ways. +If the values are known at compile time, the tuple can be created using the tuple literal syntax, otherwise they need to be explicitly declared. +It is also important that the types of the values match the types specified in the tuple and that the number of values matches the number of types specified. +Here is an example of defning through tuple literal syntax: + +```crystal +tuple = {1, "foo", 'c'} # Tuple(Int32, String, Char) +``` + +There is also the possibility to create a tuple using the `Tuple` class. + +```crystal +tuple = Tuple(Int32, String, Char).new(1, "foo", 'c') +``` + +Alternatively, you can explicitly specify the type of the variable assigned to the tuple. + +```crystal +tuple : Tuple(Int32, String, Char) = {1, "foo", 'c'} +``` + +Explicitly specifying the type of the tuple can be useful since that allows for defining that a position should hold a union type. +This means that a position can hold multiple types. + +```crystal +tuple : Tuple(Int32 | String, String, Char) = {1, "foo", 'c'} +``` + +## Conversion + +### Creating a tuple from an array + +You can create a tuple from an array using the `Tuple` class's `from` method. +This requires that the type of the tuple is specified. + +```crystal +array = [1, "foo", 'c'] +tuple = Tuple(Int32, String, Char).from(array) +``` + +### Conversion to Array + +You can convert a tuple to an array using the `to_a` method. +The resulting array's element type is the union of the type of each field in the tuple. + +```crystal +tuple = {1, "foo", 'c'} +array = tuple.to_a +array # => [1, "foo", 'c'] +``` + +## Accessing Elements + +Like arrays, tuples are zero-indexed, meaning that the first element is at index 0. +However, unlike arrays, the type of each element is fixed and known at compile time, therefore when indexing a tuple, the type of the element is specific to the position. +To access an element in a tuple, you can use the `[]` operator. + +```crystal +array = [1, "foo", 'c'] +array[0] # => 1 +typeof(array[0]) # => Int32 | String | Char + +tuple = {1, "foo", 'c'} +tuple[0] # => 1 +typeof(tuple[0]) # => Int32 +``` + +Another difference when it comes to accessing elements from arrays is that if the index is specified, then the compiler will check that the index is within the bounds of the tuple. +This means you will get a compile time error instead of a runtime error. + +```crystal +tuple = {1, "foo", 'c'} +tuple[3] +# => Error: index out of bounds for Tuple(Int32, String, Char) (3 not in -3..2) +``` + +However, if the index is stored in a variable, then the compiler will not be able to check if the index is within the bounds of the tuple at compile time and will instead give a runtime error. + +## Subtuple + +You can get a subtuple of a tuple by using the `[]` operator with a range. +What is returned is a new tuple with the elements from the range specified. +The range has to be given at compile time, otherwise the compiler will not be able to know the types of the elements in the subtuple. +This means that the range has to be a range literal and not assigned to a variable. + +```crystal +tuple = {1, "foo", 'c'} +subtuple = tuple[0..1] # Tuple(Int32, String) + +i = 0..1 +tuple[i] +# Error: Tuple#[](Range) can only be called with range literals known at compile-time +``` + +## When to use a Tuple + +Tuples are useful when you want to group a fixed number of values together where the types of the values are known at compile time. +This is because tuples require less memory and are faster than arrays due to the immutability of tuples. +Another use case is returning multiple values from a method. +This is particularly helpful if the values have different types since each position in the tuple can have a different type. + +Tuples should not be used when a data structure is needed that can grow or shrink in size or often needs to be modified. + +# About + +[Symbols][symbols] are named identifiers that can be used to refer to a value. +Symbols are created through a symbol literal, which is by prefixing a name with a `:` character, e.g. `:foo`. +They also allow for being written with quotes, e.g. `:"foo"`, which allows, for example, spaces in the name. + +```crystal +:foo # => :foo +:"foo boo" # => :"foo boo" +``` + +Symbols are used in many places in the language, including as keys in namedtuples and to represent method and variable names. + +## Symbols in Crystal + +Symbols in Crystal are quite different from Ruby. +In Crystal a symbol is a type of constant and is thereby is assigned at compile time. +This means that symbols can't be created dynamically, which is possible in Ruby. + +Symbols in Crystal are represented as `Int32`s which makes them very efficient. + +## Identifier + +What makes symbols different from strings is that they are identifiers and do not represent data or text. +This means that two symbols with the same name are always the same object. + +```ruby +"foo".object_id # => 60 +"foo".object_id # => 80 +:foo.object_id # => 1086748 +:foo.object_id # => 1086748 +``` + +## Conversion + +Symbols can be converted to strings but not vice versa. +This is because symbols are created at compile time and strings are created at runtime. + +```crystal +:foo.to_s # => "foo" +``` + +[tuple]: https://crystal-lang.org/reference/syntax_and_semantics/literals/tuple.html +[symbols]: https://crystal-lang.org/reference/syntax_and_semantics/literals/symbol.html diff --git a/exercises/concept/kitchen-calculator/.meta/config.json b/exercises/concept/kitchen-calculator/.meta/config.json new file mode 100644 index 00000000..a80ea50c --- /dev/null +++ b/exercises/concept/kitchen-calculator/.meta/config.json @@ -0,0 +1,12 @@ +{ + "authors": ["meatball133"], + "contributors": ["ryanplusplus"], + "files": { + "solution": ["src/kitchen_calculator.cr"], + "test": ["spec/kitchen_calculator_spec.cr"], + "exemplar": [".meta/src/exemplar.cr"] + }, + "blurb": "Learn about tuples and symbols by converting common US baking measurements to the metric system.", + "icon": "kitchen-calculator", + "forked_from": ["elixir/kitchen-calculator"] +} diff --git a/exercises/concept/kitchen-calculator/.meta/design.md b/exercises/concept/kitchen-calculator/.meta/design.md new file mode 100644 index 00000000..a7f2728a --- /dev/null +++ b/exercises/concept/kitchen-calculator/.meta/design.md @@ -0,0 +1,30 @@ +## Goal + +The goal of this exercise is to teach the student about symbols and tuples in Crystal. + +## Learning objectives + +- Know how to create a tuple +- Know how to get items from tuple +- Know how to define a symbol +- Know when you can use symbols + +## Out of scope + +- Named tuples + +## Concepts + +`Tuples`: + +- Create a Tuple +- Index tuples + +`Symbol`: + +- Create symbol +- Compare symbol + +## Prerequisites + +- Iteration diff --git a/exercises/concept/kitchen-calculator/.meta/src/exemplar.cr b/exercises/concept/kitchen-calculator/.meta/src/exemplar.cr new file mode 100644 index 00000000..adce8f30 --- /dev/null +++ b/exercises/concept/kitchen-calculator/.meta/src/exemplar.cr @@ -0,0 +1,39 @@ +module KitchenCalculator + def self.get_volume(volume_pair) + volume_pair[1] + end + + def self.to_milliliter(volume_pair) + case volume_pair[0] + when :cup + {:milliliter, volume_pair[1] * 240} + when :fluid_ounce + {:milliliter, volume_pair[1] * 30} + when :teaspoon + {:milliliter, volume_pair[1] * 5} + when :tablespoon + {:milliliter, volume_pair[1] * 15} + else + volume_pair + end + end + + def self.from_milliliter(volume_pair, unit) + case unit + when :cup + {:cup, volume_pair[1] / 240} + when :fluid_ounce + {:fluid_ounce, volume_pair[1] / 30} + when :teaspoon + {:teaspoon, volume_pair[1] / 5} + when :tablespoon + {:tablespoon, volume_pair[1] / 15} + else + volume_pair + end + end + + def self.convert(volume_pair, unit) + from_milliliter(to_milliliter(volume_pair), unit) + end +end diff --git a/exercises/concept/kitchen-calculator/.meta/template.md b/exercises/concept/kitchen-calculator/.meta/template.md new file mode 100644 index 00000000..78c597a9 --- /dev/null +++ b/exercises/concept/kitchen-calculator/.meta/template.md @@ -0,0 +1,2 @@ +{% $tuple \ $"tuple"."Tuples definition shorthand" %} +{% $symbol %} \ No newline at end of file diff --git a/exercises/concept/kitchen-calculator/spec/kitchen_calculator_spec.cr b/exercises/concept/kitchen-calculator/spec/kitchen_calculator_spec.cr new file mode 100644 index 00000000..e0996ab3 --- /dev/null +++ b/exercises/concept/kitchen-calculator/spec/kitchen_calculator_spec.cr @@ -0,0 +1,88 @@ +require "spec" +require "../src/*" + +describe KitchenCalculator do + describe "get_volume" do + it "get cups" do + KitchenCalculator.get_volume({:cup, 1}).should eq(1) + end + + it "get fluid ounces" do + KitchenCalculator.get_volume({:fluid_ounce, 2}).should eq(2) + end + + it "get teaspoons" do + KitchenCalculator.get_volume({:teaspoon, 3}).should eq(3) + end + + it "get tablespoons" do + KitchenCalculator.get_volume({:tablespoon, 4}).should eq(4) + end + + it "get milliliters" do + KitchenCalculator.get_volume({:milliliter, 5}).should eq(5) + end + end + + describe "to_milliliter" do + it "converts cups to milliliters" do + KitchenCalculator.to_milliliter({:milliliter, 3}).should eq({:milliliter, 3}) + end + + it "cups" do + KitchenCalculator.to_milliliter({:cup, 3}).should eq({:milliliter, 720}) + end + + it "fluid ounces" do + KitchenCalculator.to_milliliter({:fluid_ounce, 100}).should eq({:milliliter, 3000}) + end + + it "teaspoons" do + KitchenCalculator.to_milliliter({:teaspoon, 3}).should eq({:milliliter, 15}) + end + + it "tablespoons" do + KitchenCalculator.to_milliliter({:tablespoon, 3}).should eq({:milliliter, 45}) + end + end + + describe "from_milliliter" do + it "milliliters" do + KitchenCalculator.from_milliliter({:milliliter, 4}, :milliliter).should eq({:milliliter, 4}) + end + + it "cups" do + KitchenCalculator.from_milliliter({:milliliter, 840}, :cup).should eq({:cup, 3.5}) + end + + it "fluid ounces" do + KitchenCalculator.from_milliliter({:milliliter, 4522.5}, :fluid_ounce).should eq({:fluid_ounce, 150.75}) + end + + it "teaspoons" do + KitchenCalculator.from_milliliter({:milliliter, 61.25}, :teaspoon).should eq({:teaspoon, 12.25}) + end + + it "tablespoons" do + KitchenCalculator.from_milliliter({:milliliter, 71.25}, :tablespoon).should eq({:tablespoon, 4.75}) + end + end + + describe "convert" do + it "teaspoon to tablespoon" do + KitchenCalculator.convert({:teaspoon, 15}, :tablespoon).should eq({:tablespoon, 5}) + end + + it "cups to fluid ounces" do + KitchenCalculator.convert({:cup, 4}, :fluid_ounce).should eq({:fluid_ounce, 32}) + end + + it "fluid ounces to teaspoons" do + KitchenCalculator.convert({:fluid_ounce, 4}, :teaspoon).should eq({:teaspoon, 24}) + end + + it "tablespoons to cups" do + KitchenCalculator.convert({:tablespoon, 320}, :cup).should eq({:cup, 20}) + end + end +end diff --git a/exercises/concept/kitchen-calculator/src/kitchen_calculator.cr b/exercises/concept/kitchen-calculator/src/kitchen_calculator.cr new file mode 100644 index 00000000..30b57c3f --- /dev/null +++ b/exercises/concept/kitchen-calculator/src/kitchen_calculator.cr @@ -0,0 +1,17 @@ +module KitchenCalculator + def self.get_volume(volume_pair) + raise "Please implement the KitchenCalculator.get_volume method" + end + + def self.to_milliliter(volume_pair) + raise "Please implement the KitchenCalculator.to_milliliter method" + end + + def self.from_milliliter(volume_pair, unit) + raise "Please implement the KitchenCalculator.from_milliliter method" + end + + def self.convert(volume_pair, unit) + raise "Please implement the KitchenCalculator.convert method" + end +end