3.5. Match Wildcard

The wildcard pattern is a single underscore: _. It always matches, but does not capture any variable (which prevents interference with other uses for _ and allows for some optimizations).

>>> user = 'Mark'
>>>
>>> match user:
...     case 'Mark':        print('Hello Mark')
...     case 'Melissa':     print('Hello Melissa')
...     case 'Rick':        print('Hello Rick')
...     case 'Alex':        print('Hello Alex')
...     case 'Beth':        print('Hello Beth')
...     case 'Chris':       print('Hello Chris')
...     case _:             raise PermissionError
...
Hello Mark

3.5.1. Use Case - 0x01

>>> def html_color(name):
...     match name:
...         case 'red':   return '#ff0000'
...         case 'green': return '#00ff00'
...         case 'blue':  return '#0000ff'
...         case _:       raise NotImplementedError('Unknown color')
>>> html_color('black')
Traceback (most recent call last):
NotImplementedError: Unknown color
>>> html_color('orange')
Traceback (most recent call last):
NotImplementedError: Unknown color

3.5.2. Use Case - 0x02

>>> weekday = 0
>>>
>>> match weekday:
...     case 1: print('Monday')
...     case 2: print('Tuesday')
...     case 3: print('Wednesday')
...     case 4: print('Thursday')
...     case 5: print('Friday')
...     case 6: print('Saturday')
...     case 7: print('Sunday')
...     case _: raise ValueError('Invalid weekday')  # wildcard pattern
...
Traceback (most recent call last):
ValueError: Invalid weekday

3.5.3. Assignments

Code 3.24. Solution
"""
* Assignment: Match Wildcard Range
* Complexity: medium
* Lines of code: 18 lines
* Time: 8 min

English:
    1. Write own implementation of a built-in function `range()`
    2. Note, that function does not take any keyword arguments
    3. How to implement passing only stop argument
       `myrange(start=0, stop=???, step=1)`?
    4. Use literal pattern and wildcard pattern
    5. Run doctests - all must succeed

Polish:
    1. Zaimplementuj własne rozwiązanie wbudowanej funkcji `range()`
    2. Zauważ, że funkcja nie przyjmuje żanych argumentów nazwanych (keyword)
    3. Jak zaimplementować możliwość podawania tylko końca
       `myrange(start=0, stop=???, step=1)`?
    4. Użyj literal pattern i wildcard pattern
    5. Uruchom doctesty - wszystkie muszą się powieść

Hint:
    * https://github.com/python/cpython/blob/main/Objects/rangeobject.c#LC75
    * match len(args)
    * case _
    * `raise TypeError('error message')`

Tests:
    >>> import sys; sys.tracebacklimit = 0
    >>> from inspect import isfunction

    >>> assert isfunction(myrange)

    >>> myrange(0, 10, 2)
    [0, 2, 4, 6, 8]

    >>> myrange(0, 5)
    [0, 1, 2, 3, 4]

    >>> myrange(5)
    [0, 1, 2, 3, 4]

    >>> myrange()
    Traceback (most recent call last):
    TypeError: myrange expected at least 1 argument, got 0

    >>> myrange(1,2,3,4)
    Traceback (most recent call last):
    TypeError: myrange expected at most 3 arguments, got 4

    >>> myrange(stop=2)
    Traceback (most recent call last):
    TypeError: myrange() takes no keyword arguments

    >>> myrange(start=1, stop=2)
    Traceback (most recent call last):
    TypeError: myrange() takes no keyword arguments

    >>> myrange(start=1, stop=2, step=2)
    Traceback (most recent call last):
    TypeError: myrange() takes no keyword arguments
"""


# myrange(start=0, stop=???, step=1)
# note, function does not take keyword arguments
# type: Callable[[int,int,int], list[int]]
def myrange(*args, **kwargs):
    if kwargs:
        raise TypeError('myrange() takes no keyword arguments')

    start = ...
    stop = ...
    step = ...

    current = start
    result = []

    while current < stop:
        result.append(current)
        current += step

    return result