繁体   English   中英

Python - 从 position 对象的二维数组打印 ASCII 笛卡尔坐标网格

[英]Python - Printing an ASCII cartesian coordinate grid from a 2D array of position objects

非常抱歉,所有人,但这将是一个漫长的过程。 因此,作为我之前关于 STDIN 的查询的一个分支,不幸的是,似乎没有足够多的问题与我的具体问题相似,至少在 Python 中没有(也没有使用像 numpy 这样的东西,这比我的水平高出一点) . 我确实有很多我已经实现的代码,所以希望这会有所帮助(尽管它不是最干净的,而且正如我上面提到的,它相当长)。

就目前而言,我的问题是打印出表示 class 对象的二维数组的 ASCII 艺术,以便 ASCII 准确地在简单的 x,y 笛卡尔坐标网格的方向上描绘它们。

我有几个相互交互的类,第一个构造包含两个整数的 position 对象,表示笛卡尔坐标对的 x 和 y:

class Pos:
    def __init__(self, x, y):
        self.x = x
        self.y = y

第二个 class 根据用户在我程序的不同部分提供的一些大小参数构造一个“正方形”网格,这里实现为一个填充了这些 position 对象的二维数组:

class Grid:
    def __init__(self, size):
        assert size > 0
        self.size = size
        self.grid = []
        #1  Perhaps there is something wrong with my 2D implementation...
        for x in range(size):
            sub_grid = []
            for y in range(size):
                sub_grid.append(Pos(x, y))
            self.grid.append(sub_grid)

在这个 class 中还有一个方法,它打印出网格的 ASCII 艺术作为用户的外部表示(这是一个混乱的位):

def print(self):
    #  This is the main ASCII-art.
    size_check = self.size - 1
    if self.size > 10:
        print('   +' + ('-' * (((self.size * 2) + 1)) + '+'))
    else:
        print('  +' + ('-' * (((self.size * 2) + 1)) + '+'))
    counter = 0
    #2  ...or perhaps it's something wrong with this iterative condition.
    for x in self.grid[::-1]:
        if self.size > 10:
            if size_check > 9:
                print(self.size - 1 - counter, '|', end=' ')
            else:
                print('', self.size - 1 - counter, '|', end=' ')
        else:
            print(self.size - 1 - counter, '|', end=' ')
        counter += 1
        size_check -= 1
        for y in x:
            #  This is only here to check if the coords are in the correct position in the art.
            ^^if y.x == 5 and y.y == 8:
                print('O ', end='')^^
            else:
                print('. ', end='')
        print('|')
    if self.size > 10:
        print('   +' + ('-' * (((self.size * 2) + 1)) + '+'))
        print('     ', end='')
    else:
        print('  +' + ('-' * (((self.size * 2) + 1)) + '+'))
        print('    ', end='')

    #  This is simply support for grid sizes greater than 10.
    if self.size > 10:
        for x in self.grid:
            if x[0].x <= 9:
                print('', '', end=' ')
            elif x[0].x > 9 and x[0].x < (self.size - 1):
                strng = str(x[0].x)
                print(int(strng[0]), end=' ')
            elif x[0].x == (self.size - 1):
                strng = str(x[0].x)
                print(int(strng[0]))
        print('     ', end='')
        for x in self.grid:
            if x[0].x <= 9:
                print(x[0].x, '', end='')
            elif x[0].x > 9 and x[0].x < (self.size - 1):
                strng = str(x[0].x)
                print(int(strng[1]), end=' ')
            elif x[0].x == (self.size - 1):
                strng = str(x[0].x)
                print(int(strng[1]), end=' ')
    else:
        for x in self.grid:
            if x[0].x < (self.size - 1):
                print(x[0].x, '', end='')
            elif x[0].x == (self.size - 1):
                print(x[0].x, end='')
    print()

我知道这很多,但它成功打印出来的内容看起来像这样(给定大小 = 10):

  +---------------------+
9 | . . . . . . . . . . |
8 | . . . . . . . . . . |
7 | . . . . . . . . . . |
6 | . . . . . . . . . . |
5 | . . . . . . . . . . |
4 | . . . . . . . . . . |
3 | . . . . . . . . . . |
2 | . . . . . . . . . . |
1 | . . . . . . . . . . |
0 | . . . . . . . . . . |
  +---------------------+
    0 1 2 3 4 5 6 7 8 9

现在这看起来很好,直到我尝试显示 position 对象应该位于的位置。 在上面的代码中,我用 ^^ 标记了一个条件语句,它在坐标 (5, 8) 处打印出一个“O”。 结果看起来像这样:

  +---------------------+
9 | . . . . . . . . . . |
8 | . . . . . . . . . . |
7 | . . . . . . . . . . |
6 | . . . . . . . . . . |
5 | . . . . . . . . O . |
4 | . . . . . . . . . . |
3 | . . . . . . . . . . |
2 | . . . . . . . . . . |
1 | . . . . . . . . . . |
0 | . . . . . . . . . . |
  +---------------------+
    0 1 2 3 4 5 6 7 8 9

理想的 output 实际上应该是这样的:

  +---------------------+
9 | . . . . . . . . . . |
8 | . . . . . O . . . . |
7 | . . . . . . . . . . |
6 | . . . . . . . . . . |
5 | . . . . . . . . . . |
4 | . . . . . . . . . . |
3 | . . . . . . . . . . |
2 | . . . . . . . . . . |
1 | . . . . . . . . . . |
0 | . . . . . . . . . . |
  +---------------------+
    0 1 2 3 4 5 6 7 8 9

正如您所看到的,ASCII 艺术以这样一种方式打印,从外部看起来好像 x 轴和 y 轴已经交换,即使在内部任何 position object 的坐标响应正确。 我在上面的代码中写了两条评论,我推测可能是什么问题。 我最好的猜测是我如何实现网格本身,或者我如何打印艺术作品; 但这就是我需要帮助弄清楚的。

一如既往,我们非常感谢您提供的任何帮助; 我再次道歉!

我建议使用字符串操作而不是直接打印来构建你的线条。 这将使代码更简单,并允许将模板字符串中的模式替换为典型的行内容。

例如:

# prepare the empty content
rows = 10
cols = 10
content = [["."]*cols for _ in range(rows)]

# assign values at coordinates as needed (based on your grid)
content[5][8]  = "O"
grid = [(4,1,"H"),(6,3,"L"),(5,2,"E"),(4,6,"R"),(7,4,"L"),(6,6,"W"),(3,6,"L"),(2,6,"D"),(5,6,"O")]
for (y,x,c) in grid: content[y][x] = c

# build frame
width       = len(str(max(rows,cols)-1))
contentLine = "# | values |"

dashes      = "-".join("-"*width for _ in range(cols))
frameLine   = contentLine.replace("values",dashes)
frameLine   = frameLine.replace("#"," "*width)
frameLine   = frameLine.replace("| ","+-").replace(" |","-+")

# print grid
print(frameLine)
for i,row in enumerate(reversed(content),1):
    values = " ".join(f"{v:{width}s}" for v in row)
    line   = contentLine.replace("values",values)
    line   = line.replace("#",f"{rows-i:{width}d}")
    print(line)
print(frameLine)

# x-axis numbers
numLine = contentLine.replace("|"," ")
numLine = numLine.replace("#"," "*width)
colNums = " ".join(f"{i:<{width}d}" for i in range(cols))
numLine = numLine.replace("values",colNums)
print(numLine)

output:

  +---------------------+
9 | . . . . . . . . . . |
8 | . . . . . O . . . . |
7 | . . . . L . . . . . |
6 | . . . L . . W . . . |
5 | . . E . . . O . . . |
4 | . H . . . . R . . . |
3 | . . . . . . L . . . |
2 | . . . . . . D . . . |
1 | . . . . . . . . . . |
0 | . . . . . . . . . . |
  +---------------------+
    0 1 2 3 4 5 6 7 8 9                  

output 行=12,列=15:

   +----------------------------------------------+
11 | .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  |
10 | .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  |
 9 | .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  |
 8 | .  .  .  .  .  O  .  .  .  .  .  .  .  .  .  |
 7 | .  .  .  .  L  .  .  .  .  .  .  .  .  .  .  |
 6 | .  .  .  L  .  .  W  .  .  .  .  .  .  .  .  |
 5 | .  .  E  .  .  .  O  .  .  .  .  .  .  .  .  |
 4 | .  H  .  .  .  .  R  .  .  .  .  .  .  .  .  |
 3 | .  .  .  .  .  .  L  .  .  .  .  .  .  .  .  |
 2 | .  .  .  .  .  .  D  .  .  .  .  .  .  .  .  |
 1 | .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  |
 0 | .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  |
   +----------------------------------------------+
     0  1  2  3  4  5  6  7  8  9  10 11 12 13 14  

这是一个使用 rust 的实现,分为几个步骤

1. Grid结构

pub struct Grid {
    cells: Vec<Vec<char>>,
    num_rows: usize,
    num_cols: usize,
}

Grid结构表示字符网格。 它具有三个字段:

  • cells :表示网格中单元格的二维字符向量。
  • num_rows :表示网格中行数的usize
  • num_cols :表示网格中列数的usize

2. Alignment枚举

#[derive(Debug, PartialEq)]
enum Alignment {
    Left,
    Center,
    Right,
}

Alignment枚举表示打印网格时单元格值的可能对齐方式。 它具有三种变体:

  • Left :将单元格值左对齐。
  • Center :将单元格值对齐到中心。
  • Right :将单元格值右对齐。

3. new方法

    fn new(num_rows: usize, num_cols: usize, empty_cell_char: char) -> Self {
        let cells = vec![vec![empty_cell_char; num_cols]; num_rows];
        Self {
            cells,
            num_rows,
            num_cols,
        }
    }
}

new方法是Grid结构的构造方法。 它需要三个参数:

  • num_rows :表示网格中行数的usize
  • num_cols :表示网格中列数的usize
  • empty_cell_char :表示要填充空单元格的char的字符。

它创建一个具有给定行数和列数的新网Grid object,并用给定字符填充单元格。

set_cell方法

    fn set_cell(&mut self, row: usize, col: usize, character: char) {
        self.cells[row][col] = character;
    }
}

set_cell方法在网格的特定单元格中设置字符。 它需要三个参数:

  • row :表示单元格行号的usize
  • col :表示单元格列号的usize
  • character :一个char表示要在单元格中设置的字符。

它在给定行和列的单元格中设置字符。

5. draw方法

impl Grid {
    fn draw(
        &self,
        cell_width: usize,
        border_char: char,
        row_separator_char: char,
        col_separator_char: char,
        x_axis_separator_char: char,
        alignment: Alignment,
    ) {
        let frame_top_bottom = (0..self.num_cols)
            .map(|_| border_char.to_string().repeat(cell_width))
            .collect::<Vec<_>>()
            .join(&border_char.to_string());

        let row_template = format!(
            "{row_num}{row_separator}{row_values}{row_values_separator}",
            row_num = "{row_num}",
            row_separator = row_separator_char,
            row_values = "{row_values}",
            row_values_separator = row_separator_char
        );

        let row_num = row_template.replace(
            "{row_num}",
            format!("{:width$}", "", width = cell_width).as_str(),
        );

        let frame_top = row_num.replace("{row_values}", &frame_top_bottom);

        println!("{}", frame_top);

        for (row_num, row) in self.cells.iter().rev().enumerate() {
            let row_num_str = format!("{:width$}", self.num_rows - row_num - 1, width = cell_width);
            let row_values: Vec<String> = row
                .iter()
                .map(|cell| {
                    let aligned_string: String = match alignment {
                        Alignment::Left => format!("{:<width$}", cell, width = cell_width),
                        Alignment::Center => format!("{:^width$}", cell, width = cell_width),
                        Alignment::Right => format!("{:>width$}", cell, width = cell_width),
                    };

                    aligned_string
                })
                .collect();

            println!(
                "{}",
                row_template
                    .replace(
                        "{row_num}",
                        format!("{:<width$}", row_num_str, width = cell_width).as_str()
                    )
                    .replace(
                        "{row_values}",
                        &row_values.join(&col_separator_char.to_string())
                    )
            );
        }
        println!("{}", frame_top);

        let col_nums: Vec<String> = (0..self.num_cols)
            .map(|col| {
                let aligned_string: String = match alignment {
                    Alignment::Left => format!("{:<width$}", col, width = cell_width),
                    Alignment::Center => format!("{:^width$}", col, width = cell_width),
                    Alignment::Right => format!("{:>width$}", col, width = cell_width),
                };

                aligned_string
            })
            .collect();
        let x_axis = row_num.replace(
            "{row_values}",
            &col_nums.join(&x_axis_separator_char.to_string()),
        );
        println!("{}", x_axis);
    }
}

draw方法使用给定的参数将网格打印到控制台。 它需要六个参数:

  • cell_width :表示打印网格时每个单元格宽度的usize

  • border_char :一个char表示用于网格边界的字符。

  • row_separator_char :一个char表示用作网格行之间分隔符的字符。

  • col_separator_char :一个char表示用作网格列之间分隔符的字符。

  • x_axis_separator_char :一个char ,表示用作网格底部列号之间分隔符的字符。

  • alignment :一个Alignment值,表示打印网格时单元格值的 alignment。

draw方法首先使用border_charcell_width生成网格的顶部和底部边框。 然后它为网格的每一行创建一个模板,该模板由行号、行分隔符、该行的单元格值和行值分隔符组成。

然后它从下到上遍历网格的行,并使用模板和当前行号和单元格值为每一行生成一个字符串。 然后它将每一行打印到控制台。

最后,它使用alignmentx_axis_separator_char在网格底部生成列号,并将它们打印到控制台。

6. main function

复制代码

fn main() {
    let mut grid = Grid::new(3, 3, ' ');
    grid.set_cell(0, 0, 'X');
    grid.set_cell(1, 1, 'O');
    grid.set_cell(2, 2, 'X');
    grid.draw(
        4,
        '+',
        '|',
        ' ',
        ' ',
        Alignment::Right,
    );
}

main function新建一个3行3列的Grid object,并使用set_cell方法设置单元格中的字符。 然后它调用Grid object 上的draw方法将其打印到控制台,单元格宽度为 4,边框字符为'+' ,行分隔符为'|' 、列分隔符' ' 、x 轴分隔符' '和 alignment Alignment::Right

这是表格中的摘要

描述
定义Grid结构 Grid结构表示具有三个字段的字符网格: cells ,表示网格中单元格的二维字符向量; num_rows ,表示网格中行数的usize num_cols ,一个表示网格中列数的usize
定义Alignment枚举 Alignment枚举表示打印网格时单元格值的可能对齐方式。 它具有三种变体: Left ,将单元格值左对齐; Center ,将单元格值与中心对齐; Right ,它将单元格值右对齐。
定义new方法 new方法是Grid结构的构造函数。 它需要三个参数: num_rows ,一个表示网格中行数的usize num_cols ,表示网格中列数的usize empty_cell_char ,一个char表示要填充空单元格的字符。 它创建一个具有给定行数和列数的新网Grid object,并用给定字符填充单元格。
定义set_cell方法 set_cell方法在网格的特定单元格中设置字符。 它需要三个参数: row ,一个usize代表单元格的行号; col ,表示单元格列号的usize character ,一个char表示要在单元格中设置的字符。 它在给定行和列的单元格中设置字符。
定义draw方法 draw方法使用给定的参数将网格打印到控制台。 它有六个参数: cell_width ,一个usize ,表示打印网格时每个单元格的宽度; border_char ,一个char表示用于网格边界的字符; row_separator_char ,一个char表示用作网格行之间分隔符的字符; col_separator_char ,一个char表示用作网格列之间分隔符的字符; x_axis_separator_char ,一个char表示用作网格底部列号之间分隔符的字符; alignment ,一个Alignment值,表示打印网格时单元格值的 alignment。 它首先使用border_charcell_width生成网格的顶部和底部边框。 然后它为网格的每一行创建一个模板,该模板由行号、行分隔符、该行的单元格值和行值分隔符组成。 然后它从下到上遍历网格的行,并使用模板和当前行号和单元格值为每一行生成一个字符串。 然后它将每一行打印到控制台。 最后,它使用alignmentx_axis_separator_char在网格底部生成列号,并将它们打印到控制台。
定义main function main function新建一个3行3列的Grid object,并使用set_cell方法设置单元格中的字符。 然后它调用Grid object 上的draw方法将其打印到控制台,单元格宽度为 4,边框字符为'+' ,行分隔符为 `'

这是表格的超摘要

描述
定义Grid结构 Grid结构表示具有三个字段的字符网格: cells ,表示网格中单元格的二维字符向量; num_rows ,表示网格中行数的usize num_cols ,一个表示网格中列数的usize
定义Alignment枚举 Alignment枚举表示打印网格时单元格值的 alignment。 它具有三个变体: LeftCenterRight
定义new方法 new方法创建一个新的Grid object 具有给定的行数和列数,并用给定的字符填充单元格。
定义set_cell方法 set_cell方法在网格的特定单元格中设置字符。
定义draw方法 draw方法使用给定的参数将网格打印到控制台。
定义main function main function 创建一个Grid object 并在其单元格中设置字符。 然后它使用draw方法将网格打印到控制台。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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