From 7d72c0e4e6d40f42f834d9bdf2d16ce363e49c1b Mon Sep 17 00:00:00 2001 From: meatball Date: Fri, 31 May 2024 23:01:42 +0200 Subject: [PATCH 1/6] Start --- concepts/symbol/.meta/config.json | 5 + concepts/symbol/.meta/template.md | 1 + concepts/symbol/about.md | 63 +++++++++++ concepts/symbol/introduction.md | 181 ++++++++++++++++++++++++++++++ concepts/symbol/links.json | 10 ++ 5 files changed, 260 insertions(+) create mode 100644 concepts/symbol/.meta/config.json create mode 100644 concepts/symbol/.meta/template.md create mode 100644 concepts/symbol/about.md create mode 100644 concepts/symbol/introduction.md create mode 100644 concepts/symbol/links.json diff --git a/concepts/symbol/.meta/config.json b/concepts/symbol/.meta/config.json new file mode 100644 index 00000000..bd4d691d --- /dev/null +++ b/concepts/symbol/.meta/config.json @@ -0,0 +1,5 @@ +{ + "blurb": "Array is a data structure which stores a collection of elements of a certain type. Arrays are indexable and mutable", + "authors": ["meatball133"], + "contributors": ["ryanplusplus"] +} diff --git a/concepts/symbol/.meta/template.md b/concepts/symbol/.meta/template.md new file mode 100644 index 00000000..2fb69d6c --- /dev/null +++ b/concepts/symbol/.meta/template.md @@ -0,0 +1 @@ +{% $array \ $"array"."Array pointer" %} \ No newline at end of file diff --git a/concepts/symbol/about.md b/concepts/symbol/about.md new file mode 100644 index 00000000..4530a646 --- /dev/null +++ b/concepts/symbol/about.md @@ -0,0 +1,63 @@ +# 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. + +```ruby +:foo # => :foo +:"foo boo" # => :"foo boo" +``` + +Symbols are used in many places in the language, including as keys in namedtuples, to represent method names and variable names. + +## Symbols in Crystal + +Symbols in Crystal is quite different from Ruby. +In Crystal is a symbol a form of constants 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 is represented as an `Int32` which makes 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 +``` + +## Modifying Symbols + +Symbols are immutable, which means that they cannot be modified. +This means that when you "modify" a symbol, you are actually creating a new symbol. +There are a few methods that can be used to manipulate symbols, they all return new symbols. +All methods can be found in the [Symbol API][symbols-api]. + +```ruby +:foo.upcase # => :FOO + +:foo.object_id # => 1086748 +:foo.upcase.object_id # => 60 +``` + +The benefit of symbols being immutable is that they are more memory efficient than strings, but also safer to use as identifiers. + +## Conversion + +Symbols can be converted to strings and vice versa. +This can be useful when you want to modify a symbol, or when you want to use a symbol as a string. +To present a string as a symbol, you can use the `String#to_sym` method, and to do the opposite, you can use the `Symbol#to_s` method. +Due to symbols having a limited set of methods, it can be useful to convert a symbol to a string to use string methods on it, if a new symbol is needed. + +```ruby +:foo.to_s # => "foo" +"foo".to_sym # => :foo +``` + +[symbols]: https://www.rubyguides.com/2018/02/ruby-symbols/ +[symbols-api]: https://rubyapi.org/o/symbol diff --git a/concepts/symbol/introduction.md b/concepts/symbol/introduction.md new file mode 100644 index 00000000..d2c450dd --- /dev/null +++ b/concepts/symbol/introduction.md @@ -0,0 +1,181 @@ +# Array + +[Array][array] is a mutable data structure that stores a collection of elements of a specific type. +An array is an [indexable][indexable] data structure. +Arrays being mutable means that if a method is called on an array that modifies the array, the array will be modified. +Meaning it doesn't have to be reassigned to the variable. + +To create an array, use the array literal denotation syntax (`[]`) and within it, specify the elements of the array separated by a comma. + +```crystal +[1, 2, 3] # => [1, 2, 3] +``` + +Crystal will infer the type of the array from the elements. + +```crystal +[1, 2, 3].class # => Array(Int32) +``` + +## Multi type Arrays + +Even if mentioned earlier about arrays being a collection of elements of a specific type, you can create an array with multiple types through the use of [union types][union-types]. +This makes so that the array can contain elements of any of the types specified in the union type. +Crystal will infer the type of the array from the elements. + +```crystal +[1, "2", 3.0] # => [1, "2", 3.0] + +[1, "2", 3.0].class # => Array(Int32 | Float64 | String) +``` + +## Multi-dimensional arrays + +A multi-dimensional array is an array of arrays. +This means that each element in the array is an array itself. +This can be useful when wanting to store data in a table format. +To define a multi-dimensional array, you can either write an array of arrays literal or use the `Array.new` constructor. + +```crystal +[[1, 2], [3, 4]] # => [[1, 2], [3, 4]] +numbers = Array(Array(Int32)).new() # => [] +numbers << [1, 2] # => [[1, 2]] +``` + +## Add an element to an array + +To add an element to an array, use the [`<<` (append) operator][append]. + +```crystal +[1, 2, 3] << 4 # => [1, 2, 3, 4] +``` + +It is important that the type of the element you want to add is the same as the type of the array, if it is not an error will be raised. + +```crystal +numbers : Array(Int32 | Float64) = [1, 2, 3] +numbers << 4.0 +numbers # => [1, 2, 3, 4.0] + +numbers << "5" # => Error: no overload matches 'Array(Int32 | Float64)#<<' with type String +``` + +## Size of an Array + +As with `String`, can you get the size of an array by using the [`size`][size] method. + +```crystal +[1, 2, 3].size # => 3 +``` + +## Empty Arrays + +When creating an empty array, the compiler cannot infer the type of the array. +Therefore, you need to specify the type of the array. +This can be done by specifying the type of the array, by using the `of` keyword, or by using the array initializer syntax, which is: `Array(T).new`. + +```crystal +[] of (Int32 | Float64 | String) # => [] +Array(Int32 | Float64 | String).new # => [] +``` + +## Accessing Elements + +As with `String`, you can access elements in an array by using the [`[]` (index) operator][index] and giving it the index of the element you want to access. +If the index is out of bounds, an `IndexError` will be raised. + +```crystal +[1, 2, 3][0] # => 1 + +[1, 2, 3][3] # => Index out of bounds (IndexError) +``` + +It is also possible to access elements by using a range. + +```crystal +[1, 2, 3][0..1] # => [1, 2] +``` + +## Create an array from a range + +To create an array from a range, use the [`to_a` method][to_a]. +This can be useful when you want to create an array of numbers. + +```crystal +(1..3).to_a # => [1, 2, 3] +``` + +## Create an array from a string + +To create an array from a string, use the [`split`][split] method. +This lets you split a string into an array of strings by using a delimiter. + +```crystal +"1,2,3".split(",") # => ["1", "2", "3"] +``` + +It is also possible to get an array of characters from a string by using the [`chars`][chars] method. + +```crystal +"123".chars # => ['1', '2', '3'] +``` + +To convert an array of `Char` or `String` to a `String` you can use the [`join`][join] method which takes a delimiter as an argument. + +```crystal +['1', '2', '3'].join(".") # => "1.2.3" +``` + +## Deleting element from an array + +When you want to delete an element from the end of an array, you can use [`pop`][pop] method which takes an optional argument specifying how many elements to remove from the end of the array. +The method returns the element that was removed. +If the array is empty an `IndexError` will be raised. + +```crystal +numbers = [1, 2, 3] +[1, 2, 3].pop # => 3 +numbers # => [1, 2] + +empty_numbers = [] of Int32 +empty_numbers.pop # => Index out of bounds (IndexError) +``` + +When you want to delete an element of a specific index from an array, you can use the [`delete_at`][delete_at] method which takes the index of the element to remove as an argument. +If the array is empty an `IndexError` will be raised. + +```crystal +numbers = [1, 2, 3] +[1, 2, 3].delete_at(1) # => 2 +numbers # => [1, 3] + +empty_numbers = [] of Int32 +empty_numbers.delete_at(0) # => Index out of bounds (IndexError) +``` + +## Modifying values in an array + +When you want to modify an element of a specific index from an array, you can use the [`[]=` (index assign) operator][index-assign] which takes the index of the element to modify and the new value as arguments. +If the index is out of bounds, an `IndexError` will be raised. + +```crystal +numbers = [1, 2, 3] +numbers[1] = 4 +numbers # => [1, 4, 3] + +numbers[3] = 5 # => Index out of bounds (IndexError) +``` + +[array]: https://crystal-lang.org/reference/syntax_and_semantics/literals/array.html +[pop]: https://crystal-lang.org/api/Array.html#pop%3AT-instance-method +[index]: https://crystal-lang.org/api/Indexable.html#%5B%5D%28index%3AInt%29-instance-method +[split]: https://crystal-lang.org/api/String.html#split%28separator%3AString%2Climit%3Dnil%2C%2A%2Cremove_empty%3Dfalse%29%3AArray%28String%29-instance-method +[indexable]: https://crystal-lang.org/api/Indexable.html +[union-types]: https://crystal-lang.org/reference/syntax_and_semantics/union_types.html +[append]: https://crystal-lang.org/api/Array.html#%3C%3C%28value%3AT%29%3Aself-instance-method +[size]: https://crystal-lang.org/api/Array.html#size%3AInt32-instance-method +[to_a]: https://crystal-lang.org/api/Enumerable.html#to_a-instance-method +[chars]: https://crystal-lang.org/api/String.html#chars%3AArray%28Char%29-instance-method +[join]: https://crystal-lang.org/api/Indexable.html#join%28separator%3AString%7CChar%7CNumber%3D%22%22%29%3AString-instance-method +[index-assign]: https://crystal-lang.org/api/Indexable/Mutable.html#%5B%5D%3D%28index%3AInt%2Cvalue%3AT%29%3AT-instance-method +[delete_at]: https://crystal-lang.org/api/Array.html#delete_at%28index%3AInt%29%3AT-instance-method diff --git a/concepts/symbol/links.json b/concepts/symbol/links.json new file mode 100644 index 00000000..3639f9d3 --- /dev/null +++ b/concepts/symbol/links.json @@ -0,0 +1,10 @@ +[ + { + "url": "https://crystal-lang.org/reference/syntax_and_semantics/literals/array.html", + "description": "Crystal docs: Array" + }, + { + "url": "https://crystal-lang.org/api/Array.html", + "description": "Crystal api: Array" + } +] From 1c3f95b99c34b14cecaebb88032a1f4f735a92af Mon Sep 17 00:00:00 2001 From: meatball Date: Mon, 17 Jun 2024 22:26:14 +0200 Subject: [PATCH 2/6] added exercise and tuple concept --- concepts/symbol/.meta/config.json | 4 +- concepts/symbol/.meta/template.md | 2 +- concepts/symbol/about.md | 20 +-- concepts/symbol/links.json | 8 +- concepts/tuples/.meta/config.json | 5 + concepts/tuples/.meta/template.md | 1 + concepts/tuples/about.md | 119 ++++++++++++++++++ concepts/tuples/introduction.md | 110 ++++++++++++++++ concepts/tuples/links.json | 10 ++ .../concept/kitchen-calculator/.docs/hints.md | 26 ++++ .../kitchen-calculator/.docs/instructions.md | 56 +++++++++ .../kitchen-calculator/.docs/introduction.md | 102 +++++++++++++++ .../kitchen-calculator/.meta/config.json | 11 ++ .../kitchen-calculator/.meta/design.md | 23 ++++ .../kitchen-calculator/.meta/src/exemplar.cr | 39 ++++++ .../kitchen-calculator/.meta/template.md | 2 + .../spec/kitchen_calculator_spec.cr | 88 +++++++++++++ .../src/kitchen_calculator.cr | 17 +++ 18 files changed, 618 insertions(+), 25 deletions(-) create mode 100644 concepts/tuples/.meta/config.json create mode 100644 concepts/tuples/.meta/template.md create mode 100644 concepts/tuples/about.md create mode 100644 concepts/tuples/introduction.md create mode 100644 concepts/tuples/links.json create mode 100644 exercises/concept/kitchen-calculator/.docs/hints.md create mode 100644 exercises/concept/kitchen-calculator/.docs/instructions.md create mode 100644 exercises/concept/kitchen-calculator/.docs/introduction.md create mode 100644 exercises/concept/kitchen-calculator/.meta/config.json create mode 100644 exercises/concept/kitchen-calculator/.meta/design.md create mode 100644 exercises/concept/kitchen-calculator/.meta/src/exemplar.cr create mode 100644 exercises/concept/kitchen-calculator/.meta/template.md create mode 100644 exercises/concept/kitchen-calculator/spec/kitchen_calculator_spec.cr create mode 100644 exercises/concept/kitchen-calculator/src/kitchen_calculator.cr diff --git a/concepts/symbol/.meta/config.json b/concepts/symbol/.meta/config.json index bd4d691d..1221916c 100644 --- a/concepts/symbol/.meta/config.json +++ b/concepts/symbol/.meta/config.json @@ -1,5 +1,5 @@ { - "blurb": "Array is a data structure which stores a collection of elements of a certain type. Arrays are indexable and mutable", + "blurb": "Symbol represent a unique identifier through the program, which is created at compile time", "authors": ["meatball133"], - "contributors": ["ryanplusplus"] + "contributors": [""] } diff --git a/concepts/symbol/.meta/template.md b/concepts/symbol/.meta/template.md index 2fb69d6c..cd2c1249 100644 --- a/concepts/symbol/.meta/template.md +++ b/concepts/symbol/.meta/template.md @@ -1 +1 @@ -{% $array \ $"array"."Array pointer" %} \ No newline at end of file +{% $symbol \ $"array"."Array pointer" %} \ No newline at end of file diff --git a/concepts/symbol/about.md b/concepts/symbol/about.md index 4530a646..f1667596 100644 --- a/concepts/symbol/about.md +++ b/concepts/symbol/about.md @@ -31,28 +31,12 @@ This means that two symbols with the same name are always the same object. :foo.object_id # => 1086748 ``` -## Modifying Symbols - -Symbols are immutable, which means that they cannot be modified. -This means that when you "modify" a symbol, you are actually creating a new symbol. -There are a few methods that can be used to manipulate symbols, they all return new symbols. -All methods can be found in the [Symbol API][symbols-api]. - -```ruby -:foo.upcase # => :FOO - -:foo.object_id # => 1086748 -:foo.upcase.object_id # => 60 -``` - -The benefit of symbols being immutable is that they are more memory efficient than strings, but also safer to use as identifiers. - ## Conversion -Symbols can be converted to strings and vice versa. +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. This can be useful when you want to modify a symbol, or when you want to use a symbol as a string. To present a string as a symbol, you can use the `String#to_sym` method, and to do the opposite, you can use the `Symbol#to_s` method. -Due to symbols having a limited set of methods, it can be useful to convert a symbol to a string to use string methods on it, if a new symbol is needed. ```ruby :foo.to_s # => "foo" diff --git a/concepts/symbol/links.json b/concepts/symbol/links.json index 3639f9d3..a6d207ca 100644 --- a/concepts/symbol/links.json +++ b/concepts/symbol/links.json @@ -1,10 +1,10 @@ [ { - "url": "https://crystal-lang.org/reference/syntax_and_semantics/literals/array.html", - "description": "Crystal docs: Array" + "url": "https://crystal-lang.org/reference/syntax_and_semantics/literals/symbol.html", + "description": "Crystal docs: Symbol" }, { - "url": "https://crystal-lang.org/api/Array.html", - "description": "Crystal api: Array" + "url": "https://crystal-lang.org/api/Symbol.html", + "description": "Crystal api: Symbol" } ] diff --git a/concepts/tuples/.meta/config.json b/concepts/tuples/.meta/config.json new file mode 100644 index 00000000..dfc9ef8c --- /dev/null +++ b/concepts/tuples/.meta/config.json @@ -0,0 +1,5 @@ +{ + "blurb": "Tuples is a data structure which stores a fixed amount of elements. Tuples are indexable and inmutable", + "authors": ["meatball133"], + "contributors": [] +} diff --git a/concepts/tuples/.meta/template.md b/concepts/tuples/.meta/template.md new file mode 100644 index 00000000..90614142 --- /dev/null +++ b/concepts/tuples/.meta/template.md @@ -0,0 +1 @@ +{% $tuples \ $"tuples"."Tuples definition shorthand" %} \ No newline at end of file diff --git a/concepts/tuples/about.md b/concepts/tuples/about.md new file mode 100644 index 00000000..80be1547 --- /dev/null +++ b/concepts/tuples/about.md @@ -0,0 +1,119 @@ +# 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 each position is. +The types used in a tuple can be different, but the types must be known at compile time. + +## Creating a Tuple + +Depending on if the tuples values types can be interprited under compilation, the tuple can be created in different ways. +It is also important that the types of the values match the types specified in the tuple and that the number of values match the number of types specified. +If the values are known at compile time, the tuple can be created using the tuple literal syntax. + +```crystal +tuple = {1, "foo", 'c'} # Tuple(Int32, String, Char) +``` + +There is also 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 usefull 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. + +```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. +Meaning 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 as such 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, since 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 is faster than arrays due to the immutability of tuples. +Another use case is when you want to return multiple fixed values from a method. +This is particular helpful if the values have different types since each position in the tuple can have a different type. + +When to not use a Tuple is if a datastructure 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/tuples/introduction.md b/concepts/tuples/introduction.md new file mode 100644 index 00000000..79718416 --- /dev/null +++ b/concepts/tuples/introduction.md @@ -0,0 +1,110 @@ +# 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 each position is. +The types used in a tuple can be different, but the types must be known at compile time. + +## Creating a Tuple + +Depending on if the tuples values types can be interprited under compilation, the tuple can be created in different ways. +It is also important that the types of the values match the types specified in the tuple and that the number of values match the number of types specified. +If the values are known at compile time, the tuple can be created using the tuple literal syntax. + +```crystal +tuple = {1, "foo", 'c'} # Tuple(Int32, String, Char) +``` + +There is also 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 usefull 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. + +```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. +Meaning 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 as such 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, since 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 is faster than arrays due to the immutability of tuples. +Another use case is when you want to return multiple fixed values from a method. +This is particular helpful if the values have different types since each position in the tuple can have a different type. + +When to not use a Tuple is if a datastructure 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/tuples/links.json b/concepts/tuples/links.json new file mode 100644 index 00000000..77bd76b6 --- /dev/null +++ b/concepts/tuples/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/exercises/concept/kitchen-calculator/.docs/hints.md b/exercises/concept/kitchen-calculator/.docs/hints.md new file mode 100644 index 00000000..42e626ac --- /dev/null +++ b/exercises/concept/kitchen-calculator/.docs/hints.md @@ -0,0 +1,26 @@ +# Hints + +## General + +- [Tuples][tuple] are data structures which are inmutable, have a fixed size, and can hold any data-type. +- Symbols may be used to denote finite states, as 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..dc0219f8 --- /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. +Being only 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} +``` \ No newline at end of file diff --git a/exercises/concept/kitchen-calculator/.docs/introduction.md b/exercises/concept/kitchen-calculator/.docs/introduction.md new file mode 100644 index 00000000..5e2d5472 --- /dev/null +++ b/exercises/concept/kitchen-calculator/.docs/introduction.md @@ -0,0 +1,102 @@ +# Modules + +[Modules][modules] in Crystal serve 2 purposes: + +The first purpose is to create a [namespace][namespace] to avoid name collisions. +But it also forms as a form of grouping code together, this is to make it easier to understand what the code is for. + +The second purpose is to define [mixins][mixin] to share code to types. + +Modules have similarities to classes, but the main difference is that modules cannot be instantiated, and thereby don't have instance variables. + +To declare a module you use the `module` keyword followed by the name of the module. + +```crystal +module Foo +end +``` + +## Namespace + +A namespace is a way to group code together, this is to avoid name clashes, but also to make it easier to understand what the code is for. +When wanting to access for example a constant or a class that has been placed inside a namespace you use the `::` operator. + +```crystal +module Foo + class Bar + end +end + +Foo::Bar.new +``` + +## Use it as a mixin + +This can be useful when, for example, wanting multiple classes to have the same "base" functionality or when wanting to share code between classes that are not related. +Or when wanting to share code between classes that are not related. + +There are 2 different ways to use a module as a mixin: the first one is to use the `include` keyword, the second one is to use the `extend` keyword. + +Both methods will make constants available to the type that includes or extends the module. + +### Include + +Include will make all methods in the module available as instance methods on the type that includes the module. +The `include` keyword should be written at the top of the type, followed by the name of the module. + +```crystal +module Foo + def foo + "foo" + end +end + +class Bar + include Foo +end + +Bar.new.foo # => "foo" +``` + +### Extend + +Extend works similarly to include, but instead of making the methods available as instance methods, it makes them available as class methods. +The `extend` keyword should be written at the top of the type followed by the name of the module. + +```crystal +module Foo + def foo + "foo" + end +end + +class Bar + extend Foo +end + +Bar.foo # => "foo" +``` + +## Extend self + +A quite common pattern in Crystal is to use the [`extend self`][extend self] pattern, in a module. +This will make all methods in the module available as class methods on the module itself. +This means you don't have to assign each method to the module itself using the `def self.method_name` syntax. + + +```crystal +module Foo + extend self + + def foo + "foo" + end +end + +Foo.foo # => "foo" +``` + +[mixin]: https://en.wikipedia.org/wiki/Mixin +[modules]: https://crystal-lang.org/reference/syntax_and_semantics/modules.html +[extend self]: https://crystal-lang.org/reference/syntax_and_semantics/modules.html#extend-self +[namespace]: https://en.wikipedia.org/wiki/Namespace diff --git a/exercises/concept/kitchen-calculator/.meta/config.json b/exercises/concept/kitchen-calculator/.meta/config.json new file mode 100644 index 00000000..eb15841a --- /dev/null +++ b/exercises/concept/kitchen-calculator/.meta/config.json @@ -0,0 +1,11 @@ +{ + "authors": ["meatball133"], + "contributors": [], + "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" +} diff --git a/exercises/concept/kitchen-calculator/.meta/design.md b/exercises/concept/kitchen-calculator/.meta/design.md new file mode 100644 index 00000000..248dd919 --- /dev/null +++ b/exercises/concept/kitchen-calculator/.meta/design.md @@ -0,0 +1,23 @@ +## Goal + +The goal of this exercise is to teach the student about modules in Crystal. + +## Learning objectives + +- Know how to create a module +- How to include a module in a class + +## Out of scope + +- How to use mixins between classes + +## Concepts + +`Modules`: + +- Create a module +- Include a module in a class + +## Prerequisites + +- return 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..77d1ffc5 --- /dev/null +++ b/exercises/concept/kitchen-calculator/.meta/template.md @@ -0,0 +1,2 @@ +{% $tuples \ $"tuples"."Tuples definition shorthand" %} +{% $symbols %} \ 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 From 72d01f56b40ff3b36c1beaadc5aad0b5f2c4c036 Mon Sep 17 00:00:00 2001 From: meatball Date: Fri, 28 Jun 2024 13:28:06 +0200 Subject: [PATCH 3/6] Templating and more work --- concepts/symbol/.meta/template.md | 2 +- concepts/symbol/about.md | 11 +- concepts/symbol/introduction.md | 188 +++--------------- .../kitchen-calculator/.docs/introduction.md | 177 +++++++++++------ .../kitchen-calculator/.meta/template.md | 2 +- 5 files changed, 145 insertions(+), 235 deletions(-) diff --git a/concepts/symbol/.meta/template.md b/concepts/symbol/.meta/template.md index cd2c1249..cda2d9f1 100644 --- a/concepts/symbol/.meta/template.md +++ b/concepts/symbol/.meta/template.md @@ -1 +1 @@ -{% $symbol \ $"array"."Array pointer" %} \ No newline at end of file +{% $symbol %} \ No newline at end of file diff --git a/concepts/symbol/about.md b/concepts/symbol/about.md index f1667596..0dc1ba2f 100644 --- a/concepts/symbol/about.md +++ b/concepts/symbol/about.md @@ -4,7 +4,7 @@ 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. -```ruby +```crystal :foo # => :foo :"foo boo" # => :"foo boo" ``` @@ -35,13 +35,10 @@ This means that two symbols with the same name are always the same object. 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. -This can be useful when you want to modify a symbol, or when you want to use a symbol as a string. -To present a string as a symbol, you can use the `String#to_sym` method, and to do the opposite, you can use the `Symbol#to_s` method. -```ruby +```crystal :foo.to_s # => "foo" -"foo".to_sym # => :foo ``` -[symbols]: https://www.rubyguides.com/2018/02/ruby-symbols/ -[symbols-api]: https://rubyapi.org/o/symbol +[symbols]: https://crystal-lang.org/reference/syntax_and_semantics/literals/symbol.html +[symbols-api]: https://crystal-lang.org/api/Symbol.html diff --git a/concepts/symbol/introduction.md b/concepts/symbol/introduction.md index d2c450dd..b8942d73 100644 --- a/concepts/symbol/introduction.md +++ b/concepts/symbol/introduction.md @@ -1,181 +1,43 @@ -# Array +# About -[Array][array] is a mutable data structure that stores a collection of elements of a specific type. -An array is an [indexable][indexable] data structure. -Arrays being mutable means that if a method is called on an array that modifies the array, the array will be modified. -Meaning it doesn't have to be reassigned to the variable. - -To create an array, use the array literal denotation syntax (`[]`) and within it, specify the elements of the array separated by a comma. - -```crystal -[1, 2, 3] # => [1, 2, 3] -``` - -Crystal will infer the type of the array from the elements. - -```crystal -[1, 2, 3].class # => Array(Int32) -``` - -## Multi type Arrays - -Even if mentioned earlier about arrays being a collection of elements of a specific type, you can create an array with multiple types through the use of [union types][union-types]. -This makes so that the array can contain elements of any of the types specified in the union type. -Crystal will infer the type of the array from the elements. - -```crystal -[1, "2", 3.0] # => [1, "2", 3.0] - -[1, "2", 3.0].class # => Array(Int32 | Float64 | String) -``` - -## Multi-dimensional arrays - -A multi-dimensional array is an array of arrays. -This means that each element in the array is an array itself. -This can be useful when wanting to store data in a table format. -To define a multi-dimensional array, you can either write an array of arrays literal or use the `Array.new` constructor. - -```crystal -[[1, 2], [3, 4]] # => [[1, 2], [3, 4]] -numbers = Array(Array(Int32)).new() # => [] -numbers << [1, 2] # => [[1, 2]] -``` - -## Add an element to an array - -To add an element to an array, use the [`<<` (append) operator][append]. - -```crystal -[1, 2, 3] << 4 # => [1, 2, 3, 4] -``` - -It is important that the type of the element you want to add is the same as the type of the array, if it is not an error will be raised. - -```crystal -numbers : Array(Int32 | Float64) = [1, 2, 3] -numbers << 4.0 -numbers # => [1, 2, 3, 4.0] - -numbers << "5" # => Error: no overload matches 'Array(Int32 | Float64)#<<' with type String -``` - -## Size of an Array - -As with `String`, can you get the size of an array by using the [`size`][size] method. - -```crystal -[1, 2, 3].size # => 3 -``` - -## Empty Arrays - -When creating an empty array, the compiler cannot infer the type of the array. -Therefore, you need to specify the type of the array. -This can be done by specifying the type of the array, by using the `of` keyword, or by using the array initializer syntax, which is: `Array(T).new`. - -```crystal -[] of (Int32 | Float64 | String) # => [] -Array(Int32 | Float64 | String).new # => [] -``` - -## Accessing Elements - -As with `String`, you can access elements in an array by using the [`[]` (index) operator][index] and giving it the index of the element you want to access. -If the index is out of bounds, an `IndexError` will be raised. - -```crystal -[1, 2, 3][0] # => 1 - -[1, 2, 3][3] # => Index out of bounds (IndexError) -``` - -It is also possible to access elements by using a range. - -```crystal -[1, 2, 3][0..1] # => [1, 2] -``` - -## Create an array from a range - -To create an array from a range, use the [`to_a` method][to_a]. -This can be useful when you want to create an array of numbers. +[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 -(1..3).to_a # => [1, 2, 3] +:foo # => :foo +:"foo boo" # => :"foo boo" ``` -## Create an array from a string +Symbols are used in many places in the language, including as keys in namedtuples, to represent method names and variable names. -To create an array from a string, use the [`split`][split] method. -This lets you split a string into an array of strings by using a delimiter. +## Symbols in Crystal -```crystal -"1,2,3".split(",") # => ["1", "2", "3"] -``` +Symbols in Crystal is quite different from Ruby. +In Crystal is a symbol a form of constants and is thereby is assigned at compile time. +This means that symbols can't be created dynamically, which is possible in Ruby. -It is also possible to get an array of characters from a string by using the [`chars`][chars] method. +Symbols in Crystal is represented as an `Int32` which makes very efficient. -```crystal -"123".chars # => ['1', '2', '3'] -``` +## Identifier -To convert an array of `Char` or `String` to a `String` you can use the [`join`][join] method which takes a delimiter as an argument. +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. -```crystal -['1', '2', '3'].join(".") # => "1.2.3" +```ruby +"foo".object_id # => 60 +"foo".object_id # => 80 +:foo.object_id # => 1086748 +:foo.object_id # => 1086748 ``` -## Deleting element from an array +## Conversion -When you want to delete an element from the end of an array, you can use [`pop`][pop] method which takes an optional argument specifying how many elements to remove from the end of the array. -The method returns the element that was removed. -If the array is empty an `IndexError` will be raised. +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 -numbers = [1, 2, 3] -[1, 2, 3].pop # => 3 -numbers # => [1, 2] - -empty_numbers = [] of Int32 -empty_numbers.pop # => Index out of bounds (IndexError) -``` - -When you want to delete an element of a specific index from an array, you can use the [`delete_at`][delete_at] method which takes the index of the element to remove as an argument. -If the array is empty an `IndexError` will be raised. - -```crystal -numbers = [1, 2, 3] -[1, 2, 3].delete_at(1) # => 2 -numbers # => [1, 3] - -empty_numbers = [] of Int32 -empty_numbers.delete_at(0) # => Index out of bounds (IndexError) -``` - -## Modifying values in an array - -When you want to modify an element of a specific index from an array, you can use the [`[]=` (index assign) operator][index-assign] which takes the index of the element to modify and the new value as arguments. -If the index is out of bounds, an `IndexError` will be raised. - -```crystal -numbers = [1, 2, 3] -numbers[1] = 4 -numbers # => [1, 4, 3] - -numbers[3] = 5 # => Index out of bounds (IndexError) +:foo.to_s # => "foo" ``` -[array]: https://crystal-lang.org/reference/syntax_and_semantics/literals/array.html -[pop]: https://crystal-lang.org/api/Array.html#pop%3AT-instance-method -[index]: https://crystal-lang.org/api/Indexable.html#%5B%5D%28index%3AInt%29-instance-method -[split]: https://crystal-lang.org/api/String.html#split%28separator%3AString%2Climit%3Dnil%2C%2A%2Cremove_empty%3Dfalse%29%3AArray%28String%29-instance-method -[indexable]: https://crystal-lang.org/api/Indexable.html -[union-types]: https://crystal-lang.org/reference/syntax_and_semantics/union_types.html -[append]: https://crystal-lang.org/api/Array.html#%3C%3C%28value%3AT%29%3Aself-instance-method -[size]: https://crystal-lang.org/api/Array.html#size%3AInt32-instance-method -[to_a]: https://crystal-lang.org/api/Enumerable.html#to_a-instance-method -[chars]: https://crystal-lang.org/api/String.html#chars%3AArray%28Char%29-instance-method -[join]: https://crystal-lang.org/api/Indexable.html#join%28separator%3AString%7CChar%7CNumber%3D%22%22%29%3AString-instance-method -[index-assign]: https://crystal-lang.org/api/Indexable/Mutable.html#%5B%5D%3D%28index%3AInt%2Cvalue%3AT%29%3AT-instance-method -[delete_at]: https://crystal-lang.org/api/Array.html#delete_at%28index%3AInt%29%3AT-instance-method +[symbols]: https://crystal-lang.org/reference/syntax_and_semantics/literals/symbol.html diff --git a/exercises/concept/kitchen-calculator/.docs/introduction.md b/exercises/concept/kitchen-calculator/.docs/introduction.md index 5e2d5472..8be8817f 100644 --- a/exercises/concept/kitchen-calculator/.docs/introduction.md +++ b/exercises/concept/kitchen-calculator/.docs/introduction.md @@ -1,102 +1,153 @@ -# Modules +# Tuples -[Modules][modules] in Crystal serve 2 purposes: +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 each position is. +The types used in a tuple can be different, but the types must be known at compile time. -The first purpose is to create a [namespace][namespace] to avoid name collisions. -But it also forms as a form of grouping code together, this is to make it easier to understand what the code is for. +## Creating a Tuple -The second purpose is to define [mixins][mixin] to share code to types. +Depending on if the tuples values types can be interprited under compilation, the tuple can be created in different ways. +It is also important that the types of the values match the types specified in the tuple and that the number of values match the number of types specified. +If the values are known at compile time, the tuple can be created using the tuple literal syntax. -Modules have similarities to classes, but the main difference is that modules cannot be instantiated, and thereby don't have instance variables. +```crystal +tuple = {1, "foo", 'c'} # Tuple(Int32, String, Char) +``` -To declare a module you use the `module` keyword followed by the name of the module. +There is also possibility to create a tuple using the `Tuple` class. ```crystal -module Foo -end +tuple = Tuple(Int32, String, Char).new(1, "foo", 'c') ``` -## Namespace - -A namespace is a way to group code together, this is to avoid name clashes, but also to make it easier to understand what the code is for. -When wanting to access for example a constant or a class that has been placed inside a namespace you use the `::` operator. +Alternatively, you can explicitly specify the type of the variable assigned to the tuple. ```crystal -module Foo - class Bar - end -end +tuple : Tuple(Int32, String, Char) = {1, "foo", 'c'} +``` -Foo::Bar.new +Explicitly specifying the type of the tuple can be usefull 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'} ``` -## Use it as a mixin +## Conversion -This can be useful when, for example, wanting multiple classes to have the same "base" functionality or when wanting to share code between classes that are not related. -Or when wanting to share code between classes that are not related. +### Creating a tuple from an array -There are 2 different ways to use a module as a mixin: the first one is to use the `include` keyword, the second one is to use the `extend` keyword. +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. -Both methods will make constants available to the type that includes or extends the module. +```crystal +array = [1, "foo", 'c'] +tuple = Tuple(Int32, String, Char).from(array) +``` -### Include +### Conversion to Array -Include will make all methods in the module available as instance methods on the type that includes the module. -The `include` keyword should be written at the top of the type, followed by the name of the module. +You can convert a tuple to an array using the `to_a` method. ```crystal -module Foo - def foo - "foo" - end -end +tuple = {1, "foo", 'c'} +array = tuple.to_a +array # => [1, "foo", 'c'] +``` -class Bar - include Foo -end +## Accessing Elements -Bar.new.foo # => "foo" -``` +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 -### Extend +tuple = {1, "foo", 'c'} +tuple[0] # => 1 +typeof(tuple[0]) # => Int32 +``` -Extend works similarly to include, but instead of making the methods available as instance methods, it makes them available as class methods. -The `extend` keyword should be written at the top of the type followed by the name of the module. +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. +Meaning you will get a compile time error instead of a runtime error. ```crystal -module Foo - def foo - "foo" - end -end +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 as such give a runtime error. + +## Subtuple -class Bar - extend Foo -end +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, since 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. -Bar.foo # => "foo" +```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 ``` -## Extend self +## 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 is faster than arrays due to the immutability of tuples. +Another use case is when you want to return multiple fixed values from a method. +This is particular helpful if the values have different types since each position in the tuple can have a different type. + +When to not use a Tuple is if a datastructure is needed that can grow or shrink in size or often needs to be modified. -A quite common pattern in Crystal is to use the [`extend self`][extend self] pattern, in a module. -This will make all methods in the module available as class methods on the module itself. -This means you don't have to assign each method to the module itself using the `def self.method_name` syntax. +# 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 -module Foo - extend self +:foo # => :foo +:"foo boo" # => :"foo boo" +``` + +Symbols are used in many places in the language, including as keys in namedtuples, to represent method names and variable names. + +## Symbols in Crystal + +Symbols in Crystal is quite different from Ruby. +In Crystal is a symbol a form of constants and is thereby is assigned at compile time. +This means that symbols can't be created dynamically, which is possible in Ruby. - def foo - "foo" - end -end +Symbols in Crystal is represented as an `Int32` which makes very efficient. -Foo.foo # => "foo" +## 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" ``` -[mixin]: https://en.wikipedia.org/wiki/Mixin -[modules]: https://crystal-lang.org/reference/syntax_and_semantics/modules.html -[extend self]: https://crystal-lang.org/reference/syntax_and_semantics/modules.html#extend-self -[namespace]: https://en.wikipedia.org/wiki/Namespace +[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/template.md b/exercises/concept/kitchen-calculator/.meta/template.md index 77d1ffc5..adbaca7f 100644 --- a/exercises/concept/kitchen-calculator/.meta/template.md +++ b/exercises/concept/kitchen-calculator/.meta/template.md @@ -1,2 +1,2 @@ {% $tuples \ $"tuples"."Tuples definition shorthand" %} -{% $symbols %} \ No newline at end of file +{% $symbol %} \ No newline at end of file From cdf9dac4aa4566c399f11c0b2f78596555fb5823 Mon Sep 17 00:00:00 2001 From: meatball <69751659+meatball133@users.noreply.github.com> Date: Wed, 7 Aug 2024 14:08:52 +0000 Subject: [PATCH 4/6] Update configs --- config.json | 23 +++++++++++++++++++ .../kitchen-calculator/.meta/config.json | 3 ++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/config.json b/config.json index c288fbc8..e4634659 100644 --- a/config.json +++ b/config.json @@ -277,6 +277,19 @@ "array-methods" ], "status": "beta" + }, + { + "slug": "kitchen-calculator", + "name": "Kitcen Calculator", + "uuid": "e7250ff1-967d-4b5d-8fa8-a57fdbbe24c5", + "concepts": [ + "symbol", + "tuples" + ], + "prerequisites": [ + "procs-blocks" + ], + "status": "beta" } ], "practice": [ @@ -1552,6 +1565,16 @@ "uuid": "22b6c054-9705-46c9-a14e-cf051c1d653a", "slug": "procs-blocks", "name": "Procs & Blocks" + }, + { + "uuid": "a36e93cb-5ab0-4693-a6ab-ce698a3550ce", + "slug": "tuples", + "name": "Tuples" + }, + { + "uuid": "268c4865-1a15-4f65-88d9-5a1bb1ba5ac4", + "slug": "symbol", + "name": "Symbol" } ], "key_features": [ diff --git a/exercises/concept/kitchen-calculator/.meta/config.json b/exercises/concept/kitchen-calculator/.meta/config.json index eb15841a..e554343f 100644 --- a/exercises/concept/kitchen-calculator/.meta/config.json +++ b/exercises/concept/kitchen-calculator/.meta/config.json @@ -7,5 +7,6 @@ "exemplar": [".meta/src/exemplar.cr"] }, "blurb": "Learn about tuples and symbols by converting common US baking measurements to the metric system.", - "icon": "kitchen-calculator" + "icon": "kitchen-calculator", + "forked_from": ["elixir/kitchen-calculator"] } From 9787f6c9df0ccd07bced1fec0fff12398dcc7373 Mon Sep 17 00:00:00 2001 From: meatball <69751659+meatball133@users.noreply.github.com> Date: Wed, 7 Aug 2024 14:16:05 +0000 Subject: [PATCH 5/6] Fix zero length string --- concepts/symbol/.meta/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concepts/symbol/.meta/config.json b/concepts/symbol/.meta/config.json index 1221916c..be9cbe45 100644 --- a/concepts/symbol/.meta/config.json +++ b/concepts/symbol/.meta/config.json @@ -1,5 +1,5 @@ { "blurb": "Symbol represent a unique identifier through the program, which is created at compile time", "authors": ["meatball133"], - "contributors": [""] + "contributors": [] } From 7cec5cf4d32783a5f61615a1f327dd419f222ef9 Mon Sep 17 00:00:00 2001 From: meatball <69751659+meatball133@users.noreply.github.com> Date: Sat, 10 Aug 2024 17:51:22 +0000 Subject: [PATCH 6/6] Update based on feedback --- concepts/symbol/.meta/config.json | 4 +- concepts/symbol/about.md | 13 +++--- concepts/symbol/introduction.md | 12 ++--- concepts/tuple/.meta/config.json | 5 +++ concepts/tuple/.meta/template.md | 1 + concepts/{tuples => tuple}/about.md | 33 +++++++------- concepts/{tuples => tuple}/introduction.md | 33 +++++++------- concepts/{tuples => tuple}/links.json | 0 concepts/tuples/.meta/config.json | 5 --- concepts/tuples/.meta/template.md | 1 - config.json | 8 ++-- .../concept/kitchen-calculator/.docs/hints.md | 4 +- .../kitchen-calculator/.docs/instructions.md | 4 +- .../kitchen-calculator/.docs/introduction.md | 45 ++++++++++--------- .../kitchen-calculator/.meta/config.json | 2 +- .../kitchen-calculator/.meta/design.md | 23 ++++++---- .../kitchen-calculator/.meta/template.md | 2 +- 17 files changed, 105 insertions(+), 90 deletions(-) create mode 100644 concepts/tuple/.meta/config.json create mode 100644 concepts/tuple/.meta/template.md rename concepts/{tuples => tuple}/about.md (65%) rename concepts/{tuples => tuple}/introduction.md (63%) rename concepts/{tuples => tuple}/links.json (100%) delete mode 100644 concepts/tuples/.meta/config.json delete mode 100644 concepts/tuples/.meta/template.md diff --git a/concepts/symbol/.meta/config.json b/concepts/symbol/.meta/config.json index be9cbe45..8adc4faa 100644 --- a/concepts/symbol/.meta/config.json +++ b/concepts/symbol/.meta/config.json @@ -1,5 +1,5 @@ { - "blurb": "Symbol represent a unique identifier through the program, which is created at compile time", + "blurb": "Symbols represent unique identifiers throughout the program, which is created at compile time", "authors": ["meatball133"], - "contributors": [] + "contributors": ["ryanplusplus"] } diff --git a/concepts/symbol/about.md b/concepts/symbol/about.md index 0dc1ba2f..5224074f 100644 --- a/concepts/symbol/about.md +++ b/concepts/symbol/about.md @@ -9,19 +9,19 @@ They also allow for being written with quotes, e.g. `:"foo"`, which allows, for :"foo boo" # => :"foo boo" ``` -Symbols are used in many places in the language, including as keys in namedtuples, to represent method names and variable names. +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 is quite different from Ruby. -In Crystal is a symbol a form of constants and is thereby is assigned at compile time. +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 is represented as an `Int32` which makes very efficient. +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. +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 @@ -34,11 +34,10 @@ This means that two symbols with the same name are always the same object. ## 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. +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 -[symbols-api]: https://crystal-lang.org/api/Symbol.html diff --git a/concepts/symbol/introduction.md b/concepts/symbol/introduction.md index b8942d73..5224074f 100644 --- a/concepts/symbol/introduction.md +++ b/concepts/symbol/introduction.md @@ -9,19 +9,19 @@ They also allow for being written with quotes, e.g. `:"foo"`, which allows, for :"foo boo" # => :"foo boo" ``` -Symbols are used in many places in the language, including as keys in namedtuples, to represent method names and variable names. +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 is quite different from Ruby. -In Crystal is a symbol a form of constants and is thereby is assigned at compile time. +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 is represented as an `Int32` which makes very efficient. +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. +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 @@ -34,7 +34,7 @@ This means that two symbols with the same name are always the same object. ## 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. +This is because symbols are created at compile time and strings are created at runtime. ```crystal :foo.to_s # => "foo" 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/tuples/about.md b/concepts/tuple/about.md similarity index 65% rename from concepts/tuples/about.md rename to concepts/tuple/about.md index 80be1547..7370396f 100644 --- a/concepts/tuples/about.md +++ b/concepts/tuple/about.md @@ -1,20 +1,22 @@ # 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 each position is. -The types used in a tuple can be different, but the types must be known at compile time. +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 interprited under compilation, the tuple can be created in different ways. -It is also important that the types of the values match the types specified in the tuple and that the number of values match the number of types specified. -If the values are known at compile time, the tuple can be created using the tuple literal syntax. +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 possibility to create a tuple using the `Tuple` class. +There is also the possibility to create a tuple using the `Tuple` class. ```crystal tuple = Tuple(Int32, String, Char).new(1, "foo", 'c') @@ -26,7 +28,7 @@ Alternatively, you can explicitly specify the type of the variable assigned to t tuple : Tuple(Int32, String, Char) = {1, "foo", 'c'} ``` -Explicitly specifying the type of the tuple can be usefull since that allows for defining that a position should hold a union type. +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 @@ -48,6 +50,7 @@ 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'} @@ -81,7 +84,7 @@ 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. -Meaning you will get a compile time error instead of a runtime error. +This means you will get a compile time error instead of a runtime error. ```crystal tuple = {1, "foo", 'c'} @@ -89,13 +92,13 @@ 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 as such give a runtime error. +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, since otherwise the compiler will not be able to know the types of the elements in the subtuple. +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 @@ -109,11 +112,11 @@ tuple[i] ## 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 is faster than arrays due to the immutability of tuples. -Another use case is when you want to return multiple fixed values from a method. -This is particular helpful if the values have different types since each position in the tuple can have a different type. +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. -When to not use a Tuple is if a datastructure is needed that can grow or shrink in size or often needs to be modified. +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/tuples/introduction.md b/concepts/tuple/introduction.md similarity index 63% rename from concepts/tuples/introduction.md rename to concepts/tuple/introduction.md index 79718416..dacb9d5f 100644 --- a/concepts/tuples/introduction.md +++ b/concepts/tuple/introduction.md @@ -1,20 +1,22 @@ # 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 each position is. -The types used in a tuple can be different, but the types must be known at compile time. +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 interprited under compilation, the tuple can be created in different ways. -It is also important that the types of the values match the types specified in the tuple and that the number of values match the number of types specified. -If the values are known at compile time, the tuple can be created using the tuple literal syntax. +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 possibility to create a tuple using the `Tuple` class. +There is also the possibility to create a tuple using the `Tuple` class. ```crystal tuple = Tuple(Int32, String, Char).new(1, "foo", 'c') @@ -26,7 +28,7 @@ Alternatively, you can explicitly specify the type of the variable assigned to t tuple : Tuple(Int32, String, Char) = {1, "foo", 'c'} ``` -Explicitly specifying the type of the tuple can be usefull since that allows for defining that a position should hold a union type. +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 @@ -48,6 +50,7 @@ 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'} @@ -72,7 +75,7 @@ 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. -Meaning you will get a compile time error instead of a runtime error. +This means you will get a compile time error instead of a runtime error. ```crystal tuple = {1, "foo", 'c'} @@ -80,13 +83,13 @@ 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 as such give a runtime error. +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, since otherwise the compiler will not be able to know the types of the elements in the subtuple. +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 @@ -100,11 +103,11 @@ tuple[i] ## 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 is faster than arrays due to the immutability of tuples. -Another use case is when you want to return multiple fixed values from a method. -This is particular helpful if the values have different types since each position in the tuple can have a different type. +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. -When to not use a Tuple is if a datastructure is needed that can grow or shrink in size or often needs to be modified. +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/tuples/links.json b/concepts/tuple/links.json similarity index 100% rename from concepts/tuples/links.json rename to concepts/tuple/links.json diff --git a/concepts/tuples/.meta/config.json b/concepts/tuples/.meta/config.json deleted file mode 100644 index dfc9ef8c..00000000 --- a/concepts/tuples/.meta/config.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "blurb": "Tuples is a data structure which stores a fixed amount of elements. Tuples are indexable and inmutable", - "authors": ["meatball133"], - "contributors": [] -} diff --git a/concepts/tuples/.meta/template.md b/concepts/tuples/.meta/template.md deleted file mode 100644 index 90614142..00000000 --- a/concepts/tuples/.meta/template.md +++ /dev/null @@ -1 +0,0 @@ -{% $tuples \ $"tuples"."Tuples definition shorthand" %} \ No newline at end of file diff --git a/config.json b/config.json index ca6b902f..b00f1044 100644 --- a/config.json +++ b/config.json @@ -292,11 +292,11 @@ }, { "slug": "kitchen-calculator", - "name": "Kitcen Calculator", + "name": "Kitchen Calculator", "uuid": "e7250ff1-967d-4b5d-8fa8-a57fdbbe24c5", "concepts": [ "symbol", - "tuples" + "tuple" ], "prerequisites": [ "iteration" @@ -1593,8 +1593,8 @@ }, { "uuid": "a36e93cb-5ab0-4693-a6ab-ce698a3550ce", - "slug": "tuples", - "name": "Tuples" + "slug": "tuple", + "name": "Tuple" }, { "uuid": "268c4865-1a15-4f65-88d9-5a1bb1ba5ac4", diff --git a/exercises/concept/kitchen-calculator/.docs/hints.md b/exercises/concept/kitchen-calculator/.docs/hints.md index 42e626ac..c05ed978 100644 --- a/exercises/concept/kitchen-calculator/.docs/hints.md +++ b/exercises/concept/kitchen-calculator/.docs/hints.md @@ -2,8 +2,8 @@ ## General -- [Tuples][tuple] are data structures which are inmutable, have a fixed size, and can hold any data-type. -- Symbols may be used to denote finite states, as this exercise uses `:cup`, `:fluid_ounce`, `:teaspoon`, `:tablespoon`, `:milliliter` to denote the units used. +- [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 diff --git a/exercises/concept/kitchen-calculator/.docs/instructions.md b/exercises/concept/kitchen-calculator/.docs/instructions.md index dc0219f8..0dc29650 100644 --- a/exercises/concept/kitchen-calculator/.docs/instructions.md +++ b/exercises/concept/kitchen-calculator/.docs/instructions.md @@ -1,7 +1,7 @@ # 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. -Being only 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. +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: @@ -53,4 +53,4 @@ Given a volume-pair tuple and the desired unit, it should convert the given volu ```crystal KitchenCalculator.convert({:teaspoon, 9.0}, :tablespoon) # => {:tablespoon, 3.0} -``` \ No newline at end of file +``` diff --git a/exercises/concept/kitchen-calculator/.docs/introduction.md b/exercises/concept/kitchen-calculator/.docs/introduction.md index 8be8817f..e700ee82 100644 --- a/exercises/concept/kitchen-calculator/.docs/introduction.md +++ b/exercises/concept/kitchen-calculator/.docs/introduction.md @@ -1,20 +1,22 @@ # 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 each position is. -The types used in a tuple can be different, but the types must be known at compile time. +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 interprited under compilation, the tuple can be created in different ways. -It is also important that the types of the values match the types specified in the tuple and that the number of values match the number of types specified. -If the values are known at compile time, the tuple can be created using the tuple literal syntax. +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 possibility to create a tuple using the `Tuple` class. +There is also the possibility to create a tuple using the `Tuple` class. ```crystal tuple = Tuple(Int32, String, Char).new(1, "foo", 'c') @@ -26,7 +28,7 @@ Alternatively, you can explicitly specify the type of the variable assigned to t tuple : Tuple(Int32, String, Char) = {1, "foo", 'c'} ``` -Explicitly specifying the type of the tuple can be usefull since that allows for defining that a position should hold a union type. +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 @@ -48,6 +50,7 @@ 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'} @@ -72,7 +75,7 @@ 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. -Meaning you will get a compile time error instead of a runtime error. +This means you will get a compile time error instead of a runtime error. ```crystal tuple = {1, "foo", 'c'} @@ -80,13 +83,13 @@ 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 as such give a runtime error. +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, since otherwise the compiler will not be able to know the types of the elements in the subtuple. +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 @@ -100,12 +103,12 @@ tuple[i] ## 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 is faster than arrays due to the immutability of tuples. -Another use case is when you want to return multiple fixed values from a method. -This is particular helpful if the values have different types since each position in the tuple can have a different type. +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. -When to not use a Tuple is if a datastructure is needed that can grow or shrink in size or often needs to be modified. +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 @@ -118,19 +121,19 @@ They also allow for being written with quotes, e.g. `:"foo"`, which allows, for :"foo boo" # => :"foo boo" ``` -Symbols are used in many places in the language, including as keys in namedtuples, to represent method names and variable names. +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 is quite different from Ruby. -In Crystal is a symbol a form of constants and is thereby is assigned at compile time. +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 is represented as an `Int32` which makes very efficient. +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. +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 @@ -143,7 +146,7 @@ This means that two symbols with the same name are always the same object. ## 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. +This is because symbols are created at compile time and strings are created at runtime. ```crystal :foo.to_s # => "foo" diff --git a/exercises/concept/kitchen-calculator/.meta/config.json b/exercises/concept/kitchen-calculator/.meta/config.json index e554343f..a80ea50c 100644 --- a/exercises/concept/kitchen-calculator/.meta/config.json +++ b/exercises/concept/kitchen-calculator/.meta/config.json @@ -1,6 +1,6 @@ { "authors": ["meatball133"], - "contributors": [], + "contributors": ["ryanplusplus"], "files": { "solution": ["src/kitchen_calculator.cr"], "test": ["spec/kitchen_calculator_spec.cr"], diff --git a/exercises/concept/kitchen-calculator/.meta/design.md b/exercises/concept/kitchen-calculator/.meta/design.md index 248dd919..a7f2728a 100644 --- a/exercises/concept/kitchen-calculator/.meta/design.md +++ b/exercises/concept/kitchen-calculator/.meta/design.md @@ -1,23 +1,30 @@ ## Goal -The goal of this exercise is to teach the student about modules in Crystal. +The goal of this exercise is to teach the student about symbols and tuples in Crystal. ## Learning objectives -- Know how to create a module -- How to include a module in a class +- 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 -- How to use mixins between classes +- Named tuples ## Concepts -`Modules`: +`Tuples`: -- Create a module -- Include a module in a class +- Create a Tuple +- Index tuples + +`Symbol`: + +- Create symbol +- Compare symbol ## Prerequisites -- return +- Iteration diff --git a/exercises/concept/kitchen-calculator/.meta/template.md b/exercises/concept/kitchen-calculator/.meta/template.md index adbaca7f..78c597a9 100644 --- a/exercises/concept/kitchen-calculator/.meta/template.md +++ b/exercises/concept/kitchen-calculator/.meta/template.md @@ -1,2 +1,2 @@ -{% $tuples \ $"tuples"."Tuples definition shorthand" %} +{% $tuple \ $"tuple"."Tuples definition shorthand" %} {% $symbol %} \ No newline at end of file