From 86e78269e2477a410d7980b509f08abd20f51401 Mon Sep 17 00:00:00 2001 From: "bartek.binda.misc" Date: Mon, 10 Jul 2023 18:41:23 +0200 Subject: [PATCH] feature(C02_P05): add recursive solution to sum lists with improved tests --- chapter_02/p05_sum_lists.py | 130 +++++++++++++++++++----------------- tox.ini | 1 + 2 files changed, 70 insertions(+), 61 deletions(-) diff --git a/chapter_02/p05_sum_lists.py b/chapter_02/p05_sum_lists.py index 53201b40..eeb50df1 100644 --- a/chapter_02/p05_sum_lists.py +++ b/chapter_02/p05_sum_lists.py @@ -1,3 +1,5 @@ +import pytest + from chapter_02.linked_list import LinkedList @@ -23,34 +25,40 @@ def sum_lists(ll_a, ll_b): return ll -# this solution does not pass tests -# def sum_lists_followup(ll_a, ll_b): -# # Pad the shorter list with zeros -# if len(ll_a) < len(ll_b): -# for i in range(len(ll_b) - len(ll_a)): -# ll_a.add_to_beginning(0) -# else: -# for i in range(len(ll_a) - len(ll_b)): -# ll_b.add_to_beginning(0) -# -# # Find sum -# n1, n2 = ll_a.head, ll_b.head -# result = 0 -# while n1 and n2: -# result = (result * 10) + n1.value + n2.value -# n1 = n1.next -# n2 = n2.next -# -# # Create new linked list -# return NumericLinkedList([int(i) for i in str(result)]) +def sum_lists_recursive(ll_a, ll_b) -> "NumericLinkedList": + def sum_lists_helper(ll1_head, ll2_head, remainder, summed_list): + if ll1_head is None and ll2_head is None: + if remainder != 0: + summed_list.add(remainder) + return summed_list + elif ll1_head is None: + result = ll2_head.value + remainder + summed_list.add(result % 10) + return sum_lists_helper(ll1_head, ll2_head.next, result // 10, summed_list) + elif ll2_head is None: + result = ll1_head.value + remainder + summed_list.add(result % 10) + return sum_lists_helper(ll1_head.next, ll2_head, result // 10, summed_list) + else: + result = ll1_head.value + ll2_head.value + remainder + summed_list.add(result % 10) + return sum_lists_helper( + ll1_head.next, ll2_head.next, result // 10, summed_list + ) + + return sum_lists_helper(ll_a.head, ll_b.head, 0, NumericLinkedList()) class NumericLinkedList(LinkedList): - @classmethod - def generate_from_integer(cls, integer): - integer_parts = [int(c) for c in str(integer)] - integer_parts.reverse() - return cls(integer_parts) + def __init__(self, values=None): + """handle integer as input""" + if isinstance(values, int): + values = [int(c) for c in str(values)] + values.reverse() + elif isinstance(values, list): + values = values.copy() + + super().__init__(values) def numeric_value(self): number = 0 @@ -59,47 +67,47 @@ def numeric_value(self): return number -test_cases = ( - # all values can either be list of integer or integers - # a, b, expected_sum - ([7, 1, 6], [5, 9, 2], [2, 1, 9]), - (0, 0, 0), - ([], [], 0), - ([3, 2, 1], [3, 2, 1], [6, 4, 2]), - (123, 123, 246), - (123, 1, 124), - (1, 123, 124), -) +def test_numeric_linked_list(): + ll = NumericLinkedList(321) + assert ll.numeric_value() == 321 + assert ll.values() == [1, 2, 3] -testable_functions = ( - sum_lists, - # sum_lists_followup -) +testable_functions = (sum_lists, sum_lists_recursive) -def test_numeric_linked_list(): - ll = NumericLinkedList.generate_from_integer(321) - assert ll.numeric_value() == 321 + +@pytest.fixture(params=testable_functions) +def linked_list_summing_function(request): + return request.param + + +test_cases = ( + # inputs can either be list of integer or integers + # a, b, expected_sum + pytest.param([1], [2], [3], id="single_digit"), + pytest.param([0], [0], [0], id="single_digit_zero"), + pytest.param([], [], [], id="empty"), + pytest.param([7, 1, 6], [5, 9, 2], [2, 1, 9], id="3-digit equal length A"), + pytest.param([3, 2, 1], [3, 2, 1], [6, 4, 2], id="3-digit equal length B"), + pytest.param(123, 1, [4, 2, 1], id="3-digit and single digit"), + pytest.param([9, 9, 9], [1], [0, 0, 0, 1], id="carry end"), + pytest.param([9, 9, 9], [9, 9, 9], [8, 9, 9, 1], id="multiple carry"), +) -def test_linked_list_addition(): - for f in testable_functions: - for a, b, expected in test_cases: - print(f"{f.__name__}: {a}, {b}, {expected}") - if isinstance(a, int): - ll_a = NumericLinkedList.generate_from_integer(a) - else: - ll_a = NumericLinkedList(a.copy()) +@pytest.mark.parametrize("a, b, expected", test_cases) +def test_linked_list_addition(linked_list_summing_function, a, b, expected): + ll_a = NumericLinkedList(a) + ll_b = NumericLinkedList(b) + ll_result = linked_list_summing_function(ll_a, ll_b) + assert ll_result.values() == expected + assert ( + ll_a.numeric_value() + ll_b.numeric_value() + == NumericLinkedList(expected).numeric_value() + ) - if isinstance(b, int): - ll_b = NumericLinkedList.generate_from_integer(b) - else: - ll_b = NumericLinkedList(b.copy()) - result = f(ll_a, ll_b) - if isinstance(expected, int): - assert result.numeric_value() == expected - else: - assert result.values() == expected + ll_result_reverse = linked_list_summing_function(ll_b, ll_a) + assert ll_result_reverse.values() == expected def example(): @@ -108,8 +116,8 @@ def example(): print(ll_a) print(ll_b) print(sum_lists(ll_a, ll_b)) - # print(sum_lists_followup(ll_a, ll_b)) if __name__ == "__main__": example() + pytest.main(args=[__file__]) diff --git a/tox.ini b/tox.ini index 2b361d0e..e5713949 100644 --- a/tox.ini +++ b/tox.ini @@ -10,3 +10,4 @@ ignore = C0102,C0111,C0203,C0301,C0302,C0325,C0330,C0412,C0413,C901,E0101,E0202, [flake8] max-line-length = 88 extend-ignore = E203,E800,VNE001,VNE002 +pytest-parametrize-names-type = csv \ No newline at end of file