-
Notifications
You must be signed in to change notification settings - Fork 0
Added functions Curry and Uncurry #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| def curry(func, arity): | ||
| """ | ||
| Каррирование функции с заданной арностью. | ||
|
|
||
| Args: func - исходная функция, | ||
| arity - арность функции (кол-во ожид. аргументов). | ||
|
|
||
| return: рекурсивно с пустого кортежа собираем все аргументы | ||
| и вызываем для них исходную функцию. Её результат возвращаем. | ||
|
|
||
| raises: ValueError - если арность отрицательная. | ||
| """ | ||
|
|
||
| actual_argcount = func.__code__.co_argcount | ||
|
|
||
| if arity > actual_argcount: | ||
| raise ValueError(f"Арность {arity} превышает количество параметров функции") | ||
|
|
||
| if arity < 0: | ||
| raise ValueError("Арность не должна быть <0") | ||
|
|
||
| def curried(args_received): | ||
| """ | ||
| Рекурсивная функция, которая копит аргументы. | ||
|
|
||
| args_received: type - tuple, уже полученные аргументы | ||
|
|
||
| return: если все аргументы получены - результат данной функции, | ||
| иначе - новая функция, ждущая след. аргумент. | ||
| """ | ||
| if len(args_received) == arity: | ||
| return func(*args_received) | ||
|
|
||
| def next_arg(arg): | ||
| """ | ||
| Функция, в которой мы получаем новый аргумент | ||
| и добавлем его к кортежу полученных аргументов. | ||
|
|
||
| return: curried(аргументы + новый) и проверяем, | ||
| достаточно ли аргументов для подсчета результата. | ||
| """ | ||
| new_args = args_received + (arg,) | ||
| return curried(new_args) | ||
|
|
||
| return next_arg | ||
|
|
||
| if arity == 0: | ||
| return lambda: func() | ||
|
|
||
| return curried(()) | ||
|
|
||
|
|
||
| def uncurry(curried_func, arity): | ||
| """ | ||
| Функция декаррирования каррированной функции с заданной арностью. | ||
|
|
||
| Args: curried_func - каррированная функция, | ||
| arity - арность функции. | ||
|
|
||
| return: внутренняя декаррирующая функция, принимающая все аргументы в виде кортежа. | ||
|
|
||
| raise: ValueError - если передана отрицательная арность. | ||
| """ | ||
| if arity < 0: | ||
| raise ValueError("Арность не должна быть <0") | ||
|
|
||
| def uncurried(*args): | ||
| """ | ||
| Функция, принимающая все аргументы сразу. | ||
|
|
||
| *args - все аргументы функции. | ||
|
|
||
| return: последовательно применяем все аргументы к каррированной | ||
| функции и возвращаем результат. | ||
|
|
||
| raise: TypeError при неверном кол-ве аргументов. | ||
| """ | ||
| if len(args) != arity: | ||
| raise TypeError(f"Функция ожидает {arity} аргументов, получено {len(args)}") | ||
|
|
||
| result = curried_func | ||
| for arg in args: | ||
| result = result(arg) | ||
|
|
||
| if arity == 0: | ||
| return result() | ||
|
|
||
| return result | ||
|
|
||
| return uncurried | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,177 @@ | ||
| import pytest | ||
| from hypothesis import given | ||
| from hypothesis import strategies as st | ||
|
|
||
| from functions_curry_7.curry_uncurry import curry, uncurry | ||
|
|
||
| # unit тесты | ||
|
|
||
|
|
||
| def test_curry_basic(): | ||
| """Тест каррирования функции с 3 аргументами.""" | ||
|
|
||
| def sum_3(a, b, c): | ||
| return a + b + c | ||
|
|
||
| curried = curry(sum_3, 3) | ||
| result = curried(1)(2)(3) | ||
| assert result == 6, f"Ожидалось 6, получено {result}" | ||
|
|
||
|
|
||
| def test_curry_partial_application(): | ||
| """Тест частичного применения каррированной функции.""" | ||
|
|
||
| def multiply(a, b): | ||
| return a * b | ||
|
|
||
| curried = curry(multiply, 2) | ||
| double = curried(2) | ||
|
|
||
| assert double(5) == 10 | ||
| assert double(6) == 12 | ||
| assert callable(double), "Частично примененная функция должна быть вызываемой" | ||
|
|
||
|
|
||
| def test_uncurry_basic(): | ||
| """Тест обратимости""" | ||
|
|
||
| def add_4(a, b, c, d): | ||
| return a + b + c + d | ||
|
|
||
| curried = curry(add_4, 4) | ||
| uncurried = uncurry(curried, 4) | ||
|
|
||
| assert curried(1)(2)(3)(4) == 10 | ||
| assert uncurried(1, 2, 3, 4) == 10 | ||
|
|
||
|
|
||
| def test_zero_arity(): | ||
| """Тестирование функций с нулевой арностью""" | ||
|
|
||
| def constant(): | ||
| return 42 | ||
|
|
||
| curried_const = curry(constant, 0) | ||
| assert curried_const() == 42 | ||
|
|
||
| uncurried_const = uncurry(curried_const, 0) | ||
| assert uncurried_const() == 42 | ||
|
|
||
|
|
||
| def test_single_argument(): | ||
| """Тестирование функций с одним аргументом""" | ||
|
|
||
| def identity(x): | ||
| return x | ||
|
|
||
| curried_id = curry(identity, 1) | ||
| assert curried_id(5) == 5 | ||
|
|
||
| uncurried_id = uncurry(curried_id, 1) | ||
| assert uncurried_id(10) == 10 | ||
|
|
||
|
|
||
| # тесты ошибок | ||
|
|
||
|
|
||
| def test_curry_negative_arity(): | ||
| """Тест ошибки при отрицательной арности в curry.""" | ||
|
|
||
| def func(a): | ||
| return a | ||
|
|
||
| with pytest.raises(ValueError, match="Арность не должна быть <0"): | ||
| curry(func, -1) | ||
|
|
||
| with pytest.raises(ValueError, match="Арность не должна быть <0"): | ||
| curry(func, -100) | ||
|
|
||
|
|
||
| def test_uncurry_negative_arity(): | ||
| """Тест ошибки при отрицательной арности в uncurry.""" | ||
|
|
||
| def func(a): | ||
| return a | ||
|
|
||
| curried = curry(func, 1) | ||
|
|
||
| with pytest.raises(ValueError, match="Арность не должна быть <0"): | ||
| uncurry(curried, -1) | ||
|
|
||
|
|
||
| def test_uncurry_wrong_number_of_args(): | ||
| """Тест ошибки при передаче неправильного количества аргументов в uncurry.""" | ||
|
|
||
| def sum_3(a, b, c): | ||
| return a + b + c | ||
|
|
||
| curried = curry(sum_3, 3) | ||
| uncurried = uncurry(curried, 3) | ||
|
|
||
| with pytest.raises(TypeError, match="Функция ожидает 3 аргументов"): | ||
| uncurried(1, 2) | ||
|
|
||
| with pytest.raises(TypeError, match="Функция ожидает 3 аргументов"): | ||
| uncurried(1, 2, 3, 4) | ||
|
|
||
|
|
||
| # Property-based тесты с hypothesis | ||
|
|
||
| integers = st.integers(min_value=-100, max_value=100) | ||
| small_lists = st.lists(integers, min_size=0, max_size=5) | ||
|
|
||
|
|
||
| @given(a=integers, b=integers, c=integers) | ||
| def test_curry_uncurry_inverse(a: int, b: int, c: int): | ||
| """ | ||
| Property-based тест: curry и uncurry обратны друг другу. | ||
| Для любых a, b, c результаты должны совпадать. | ||
| """ | ||
|
|
||
| def sum_3(x, y, z): | ||
| return x + y + z | ||
|
|
||
| curried = curry(sum_3, 3) | ||
| uncurried = uncurry(curried, 3) | ||
|
|
||
| assert curried(a)(b)(c) == uncurried(a, b, c) | ||
| assert curried(a)(b)(c) == a + b + c | ||
|
|
||
|
|
||
| @given(args=st.tuples(integers, integers, integers)) | ||
| def test_curry_preserves_behavior(args): | ||
| """ | ||
| Property-based тест: каррированная функция ведет себя так же, | ||
| как исходная. | ||
| """ | ||
|
|
||
| def sum_3(x, y, z): | ||
| return x + y + z | ||
|
|
||
| curried = curry(sum_3, 3) | ||
| a, b, c = args | ||
|
|
||
| direct_result = sum_3(a, b, c) | ||
| curried_result = curried(a)(b)(c) | ||
|
|
||
| assert direct_result == curried_result | ||
|
|
||
|
|
||
| @given(x=integers, y=integers, z=integers, w=integers) | ||
| def test_partial_application_property(x, y, z, w): | ||
| """ | ||
| Property-based тест: частичное применение работает корректно. | ||
| """ | ||
|
|
||
| def add4(a, b, c, d): | ||
| return a + b + c + d | ||
|
|
||
| curried_add4 = curry(add4, 4) | ||
| partially_applied = curried_add4(x)(y) | ||
|
|
||
| assert callable(partially_applied) | ||
|
|
||
| result = partially_applied(z)(w) | ||
|
|
||
| assert result == x + y + z + w | ||
| assert result == add4(x, y, z, w) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Не проверяется случай, когда пользователь указывает арность не соответствующую реальной.
Мы можем написать
f_curry = curry(sum3, 100), ошибка появится только при вызове функцииf_curry.