diff --git a/concepts/binary-octal-hexadecimal/about.md b/concepts/binary-octal-hexadecimal/about.md index a7fca3714e3..67646aed2f2 100644 --- a/concepts/binary-octal-hexadecimal/about.md +++ b/concepts/binary-octal-hexadecimal/about.md @@ -18,7 +18,7 @@ A snippet from the base 2 system looks like this, although it continues infinite | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | | 2 \*\* 7 | 2 \*\* 6 | 2 \*\* 5 | 2 \*\* 4 | 2 \*\* 3 | 2 \*\* 2 | 2 \*\* 1 | 2 \*\* 0 | -So if we want to represent the number 6, it would in binary be: 110 +So if we want to represent the number 6 in binary, it would be 110. | Place value | 4 | 2 | 1 | | ------------- | --- | --- | --- | @@ -41,7 +41,6 @@ In Python, we can represent binary literals using the `0b` prefix. If we write `0b10011`, Python will interpret it as a binary number and convert it to base 10. ```python -# 0b10011 >>> 0b10011 19 @@ -86,6 +85,8 @@ However, the usual mathematical operator rules apply: dividing two binary numbe >>> 0b10011/3 6.333333333333333 +``` + ### Converting to and from Binary Representation @@ -133,6 +134,9 @@ For example, `bit_count()` on '0b11011' will return 4: ```python >>> 0b11011.bit_count() 4 +``` + + ~~~~exercism/note If you are working locally, `bit_count()` requires at least Python 3.10. The Exercism online editor currently supports all features through Python 3.11. @@ -148,7 +152,6 @@ In Python, we can represent octal numbers using the `0o` prefix. As with binary, Python automatically converts an octal representation to an `int`. ```python -# 0o123 >>> 0o123 83 ``` @@ -157,7 +160,6 @@ As with binary, octal numbers **are ints** and support all integer operations. Prefixing a number with `0o` that is not in the octal system will raise a `SyntaxError`. ### Converting to and from Octal Representation - To convert an `int` into an octal representation, you can use the built-in [`oct()`][oct] function. This acts similarly to the `bin()` function, returning a string: @@ -165,6 +167,8 @@ This acts similarly to the `bin()` function, returning a string: ```python >>> oct(83) '0o123' +``` + To convert an octal number to an integer, we can use the `int()` function, passing an octal string representation and the base (8) as arguments: @@ -175,22 +179,21 @@ To convert an octal number to an integer, we can use the `int()` function, passi As with binary, giving the wrong base will raise a `ValueError`. -### Hexadecimal +## Hexadecimal [Hexadecimal][hexadecimal] is a base 16 numeral system. It uses the digits 0 - 9 and the letters A, B, C, D, E, and F. A is 10, B is 11, C is 12, D is 13, E is 14, and F is 15. We can represent hexadecimal numbers in Python using the `0x` prefix. -As with binary and octal, Python will automatically convert hexadecimal literals to `int`. +As with binary and octal, Python will automatically convert hexadecimal literals to `int`s. ```python -# 0x123 >>> 0x123 291 ``` -As with binary and octal - hexadecimal literals **are ints**, and you can perform all integer operations. +As with binary and octal — hexadecimal literals **are ints**, and you can perform all integer operations with them. Prefixing a non-hexadecimal number with `0x` will raise a `SyntaxError`. @@ -202,6 +205,8 @@ This acts similarly to the `bin()` function, returning a string: ```python >>> hex(291) '0x123' +``` + To convert a hexadecimal representation to an integer, we can use the `int()` function, passing a hexadecimal string with the base (16) as arguments: diff --git a/concepts/binary-octal-hexadecimal/introduction.md b/concepts/binary-octal-hexadecimal/introduction.md index a06ac922faf..84ff634263d 100644 --- a/concepts/binary-octal-hexadecimal/introduction.md +++ b/concepts/binary-octal-hexadecimal/introduction.md @@ -1,4 +1,4 @@ -# binary, octal, hexadecimal +# Binary, Octal, Hexadecimal Binary, octal, and hexadecimal (_also known as hex_) are different [numeral systems][numeral-systems] with different bases. Binary is base 2, octal is base 8, and hexadecimal is base 16. diff --git a/concepts/generators/about.md b/concepts/generators/about.md index 59b5035d6b9..4b2e74cbad2 100644 --- a/concepts/generators/about.md +++ b/concepts/generators/about.md @@ -35,9 +35,33 @@ The rationale behind this is that you use a generator when you do not need all t This saves memory and processing power, since only the value you are _currently working on_ is calculated. + ## Using a generator -Generators may be used in place of most `iterables` in Python. This includes _functions_ or _objects_ that require an `iterable`/`iterator` as an argument. +Generators (_technically [`generator-iterator`s][generator-iterator] — see the note below._) are a type of `iterator` and can be used anywhere in Python where an `iterator` or `iterable` is expected. +This includes _functions_ or _objects_ that require an `iterable`/`iterator` as an argument. +For a deeper dive, see [How to Make an Iterator in Python][how-to-iterator]. + + +~~~~exercism/note + +Generator-iterators are a special sub-set of [iterators][iterator]. +`Iterators` are the mechanism/protocol that enables looping over _iterables_. +Generator-iterators and the iterators returned by common Python [`iterables`][iterables] act very similarly, but there are some important differences to note: + +- They are _[lazily evaluated][lazy evaluation]_; iteration is _one-way_ and there is no "backing up" to a previous value. +- They are _consumed_ by iterating over the returned values; there is no resetting or saving in memory. +- They are not sortable and cannot be reversed. +- They are not sequence types, and _do not_ have `indexes`. + You cannot reference a previous or future value using addition or subtraction and you cannot use bracket (`[]`) notation or slicing. +- They cannot be used with the `len()` function, as they have no length. +- They can be _finite_ or _infinite_ - be careful when collecting all values from an _infinite_ `generator-iterator`! + +[iterator]: https://docs.python.org/3.11/glossary.html#term-iterator +[iterables]: https://wiki.python.org/moin/Iterator +[lazy evaluation]: https://en.wikipedia.org/wiki/Lazy_evaluation +~~~~ + To use the `squares_generator()` generator: @@ -140,7 +164,8 @@ Generators are also very helpful when a process or calculation is _complex_, _ex Now whenever `__next__()` is called on the `infinite_sequence` object, it will return the _previous number_ + 1. -[generator-iterator]: https://docs.python.org/3.11/glossary.html#term-generator-iterator +[generator-iterator]: https://docs.python.org/3/glossary.html#term-generator-iterator +[how-to-iterator]: https://treyhunner.com/2018/06/how-to-make-an-iterator-in-python/#Generators:_the_easy_way_to_make_an_iterator [iterables]: https://wiki.python.org/moin/Iterator [iterator]: https://docs.python.org/3.11/glossary.html#term-iterator [lazy iterator]: https://en.wikipedia.org/wiki/Lazy_evaluation diff --git a/concepts/unpacking-and-multiple-assignment/about.md b/concepts/unpacking-and-multiple-assignment/about.md index d4b9168ad13..b269b312adc 100644 --- a/concepts/unpacking-and-multiple-assignment/about.md +++ b/concepts/unpacking-and-multiple-assignment/about.md @@ -8,7 +8,7 @@ A very common example of this behavior is `for item in list`, where `item` takes This allows for code to be more concise and readable, and is done by separating the variables to be assigned with a comma such as `first, second, third = (1,2,3)` or `for index, item in enumerate(iterable)`. The special operators `*` and `**` are often used in unpacking contexts. -`*` can be used to combine multiple `lists`/`tuples` into one `list`/`tuple` by _unpacking_ each into a new common `list`/`tuple`. +`*` can be used to combine multiple `list`s/`tuple`s into one `list`/`tuple` by _unpacking_ each into a new common `list`/`tuple`. `**` can be used to combine multiple dictionaries into one dictionary by _unpacking_ each into a new common `dict`. When the `*` operator is used without a collection, it _packs_ a number of values into a `list`. @@ -73,7 +73,7 @@ Since `tuples` are immutable, you can't swap elements in a `tuple`. The examples below use `lists` but the same concepts apply to `tuples`. ~~~~ -In Python, it is possible to [unpack the elements of `list`/`tuple`/`dictionary`][unpacking] into distinct variables. +In Python, it is possible to [unpack the elements of a `list`/`tuple`/`dict`][unpacking] into distinct variables. Since values appear within `lists`/`tuples` in a specific order, they are unpacked into variables in the same order: ```python @@ -94,7 +94,7 @@ If there are values that are not needed then you can use `_` to flag them: ### Deep unpacking -Unpacking and assigning values from a `list`/`tuple` inside of a `list` or `tuple` (_also known as nested lists/tuples_), works in the same way a shallow unpacking does, but often needs qualifiers to clarify the values context or position: +Unpacking and assigning values from a `list`/`tuple` inside of a `list` or `tuple` (_also known as nested lists/tuples_), works in the same way a shallow unpacking does, but often needs qualifiers to clarify the value's context or position: ```python >>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]] diff --git a/concepts/unpacking-and-multiple-assignment/introduction.md b/concepts/unpacking-and-multiple-assignment/introduction.md index 59cab3b4ec3..c8878eb1800 100644 --- a/concepts/unpacking-and-multiple-assignment/introduction.md +++ b/concepts/unpacking-and-multiple-assignment/introduction.md @@ -8,7 +8,7 @@ A very common example of this behavior is `for item in list`, where `item` takes This allows for code to be more concise and readable, and is done by separating the variables to be assigned with a comma such as `first, second, third = (1,2,3)` or `for index, item in enumerate(iterable)`. The special operators `*` and `**` are often used in unpacking contexts. -`*` can be used to combine multiple `lists`/`tuples` into one `list`/`tuple` by _unpacking_ each into a new common `list`/`tuple`. +`*` can be used to combine multiple `list`s/`tuple`s into one `list`/`tuple` by _unpacking_ each into a new common `list`/`tuple`. `**` can be used to combine multiple dictionaries into one dictionary by _unpacking_ each into a new common `dict`. When the `*` operator is used without a collection, it _packs_ a number of values into a `list`. diff --git a/exercises/concept/plane-tickets/.docs/introduction.md b/exercises/concept/plane-tickets/.docs/introduction.md index d17f90c812c..ac0a53a8ef2 100644 --- a/exercises/concept/plane-tickets/.docs/introduction.md +++ b/exercises/concept/plane-tickets/.docs/introduction.md @@ -1,7 +1,7 @@ # Generators -A `generator` is a function or expression that returns a special type of [iterator][iterator] called [generator iterator][generator-iterator]. -`Generator-iterators` are [lazy][lazy iterator]: they do not store their `values` in memory, but _generate_ their values when needed. +A `generator` is a function or expression that returns a special type of [iterator][iterator] called a [`generator iterator`][generator-iterator]. +`Generator-iterator`s are [lazy][lazy iterator]: they do not store their `values` in memory, but _generate_ their values when needed. A generator function looks like any other function, but contains one or more [yield expressions][yield expression]. Each `yield` will suspend code execution, saving the current execution state (_including all local variables and try-statements_). @@ -144,8 +144,6 @@ Now whenever `__next__()` is called on the `infinite_sequence` object, it will r [generator-iterator]: https://docs.python.org/3.11/glossary.html#term-generator-iterator -[iterables]: https://wiki.python.org/moin/Iterator [iterator]: https://docs.python.org/3.11/glossary.html#term-iterator -[lazy evaluation]: https://en.wikipedia.org/wiki/Lazy_evaluation [lazy iterator]: https://en.wikipedia.org/wiki/Lazy_evaluation [yield expression]: https://docs.python.org/3.11/reference/expressions.html#yield-expressions