ValueError: not enough values to unpack (expected 2, got 0)

Hi all,

trying to write this function that takes a number and counts how many steps it takes to get to 1, with a few extra rules in between. Can only divide and multiply by 2 or 3. The leading digit has to be a 1,3,4 or 9. I’m encountering the value error that is the title. here is the code with the error message :
import math

from collections import deque
def SteppingStones(start, leading, factors, max_steps):
    
    '''start: integer
    leading: iterable of allowed leading digits : (1,3,4,9)
    factors: iterable of exactly two integers : (2,3)
    max_steps: guaranteed maximum number of max steps to get to 1'''
    
    assert isinstance(start,int)
    assert start >= 1
    assert all(isinstance(l,int) for l in leading)
    assert all(isinstance(F,int) for F in factors)
    assert isinstance(max_steps,int)
    if start == 1 :
        return 0, []
    leading = {str(d) for d in leading}
    factors = list(factors)
    def valid(n):
        return n > 0 and str(n)[0] in leading
    queue = deque([(start, 0)])
    visited = {start}
    parent = {start: None}
    operation = {}
    while queue :
        value, steps = queue.popleft()
        if steps >= max_steps:
            continue
        for f in factors:
            for next_value, op in (
                (value * f, f"*{f}"),
                (value // f, f"/{f}") if value % f == 0 else ()):
            
                if next_value == 1:
                    parent[1] = value
                    operation[1] = op
                    queue.clear()
                    break
                if next_value not in visited and valid(next_value):
                    visited.add(next_value)
                    parent[next_value] = value
                    operation[next_value] = op
                    queue.append((next_value, steps + 1))
            else:
                continue
            break
    else:
        raise ValueError("No solution found within max_steps")
    # Reconstruct path
    path = []
    current = 1
    while parent[current] is not None:
        prev = parent[current]
        path.append((prev, operation[current], current))
        current = prev
    path.reverse()
    steps = len(path)
    assert steps <= max_steps
    return steps, path 
def doTests(): 
    
    [steps, _ ] = SteppingStones(12, leading = {1, 3, 4, 9}, factors = {2, 3}, max_steps = 10)
    [steps_2, _ ] = SteppingStones(9, leading = {1, 3, 4, 9}, factors = {2, 3}, max_steps = 10)
    [steps_3, _ ] = SteppingStones(6, leading = {1, 3, 4, 9}, factors = {2, 3}, max_steps = 10)
    [steps_4, _ ] = SteppingStones(2, leading = {1, 3, 4, 9}, factors = {2, 3}, max_steps = 10)
    [steps_5, _ ] = SteppingStones(1, leading = {1, 3, 4, 9}, factors = {2, 3}, max_steps = 10)
    
    assert steps == 5
    assert steps_2 == 2
    assert steps_3 == 2
    assert steps_4 == 1
    assert steps_5 == 0 # Ensure expected value for steps is given from some known start values.
    
    return True
#doTests()
SteppingStones(1536, leading={1, 3, 4, 9}, factors={2, 3}, max_steps = 40)

---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[20], line 90
87 return True
89#doTests()
---> 90 SteppingStones(1536, leading={1, 3, 4, 9}, factors={2, 3}, max_steps = 40)
Cell In[20], line 38, in SteppingStones(start, leading, factors, max_steps)
35 continue
37forfin factors:
---> 38 fornext_value, opin (
39 (value * f, f"*{f}"),
40 (value // f, f"/{f}") ifvalue % f == 0else ()):
42 if next_value == 1:
43 parent[1] = value
ValueError: not enough values to unpack (expected 2, got 0)

The problem is in this part of the code. Let’s take the case of value == 2, f == 2

            for next_value, op in (
                (value * f, f"*{f}"),
                (value // f, f"/{f}") if value % f == 0 else ()):

If we convert this we end up with:

((4, "*2"), (,))

So the first time through the loop we get:

next_value == 4
op == "*2"

The second time through the loop we have an empty tuple object. Since it’s empty, there is nothing to unpack. That’s what’s causing the error.

You can rewrite this to use a Generator function rather than a comprehension like this:

def get_next_value_and_op(f, value):
    yield value * f, f'*{f}'
    if value % f == 0:
        yield value // f, f'/{f}'

When you have that your loop becomes:

for next_value, op in get_next_value_and_op(f, value):
    <your logic here>

By using a Generator function you can better handle the division case which you only want to do if f is a factor of value by simply not yielding it.

I hope this helps.

that does seem like a much better way thank you, but im now getting ValueError: No solution found within max_steps.

That looks more like a logic error. I’m assuming this is homework and I’d rather not get too into helping you debug the logic for it. (Which is different than helping you debug something somewhat nuanced in the Python language.)

yeah fair enough, thanks for the help

1 Like