简体   繁体   中英

Python- IndexError: list index out of range, while in for loop

I'm new to python. I am trying to write a code to take input from text file like

6 6
* o o o o *
o o * o o o
o o * o o *
o o * o o o
o o o o * o
o o o o o o

and count the number of "*" near each string and update each string with the new count like:

6 6
* 2 1 1 1 *
1 3 * 2 2 2
0 3 * 3 1 *
0 2 * 2 2 2
0 1 1 2 * 1
0 0 0 1 1 1

And update this on an output.txt. Until now my code is taking input and providing rows, column, and matrix but as soon as I get into the list to count, it fails giving error

if matrix[num_rows][num_columns][1] == "x":

IndexError: list index out of range

My code snippet:

def parse_in(input_name):
    list_of_lists = []
    with open(input_name,"r") as f:
        for line in f:
            with open(input_name) as f:
                num_rows, num_columns = [int(x) for x in next(f).split()]

                lines = f.read().splitlines()
            # in alternative, if you need to use the file content as numbers
        matrix = []
        print(lines)
        for x in lines:
            matrix.append(x.split(' '))
        print(matrix)
    return matrix, num_rows, num_columns


def detector(matrix, num_rows, num_columns):
    mine_count = 0
    # For every every space around the square, including itself
    for r in range(num_rows):
        for c in range(num_columns):
            # If the square exist on the matrix
            if 0 <= num_rows + r <= 2 and 0 <= num_columns + c <= 2:
                # If the square contains a mine
                if matrix[r][c] == "*":
                    # Raise the mine count
                    mine_count = mine_count + 1
            # If the original square contains a mine
            if matrix[r][c] == "*":
                print(mine_count)
                # Lower the mine count by 1, because the square itself having a mine shouldn't be counted
                mine_count = mine_count - 1
                print(mine_count)
            return mine_count


def parse_out(output_name, my_solution):
    pass


def my_main(input_name, output_name):
    # 1. We do the parseIn from the input file
    lines, num_rows, num_columns = parse_in(input_name)

    # 2. We do the strategy to solve the problem
    my_solution = detector(lines, num_rows, num_columns)

    # 3. We do the parse out to the output file
    parse_out(output_name, my_solution)


if __name__ == '__main__':
    # 1. Name of input and output files
    input_name = "input_2.txt"
    output_name = "output.txt"

    # 2. Main function
    my_main(input_name, output_name)

When creating the matrix, you don't need two loops. You can build the matrix directly in the loop that reads the file. You also don't need to open the file multiple times.

def parse_in(input_name):
    matrix = []
    with open(input_name,"r") as f:
        num_rows, num_columns = [int(x) for x in next(f).split()]
        for line in f:
            matrix.append(line.split(' '))
    return matrix, num_rows, num_columns

You don't need to pass num_rows and num_columns to the detector() function. Unlike languages like C, Python knows the length of lists, so you can just loop over the list elements directly. And you can use enumerate() to get the indexes as you're looping.

When counting the number of mines next to a square, you just need to loop from r-1 to r+1 and from c-1 to c+1 . And you need to set mine_count to 0 before this loop.

def detector(matrix):
    result = []
    for r, row in enumerate(matrix):
        result_row = []
        for c, cell in enumerate(row):
            if cell == "*":
                result_row.append(cell)
            else:
                mine_count = 0
                for x in range(c-1, c+2):
                    for y in range(r-1, r+2):
                        if 0 <= x < len(row) and 0 <= y < len(matrix) and matrix[x][y] == "*":
                            mine_count += 1
                result_row.append(str(mine_count))
        result.append(result_row)
    return result

First read the text file and get the line contents into a numpy array, with this:

with open('test1.txt', 'r') as f:
    all_lines = f.readlines()
    mat_shape = tuple(map(int, all_lines[0].split()))
    lines = [i.strip().split() for i in all_lines[1:]]
lines = np.array(lines)

Read the first line of the text file, split, map them into int and keep it in a tuple as we use it to resize our matrix later.

lines would be like this:

[['*' 'o' 'o' 'o' 'o' '*']
 ['o' 'o' '*' 'o' 'o' 'o']
 ['o' 'o' '*' 'o' 'o' '*']
 ['o' 'o' '*' 'o' 'o' 'o']
 ['o' 'o' 'o' 'o' '*' 'o']
 ['o' 'o' 'o' 'o' 'o' 'o']]

Get the neighbor items for each cell of the matrix, with this function:

def get_neighbours(lines, cell):
    row, col = cell
    row_max = len(lines)
    col_max = len(lines[0])
    cell_cont = lines[row][col]
    if cell_cont!="*":
        return [lines[row_d + row][col_d + col] for col_d in [-1,0,1] if (0 <= (col_d + col) < col_max) or (col_d == 0 and row_d==0) for row_d in [-1,0,1] if 0 <= (row_d + row) < row_max ].count('*')
    else:
        return '*'

The function takes whole matrix and a particular cell which is a tuple of row and column number. It returns only '*' if there is a star in the cell, otherwise an integer - the number of stars in the adjacent neighbor cells.

Now create a new array, and call this function for each cell of the matrix:

new = []
for i,_ in enumerate(lines):
    for j,_ in enumerate(lines[i]):
        new.append(get_neighbours(lines, (i,j)))
new = np.array(new)

If you now reshape this matrix into the desired format by this:

new = new.reshape(mat_shape)

It becomes:

[['*' '2' '1' '1' '1' '*']
 ['1' '3' '*' '2' '2' '2']
 ['0' '3' '*' '3' '1' '*']
 ['0' '2' '*' '3' '2' '2']
 ['0' '1' '1' '2' '*' '1']
 ['0' '0' '0' '1' '1' '1']]

You can write this into a new text file with this:

with open('new1.txt', 'w') as f:
    f.write(all_lines[0])
    for i in new:
        f.write(' '.join(i))
        f.write('\n')

It would write the following content into the new1.txt file:

6 6
* 2 1 1 1 *
1 3 * 2 2 2
0 3 * 3 1 *
0 2 * 2 2 2
0 1 1 2 * 1
0 0 0 1 1 1

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM